1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.car.test;
17
18import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
19import android.content.Context;
20import android.media.AudioAttributes;
21import android.media.AudioManager;
22import android.os.SystemClock;
23import android.support.car.Car;
24import android.support.car.media.CarAudioManager;
25import android.test.suitebuilder.annotation.MediumTest;
26
27import com.android.car.vehiclenetwork.VehicleNetworkConsts;
28import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
29import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
30import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
31import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
32import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
33import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStream;
34import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
35import com.android.car.vehiclenetwork.VehiclePropValueUtil;
36import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel;
37import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
38import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
39import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
40import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
41
42import java.util.LinkedList;
43import java.util.concurrent.Semaphore;
44import java.util.concurrent.TimeUnit;
45
46@MediumTest
47public class CarAudioFocusTest extends MockedCarTestBase {
48
49    private static final long TIMEOUT_MS = 3000;
50
51    private final VehicleHalPropertyHandler mAudioRoutingPolicyPropertyHandler =
52            new VehicleHalPropertyHandler() {
53        @Override
54        public void onPropertySet(VehiclePropValue value) {
55            //TODO
56        }
57
58        @Override
59        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
60            fail("cannot get");
61            return null;
62        }
63
64        @Override
65        public void onPropertySubscribe(int property, float sampleRate, int zones) {
66            fail("cannot subscribe");
67        }
68
69        @Override
70        public void onPropertyUnsubscribe(int property) {
71            fail("cannot unsubscribe");
72        }
73    };
74
75    private final FocusPropertyHandler mAudioFocusPropertyHandler =
76            new FocusPropertyHandler();
77
78    private final VehicleHalPropertyHandler mAppContextPropertyHandler =
79            new VehicleHalPropertyHandler() {
80
81        @Override
82        public void onPropertySet(VehiclePropValue value) {
83            // TODO Auto-generated method stub
84
85        }
86
87        @Override
88        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
89            // TODO Auto-generated method stub
90            return null;
91        }
92
93        @Override
94        public void onPropertySubscribe(int property, float sampleRate, int zones) {
95            // TODO Auto-generated method stub
96
97        }
98
99        @Override
100        public void onPropertyUnsubscribe(int property) {
101            // TODO Auto-generated method stub
102
103        }
104    };
105
106    private final Semaphore mWaitSemaphore = new Semaphore(0);
107    private final LinkedList<VehiclePropValue> mEvents = new LinkedList<VehiclePropValue>();
108    private AudioManager mAudioManager;
109
110    @Override
111    protected void setUp() throws Exception {
112        super.setUp();
113        // AudioManager should be created in main thread to get focus event. :(
114        runOnMainSync(new Runnable() {
115            @Override
116            public void run() {
117                mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
118            }
119        });
120
121        getVehicleHalEmulator().addProperty(
122                VehiclePropConfigUtil.getBuilder(
123                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY,
124                        VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
125                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
126                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2,
127                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
128                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
129                        mAudioRoutingPolicyPropertyHandler);
130        getVehicleHalEmulator().addProperty(
131                VehiclePropConfigUtil.getBuilder(
132                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
133                        VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
134                        VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
135                        VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4,
136                        VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
137                        0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
138                        mAudioFocusPropertyHandler);
139        getVehicleHalEmulator().addStaticProperty(
140                VehiclePropConfigUtil.createStaticStringProperty(
141                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT),
142                VehiclePropValueUtil.createIntValue(
143                        VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT, 1, 0));
144        getVehicleHalEmulator().start();
145    }
146
147    public void testMediaGainFocus() throws Exception {
148        //TODO update this to check config
149        checkSingleRequestRelease(
150                AudioManager.STREAM_MUSIC,
151                AudioManager.AUDIOFOCUS_GAIN,
152                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
153                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
154    }
155
156    public void testMediaGainTransientFocus() throws Exception {
157        checkSingleRequestRelease(
158                AudioManager.STREAM_MUSIC,
159                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
160                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
161                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
162    }
163
164    public void testMediaGainTransientMayDuckFocus() throws Exception {
165        checkSingleRequestRelease(
166                AudioManager.STREAM_MUSIC,
167                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
168                VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
169                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
170    }
171
172    public void testAlarmGainTransientFocus() throws Exception {
173        checkSingleRequestRelease(
174                AudioManager.STREAM_ALARM,
175                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
176                VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
177                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
178    }
179
180    public void testAlarmGainTransientMayDuckFocus() throws Exception {
181        checkSingleRequestRelease(
182                AudioManager.STREAM_ALARM,
183                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
184                VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
185                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG);
186    }
187
188    public void testMediaNavFocus() throws Exception {
189        //music start
190        AudioFocusListener listenerMusic = new AudioFocusListener();
191        int res = mAudioManager.requestAudioFocus(listenerMusic,
192                AudioManager.STREAM_MUSIC,
193                AudioManager.AUDIOFOCUS_GAIN);
194        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
195        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
196        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
197        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
198        assertEquals(0, request[2]);
199        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
200        mAudioFocusPropertyHandler.sendAudioFocusState(
201                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
202                request[1],
203                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
204
205        // nav guidance start
206        AudioFocusListener listenerNav = new AudioFocusListener();
207        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
208                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
209                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
210                build();
211        res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
212                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
213        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
214        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
215        assertEquals(0x3, request[1]);
216        assertEquals(0, request[2]);
217        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG |
218                VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
219        mAudioFocusPropertyHandler.sendAudioFocusState(
220                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
221                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
222
223        // nav guidance done
224        mAudioManager.abandonAudioFocus(listenerNav);
225        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
226        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
227        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
228        assertEquals(0, request[2]);
229        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
230        mAudioFocusPropertyHandler.sendAudioFocusState(
231                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
232                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
233
234        // music done
235        mAudioManager.abandonAudioFocus(listenerMusic);
236        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
237        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
238        assertEquals(0, request[1]);
239        assertEquals(0, request[2]);
240        assertEquals(0, request[3]);
241        mAudioFocusPropertyHandler.sendAudioFocusState(
242                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS, request[1],
243                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
244    }
245
246    public void testMediaExternalMediaNavFocus() throws Exception {
247        // android music
248        AudioFocusListener listenerMusic = new AudioFocusListener();
249        int res = mAudioManager.requestAudioFocus(listenerMusic,
250                AudioManager.STREAM_MUSIC,
251                AudioManager.AUDIOFOCUS_GAIN);
252        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
253        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
254        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
255        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
256        assertEquals(0, request[2]);
257        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
258        mAudioFocusPropertyHandler.sendAudioFocusState(
259                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
260                request[1],
261                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
262
263        // car plays external media (=outside Android)
264        mAudioFocusPropertyHandler.sendAudioFocusState(
265                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
266                0,
267                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
268        int focusChange = listenerMusic.waitAndGetFocusChange(TIMEOUT_MS);
269        assertEquals(AudioManager.AUDIOFOCUS_LOSS, focusChange);
270
271        // nav guidance start
272        AudioFocusListener listenerNav = new AudioFocusListener();
273        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
274                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
275                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
276                build();
277        res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
278                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
279        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
280        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
281                request[0]);
282        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
283        assertEquals(0, request[2]);
284        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
285        mAudioFocusPropertyHandler.sendAudioFocusState(
286                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT,
287                0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
288                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
289
290        // nav guidance ends
291        mAudioManager.abandonAudioFocus(listenerNav);
292        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
293        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
294        assertEquals(0, request[1]);
295        assertEquals(0, request[2]);
296        assertEquals(0, request[3]);
297        mAudioFocusPropertyHandler.sendAudioFocusState(
298                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
299                0,
300                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
301
302        // now ends external play
303        mAudioFocusPropertyHandler.sendAudioFocusState(
304                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
305                0,
306                0);
307        // music picks up
308        listenerMusic.waitForFocus(TIMEOUT_MS, AudioManager.AUDIOFOCUS_GAIN);
309        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
310        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
311        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
312        assertEquals(0, request[2]);
313        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
314        mAudioFocusPropertyHandler.sendAudioFocusState(
315                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
316                request[1],
317                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
318
319        // now ends music
320        mAudioManager.abandonAudioFocus(listenerMusic);
321        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
322        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
323        assertEquals(0, request[1]);
324        assertEquals(0, request[2]);
325        assertEquals(0, request[3]);
326        mAudioFocusPropertyHandler.sendAudioFocusState(
327                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
328                0,
329                0);
330    }
331
332    public void testMediaExternalRadioNavMediaFocus() throws Exception {
333        // android music
334        AudioFocusListener listenerMusic = new AudioFocusListener();
335        int res = mAudioManager.requestAudioFocus(listenerMusic,
336                AudioManager.STREAM_MUSIC,
337                AudioManager.AUDIOFOCUS_GAIN);
338        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
339        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
340        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
341        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
342        assertEquals(0, request[2]);
343        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
344        mAudioFocusPropertyHandler.sendAudioFocusState(
345                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
346                request[1],
347                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
348
349        // android radio
350        AudioFocusListener listenerRadio = new AudioFocusListener();
351        CarAudioManager carAudioManager = (CarAudioManager) getSupportCar().getCarManager(
352                Car.AUDIO_SERVICE);
353        assertNotNull(carAudioManager);
354        AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
355                CarAudioManager.CAR_AUDIO_USAGE_RADIO);
356        res = mAudioManager.requestAudioFocus(listenerRadio,
357                radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
358        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
359        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
360        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
361        assertEquals(0, request[1]);
362        assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
363                request[2]);
364        // no android side context for radio
365        assertEquals(0, request[3]);
366        mAudioFocusPropertyHandler.sendAudioFocusState(
367                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
368                0,
369                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
370
371        // nav guidance start
372        AudioFocusListener listenerNav = new AudioFocusListener();
373        AudioAttributes navAttrib = (new AudioAttributes.Builder()).
374                setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
375                setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
376                build();
377        res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
378                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
379        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
380        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
381                request[0]);
382        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
383        assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
384                request[2]);
385        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
386        mAudioFocusPropertyHandler.sendAudioFocusState(
387                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
388                0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
389                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
390
391        // nav guidance ends
392        mAudioManager.abandonAudioFocus(listenerNav);
393        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
394        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
395                request[0]);
396        assertEquals(0, request[1]);
397        assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
398                request[2]);
399        assertEquals(0, request[3]);
400        mAudioFocusPropertyHandler.sendAudioFocusState(
401                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
402                0,
403                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
404
405        // ends radio. music will get the focus GAIN.
406        // Music app is supposed to stop and release focus when it has lost focus, but here just
407        // check if focus is working.
408        mAudioManager.abandonAudioFocus(listenerRadio);
409        listenerMusic.waitForFocus(TIMEOUT_MS, AudioManager.AUDIOFOCUS_GAIN);
410        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
411        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
412        assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
413        assertEquals(0, request[2]);
414        assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
415        mAudioFocusPropertyHandler.sendAudioFocusState(
416                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
417                0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
418                0);
419
420        // now music release focus.
421        mAudioManager.abandonAudioFocus(listenerMusic);
422        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
423        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
424        assertEquals(0, request[1]);
425        assertEquals(0, request[2]);
426        assertEquals(0, request[3]);
427        mAudioFocusPropertyHandler.sendAudioFocusState(
428                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
429                0,
430                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
431    }
432
433    private void checkSingleRequestRelease(int streamType, int androidFocus, int streamNumber,
434            int context)
435            throws Exception {
436        AudioFocusListener lister = new AudioFocusListener();
437        int res = mAudioManager.requestAudioFocus(lister,
438                streamType,
439                androidFocus);
440        assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
441        int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
442        int expectedRequest = VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
443        int response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
444        switch (androidFocus) {
445            case AudioManager.AUDIOFOCUS_GAIN:
446                expectedRequest = VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN;
447                response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN;
448                break;
449            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
450                expectedRequest =
451                    VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
452                response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
453                break;
454            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
455                expectedRequest =
456                    VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
457                response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
458                break;
459        }
460        assertEquals(expectedRequest, request[0]);
461        assertEquals(0x1 << streamNumber, request[1]);
462        assertEquals(0, request[2]);
463        assertEquals(context, request[3]);
464        mAudioFocusPropertyHandler.sendAudioFocusState(
465                response,
466                request[1],
467                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
468        mAudioManager.abandonAudioFocus(lister);
469        request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
470        assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
471        assertEquals(0, request[1]);
472        assertEquals(0, request[2]);
473        assertEquals(0, request[3]);
474        mAudioFocusPropertyHandler.sendAudioFocusState(
475                VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
476                request[1],
477                VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
478    }
479
480    private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
481        private final Semaphore mFocusChangeWait = new Semaphore(0);
482        private int mLastFocusChange;
483
484        public int waitAndGetFocusChange(long timeoutMs) throws Exception {
485            if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
486                fail("timeout waiting for focus change");
487            }
488            return mLastFocusChange;
489        }
490
491        public void waitForFocus(long timeoutMs, int expectedFocus) throws Exception {
492            while (mLastFocusChange != expectedFocus) {
493                if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
494                    fail("timeout waiting for focus change");
495                }
496            }
497        }
498
499        @Override
500        public void onAudioFocusChange(int focusChange) {
501            mLastFocusChange = focusChange;
502            mFocusChangeWait.release();
503        }
504    }
505
506    private class FocusPropertyHandler implements VehicleHalPropertyHandler {
507
508        private int mState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
509        private int mStreams = 0;
510        private int mExtFocus = 0;
511        private int mRequest;
512        private int mRequestedStreams;
513        private int mRequestedExtFocus;
514        private int mRequestedAudioContexts;
515
516        private final Semaphore mSetWaitSemaphore = new Semaphore(0);
517
518        public void sendAudioFocusState(int state, int streams, int extFocus) {
519            synchronized (this) {
520                mState = state;
521                mStreams = streams;
522                mExtFocus = extFocus;
523            }
524            int[] values = { state, streams, extFocus, 0 };
525            getVehicleHalEmulator().injectEvent(VehiclePropValueUtil.createIntVectorValue(
526                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
527                    SystemClock.elapsedRealtimeNanos()));
528        }
529
530        public int[] waitForAudioFocusRequest(long timeoutMs) throws Exception {
531            if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
532                fail("timeout");
533            }
534            synchronized (this) {
535                return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
536                        mRequestedAudioContexts };
537            }
538        }
539
540        @Override
541        public void onPropertySet(VehiclePropValue value) {
542            assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, value.getProp());
543            synchronized (this) {
544                mRequest = value.getInt32Values(
545                        VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_FOCUS);
546                mRequestedStreams = value.getInt32Values(
547                        VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
548                mRequestedExtFocus = value.getInt32Values(
549                        VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
550                mRequestedAudioContexts = value.getInt32Values(
551                        VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS);
552            }
553            mSetWaitSemaphore.release();
554        }
555
556        @Override
557        public VehiclePropValue onPropertyGet(VehiclePropValue value) {
558            assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, value.getProp());
559            int state, streams, extFocus;
560            synchronized (this) {
561                state = mState;
562                streams = mStreams;
563                extFocus = mExtFocus;
564            }
565            int[] values = { state, streams, extFocus, 0 };
566            return VehiclePropValueUtil.createIntVectorValue(
567                    VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
568                    SystemClock.elapsedRealtimeNanos());
569        }
570
571        @Override
572        public void onPropertySubscribe(int property, float sampleRate, int zones) {
573            assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, property);
574        }
575
576        @Override
577        public void onPropertyUnsubscribe(int property) {
578            assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, property);
579        }
580    }
581}
582