• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 
17 package com.google.android.car.kitchensink.audio;
18 
19 import static android.R.layout.simple_spinner_dropdown_item;
20 import static android.R.layout.simple_spinner_item;
21 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION;
22 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND;
23 import static android.car.media.CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID;
24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING;
25 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
26 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
27 import static android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
28 import static android.media.AudioAttributes.USAGE_ASSISTANT;
29 import static android.media.AudioAttributes.USAGE_MEDIA;
30 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
31 import static android.media.AudioManager.AUDIOFOCUS_GAIN;
32 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT;
33 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
34 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
35 import static android.media.AudioManager.AUDIOFOCUS_LOSS;
36 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
37 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
38 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
39 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
40 import static android.media.AudioManager.GET_DEVICES_INPUTS;
41 import static android.os.Build.IS_EMULATOR;
42 
43 import static com.google.android.car.kitchensink.R.raw.free_flight;
44 import static com.google.android.car.kitchensink.R.raw.one2six;
45 import static com.google.android.car.kitchensink.R.raw.ring_classic_01;
46 import static com.google.android.car.kitchensink.R.raw.turnright;
47 import static com.google.android.car.kitchensink.R.raw.well_worth_the_wait;
48 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_COMPLETED;
49 
50 import android.car.Car;
51 import android.car.CarAppFocusManager;
52 import android.car.CarAppFocusManager.OnAppFocusChangedListener;
53 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
54 import android.car.media.CarAudioManager;
55 import android.content.Context;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.PackageManager;
58 import android.media.AudioAttributes;
59 import android.media.AudioDeviceInfo;
60 import android.media.AudioFocusRequest;
61 import android.media.AudioManager;
62 import android.media.AudioManager.OnAudioFocusChangeListener;
63 import android.media.HwAudioSource;
64 import android.os.Bundle;
65 import android.os.Handler;
66 import android.os.Looper;
67 import android.util.Log;
68 import android.view.LayoutInflater;
69 import android.view.View;
70 import android.view.ViewGroup;
71 import android.widget.AdapterView;
72 import android.widget.ArrayAdapter;
73 import android.widget.Button;
74 import android.widget.LinearLayout;
75 import android.widget.RadioGroup;
76 import android.widget.Spinner;
77 import android.widget.TextView;
78 
79 import androidx.fragment.app.Fragment;
80 import androidx.viewpager.widget.ViewPager;
81 
82 import com.android.internal.util.Preconditions;
83 
84 import com.google.android.car.kitchensink.R;
85 import com.google.android.car.kitchensink.audio.AudioPlayer.PlayStateListener;
86 import com.google.android.material.tabs.TabLayout;
87 
88 import java.util.ArrayList;
89 import java.util.List;
90 
91 import javax.annotation.concurrent.GuardedBy;
92 
93 public class AudioTestFragment extends Fragment {
94     private static final String TAG = "CAR.AUDIO.KS";
95     private static final boolean DBG = true;
96 
97     private static final long STOP_DELAY_TIME_MS = 3_000;
98 
99     // Key for communicating to hall which audio zone has been selected to play
100     private static final String AAE_PARAMETER_KEY_FOR_SELECTED_ZONE =
101             "com.android.car.emulator.selected_zone";
102 
103     private static final Integer[] TONES = new Integer[] { 200, 400, 600, 800, 1_000, 1_200 };
104 
105     private AudioManager mAudioManager;
106     private FocusHandler mAudioFocusHandler;
107 
108     private AudioPlayer mMusicPlayer;
109     @GuardedBy("mLock")
110     private AudioPlayer mMusicPlayerWithDelayedFocus;
111     private AudioPlayer mMusicPlayerShort;
112     private AudioPlayer mNavGuidancePlayer;
113     private AudioPlayer mPhoneAudioPlayer;
114     private AudioPlayer mVrPlayer;
115     private AudioPlayer mSystemPlayer;
116     private AudioPlayer mWavPlayer;
117     private AudioPlayer mMusicPlayerForSelectedDeviceAddress;
118     private HwAudioSource mHwAudioSource;
119     private AudioPlayer[] mAllPlayers;
120 
121     @GuardedBy("mLock")
122     private AudioTrackPlayer mAudioTrackPlayer;
123 
124     private Handler mHandler;
125     private Context mContext;
126 
127     private Car mCar;
128     private CarAppFocusManager mAppFocusManager;
129     private AudioAttributes mMusicAudioAttrib;
130     private AudioAttributes mNavAudioAttrib;
131     private AudioAttributes mPhoneAudioAttrib;
132     private AudioAttributes mVrAudioAttrib;
133     private AudioAttributes mRadioAudioAttrib;
134     private AudioAttributes mSystemSoundAudioAttrib;
135     private AudioAttributes mMusicAudioAttribForDeviceAddress;
136     private CarAudioManager mCarAudioManager;
137     private Spinner mZoneSpinner;
138     ArrayAdapter<Integer> mZoneAdapter;
139     private Spinner mDeviceAddressSpinner;
140     ArrayAdapter<CarAudioZoneDeviceInfo> mDeviceAddressAdapter;
141     private LinearLayout mDeviceAddressLayout;
142     private ArrayAdapter<String> mCarSoundUsagesAdapter;
143     private Spinner mCarSoundUsagesSpinner;
144 
145     private TabLayout mPlayerTabLayout;
146     private ViewPager mViewPager;
147     private CarAudioZoneTabAdapter mAudioPlayerZoneAdapter;
148 
149     private final Object mLock = new Object();
150 
151     @GuardedBy("mLock")
152     private AudioFocusRequest mDelayedFocusRequest;
153     private OnAudioFocusChangeListener mMediaWithDelayedFocusListener;
154     private TextView mDelayedStatusText;
155 
156     private final OnAudioFocusChangeListener mNavFocusListener = (focusChange) -> {
157         Log.i(TAG, "Nav focus change:" + focusChange);
158     };
159     private final OnAudioFocusChangeListener mVrFocusListener = (focusChange) -> {
160         Log.i(TAG, "VR focus change:" + focusChange);
161     };
162     private final OnAudioFocusChangeListener mRadioFocusListener = (focusChange) -> {
163         Log.i(TAG, "Radio focus change:" + focusChange);
164     };
165 
166     private final CarAppFocusManager.OnAppFocusOwnershipCallback mOwnershipCallbacks =
167             new OnAppFocusOwnershipCallback() {
168                 @Override
169                 public void onAppFocusOwnershipLost(int focus) {
170                 }
171                 @Override
172                 public void onAppFocusOwnershipGranted(int focus) {
173                 }
174     };
175 
176     private final PlayStateListener mNavigationStateListener = (state) -> {
177         if (state == PLAYER_STATE_COMPLETED) {
178             mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_NAVIGATION);
179         }
180     };
181 
connectCar()182     private void connectCar() {
183         mContext = getContext();
184         mHandler = new Handler(Looper.getMainLooper());
185         mCar = Car.createCar(mContext, /* handler= */ null,
186                 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> {
187                     if (!ready) {
188                         return;
189                     }
190                     mAppFocusManager =
191                             (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
192                     OnAppFocusChangedListener listener = new OnAppFocusChangedListener() {
193                         @Override
194                         public void onAppFocusChanged(int appType, boolean active) {
195                         }
196                     };
197                     mAppFocusManager.addFocusListener(listener, APP_FOCUS_TYPE_NAVIGATION);
198                     mAppFocusManager.addFocusListener(listener, APP_FOCUS_TYPE_VOICE_COMMAND);
199 
200                     mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
201 
202                     handleSetUpZoneSelection();
203 
204                     setUpDeviceAddressPlayer();
205                 });
206     }
207 
208     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle)209     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
210         Log.i(TAG, "onCreateView");
211         View view = inflater.inflate(R.layout.audio, container, false);
212         //Zone Spinner
213         setUpZoneSpinnerView(view);
214 
215         setUpDeviceAddressLayoutView(view);
216 
217         connectCar();
218         setUpTrackToneSpinnerView(view);
219         setUpCarSoundsLayouts(view);
220         initializePlayers();
221 
222         TextView currentZoneIdTextView = view.findViewById(R.id.activity_current_zone);
223         setActivityCurrentZoneId(currentZoneIdTextView);
224 
225         mAudioManager = (AudioManager) mContext.getSystemService(
226                 Context.AUDIO_SERVICE);
227         mAudioFocusHandler = new FocusHandler(
228                 view.findViewById(R.id.button_focus_request_selection),
229                 view.findViewById(R.id.button_audio_focus_request),
230                 view.findViewById(R.id.text_audio_focus_state));
231         view.findViewById(R.id.button_media_play_start).setOnClickListener(v -> {
232             boolean requestFocus = true;
233             boolean repeat = true;
234             mMusicPlayer.start(requestFocus, repeat, AUDIOFOCUS_GAIN);
235         });
236         view.findViewById(R.id.button_media_play_once).setOnClickListener(v -> {
237             mMusicPlayerShort.start(true, false, AUDIOFOCUS_GAIN_TRANSIENT);
238             mHandler.postDelayed(() -> mMusicPlayerShort.stop(), STOP_DELAY_TIME_MS);
239         });
240         view.findViewById(R.id.button_media_play_stop).setOnClickListener(v -> mMusicPlayer.stop());
241         view.findViewById(R.id.button_wav_play_start).setOnClickListener(
242                 v -> mWavPlayer.start(true, true, AUDIOFOCUS_GAIN));
243         view.findViewById(R.id.button_wav_play_stop).setOnClickListener(v -> mWavPlayer.stop());
244         view.findViewById(R.id.button_nav_play_once).setOnClickListener(v -> {
245             if (mAppFocusManager == null) {
246                 Log.e(TAG, "mAppFocusManager is null");
247                 return;
248             }
249             if (DBG) {
250                 Log.i(TAG, "Nav start");
251             }
252             mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, mOwnershipCallbacks);
253             if (!mNavGuidancePlayer.isPlaying()) {
254                 mNavGuidancePlayer.start(true, false,
255                         AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, mNavigationStateListener);
256             }
257         });
258         view.findViewById(R.id.button_vr_play_once).setOnClickListener(v -> {
259             if (mAppFocusManager == null) {
260                 Log.e(TAG, "mAppFocusManager is null");
261                 return;
262             }
263             if (DBG) {
264                 Log.i(TAG, "VR start");
265             }
266             mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_VOICE_COMMAND, mOwnershipCallbacks);
267             if (!mVrPlayer.isPlaying()) {
268                 mVrPlayer.start(true, false,
269                         AUDIOFOCUS_GAIN_TRANSIENT, mNavigationStateListener);
270             }
271         });
272         view.findViewById(R.id.button_system_play_once).setOnClickListener(v -> {
273             if (DBG) {
274                 Log.i(TAG, "System start");
275             }
276             if (!mSystemPlayer.isPlaying()) {
277                 // system sound played without focus
278                 mSystemPlayer.start(false, false, 0);
279             }
280         });
281         view.findViewById(R.id.button_nav_start).setOnClickListener(v -> handleNavStart());
282         view.findViewById(R.id.button_nav_end).setOnClickListener(v -> handleNavEnd());
283         view.findViewById(R.id.button_vr_start).setOnClickListener(v -> handleVrStart());
284         view.findViewById(R.id.button_vr_end).setOnClickListener(v -> handleVrEnd());
285         view.findViewById(R.id.button_radio_start).setOnClickListener(v -> handleRadioStart());
286         view.findViewById(R.id.button_radio_end).setOnClickListener(v -> handleRadioEnd());
287         view.findViewById(R.id.button_speaker_phone_on).setOnClickListener(
288                 v -> mAudioManager.setSpeakerphoneOn(true));
289         view.findViewById(R.id.button_speaker_phone_off).setOnClickListener(
290                 v -> mAudioManager.setSpeakerphoneOn(false));
291         view.findViewById(R.id.button_microphone_on).setOnClickListener(
292                 v -> mAudioManager.setMicrophoneMute(false));
293         view.findViewById(R.id.button_microphone_off).setOnClickListener(
294                 v -> mAudioManager.setMicrophoneMute(true));
295         final View hwAudioSourceNotFound = view.findViewById(R.id.hw_audio_source_not_found);
296         final View hwAudioSourceStart = view.findViewById(R.id.hw_audio_source_start);
297         final View hwAudioSourceStop = view.findViewById(R.id.hw_audio_source_stop);
298         if (mHwAudioSource == null) {
299             hwAudioSourceNotFound.setVisibility(View.VISIBLE);
300             hwAudioSourceStart.setVisibility(View.GONE);
301             hwAudioSourceStop.setVisibility(View.GONE);
302         } else {
303             hwAudioSourceNotFound.setVisibility(View.GONE);
304             hwAudioSourceStart.setVisibility(View.VISIBLE);
305             hwAudioSourceStop.setVisibility(View.VISIBLE);
306             view.findViewById(R.id.hw_audio_source_start).setOnClickListener(
307                     v -> handleHwAudioSourceStart());
308             view.findViewById(R.id.hw_audio_source_stop).setOnClickListener(
309                     v -> handleHwAudioSourceStop());
310         }
311 
312         // Manage buttons for audio player for device address
313         view.findViewById(R.id.button_device_media_play_start).setOnClickListener(v -> {
314             startDeviceAudio();
315         });
316         view.findViewById(R.id.button_device_media_play_once).setOnClickListener(v -> {
317             startDeviceAudio();
318             mHandler.postDelayed(
319                     () -> mMusicPlayerForSelectedDeviceAddress.stop(), STOP_DELAY_TIME_MS);
320         });
321         view.findViewById(R.id.button_device_media_play_stop)
322                 .setOnClickListener(v -> mMusicPlayerForSelectedDeviceAddress.stop());
323 
324         view.findViewById(R.id.media_delayed_focus_start)
325                 .setOnClickListener(v -> handleDelayedMediaStart());
326         view.findViewById(R.id.media_delayed_focus_stop)
327                 .setOnClickListener(v -> handleDelayedMediaStop());
328 
329         view.findViewById(R.id.phone_audio_focus_start)
330                 .setOnClickListener(v -> mPhoneAudioPlayer.start(true, true,
331                         AUDIOFOCUS_GAIN_TRANSIENT));
332         view.findViewById(R.id.phone_audio_focus_stop)
333                 .setOnClickListener(v -> mPhoneAudioPlayer.stop());
334 
335         view.findViewById(R.id.track_audio_start)
336                 .setOnClickListener(v-> startAudioTrack());
337         view.findViewById(R.id.track_audio_stop)
338                 .setOnClickListener(v -> stopAudioTrack());
339 
340         mDelayedStatusText = view.findViewById(R.id.media_delayed_player_status);
341 
342         return view;
343     }
344 
startAudioTrack()345     private void startAudioTrack() {
346         synchronized (mLock) {
347             mAudioTrackPlayer.start();
348         }
349     }
350 
stopAudioTrack()351     private void stopAudioTrack() {
352         synchronized (mLock) {
353             mAudioTrackPlayer.stop();
354         }
355     }
356 
357     @Override
onDestroyView()358     public void onDestroyView() {
359         Log.i(TAG, "onDestroyView");
360         for (AudioPlayer p : mAllPlayers) {
361             p.stop();
362         }
363         if (mMusicPlayerForSelectedDeviceAddress != null) {
364             mMusicPlayerForSelectedDeviceAddress.stop();
365             mMusicPlayerForSelectedDeviceAddress = null;
366         }
367         stopAudioTrack();
368         handleHwAudioSourceStop();
369         if (mAudioFocusHandler != null) {
370             mAudioFocusHandler.release();
371             mAudioFocusHandler = null;
372         }
373         if (mDelayedFocusRequest != null) {
374             mAudioManager.abandonAudioFocusRequest(mDelayedFocusRequest);
375         }
376         if (mAppFocusManager != null) {
377             mAppFocusManager.abandonAppFocus(mOwnershipCallbacks);
378         }
379         if (mCar != null && mCar.isConnected()) {
380             mCar.disconnect();
381             mCar = null;
382         }
383         super.onDestroyView();
384     }
385 
initializePlayers()386     private void initializePlayers() {
387         mMusicAudioAttrib = new AudioAttributes.Builder()
388                 .setUsage(USAGE_MEDIA)
389                 .build();
390         mNavAudioAttrib = new AudioAttributes.Builder()
391                 .setUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
392                 .build();
393         mPhoneAudioAttrib = new AudioAttributes.Builder()
394                 .setUsage(USAGE_VOICE_COMMUNICATION)
395                 .build();
396         mVrAudioAttrib = new AudioAttributes.Builder()
397                 .setUsage(USAGE_ASSISTANT)
398                 .build();
399         mRadioAudioAttrib = new AudioAttributes.Builder()
400                 .setUsage(USAGE_MEDIA)
401                 .build();
402         mSystemSoundAudioAttrib = new AudioAttributes.Builder()
403                 .setUsage(USAGE_ASSISTANCE_SONIFICATION)
404                 .build();
405         // Create an audio device address audio attribute
406         mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder()
407                 .setUsage(USAGE_MEDIA)
408                 .build();
409 
410 
411         mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext, well_worth_the_wait,
412                 mMusicAudioAttribForDeviceAddress);
413         mMusicPlayer = new AudioPlayer(mContext, well_worth_the_wait, mMusicAudioAttrib);
414         mMusicPlayerWithDelayedFocus = new AudioPlayer(mContext, well_worth_the_wait,
415                 mMusicAudioAttrib);
416         mMusicPlayerShort = new AudioPlayer(mContext, ring_classic_01, mMusicAudioAttrib);
417         mNavGuidancePlayer = new AudioPlayer(mContext, turnright, mNavAudioAttrib);
418         mPhoneAudioPlayer = new AudioPlayer(mContext, free_flight, mPhoneAudioAttrib);
419         mVrPlayer = new AudioPlayer(mContext, one2six, mVrAudioAttrib);
420         mSystemPlayer = new AudioPlayer(mContext, ring_classic_01, mSystemSoundAudioAttrib);
421         mWavPlayer = new AudioPlayer(mContext, free_flight, mMusicAudioAttrib);
422         final AudioDeviceInfo tuner = findTunerDevice(mContext);
423         if (tuner != null) {
424             mHwAudioSource = new HwAudioSource.Builder()
425                     .setAudioAttributes(mMusicAudioAttrib)
426                     .setAudioDeviceInfo(findTunerDevice(mContext))
427                     .build();
428         }
429         mAllPlayers = new AudioPlayer[] {
430                 mMusicPlayer,
431                 mMusicPlayerShort,
432                 mNavGuidancePlayer,
433                 mVrPlayer,
434                 mSystemPlayer,
435                 mWavPlayer,
436                 mMusicPlayerWithDelayedFocus,
437                 mPhoneAudioPlayer
438         };
439 
440         mAudioTrackPlayer = new AudioTrackPlayer(getTone(0));
441     }
442 
setActivityCurrentZoneId(TextView currentZoneIdTextView)443     private void setActivityCurrentZoneId(TextView currentZoneIdTextView) {
444         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
445             try {
446                 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
447                         mContext.getPackageName(), 0);
448                 int audioZoneId = mCarAudioManager.getZoneIdForUid(info.uid);
449                 currentZoneIdTextView.setText(Integer.toString(audioZoneId));
450             } catch (PackageManager.NameNotFoundException e) {
451                 Log.e(TAG, "setActivityCurrentZoneId Failed to find name: " , e);
452             }
453         }
454     }
455 
handleDelayedMediaStart()456     private void handleDelayedMediaStart() {
457         synchronized (mLock) {
458             if (mDelayedFocusRequest != null) {
459                 return;
460             }
461             mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();
462             mDelayedFocusRequest = new AudioFocusRequest
463                     .Builder(AUDIOFOCUS_GAIN)
464                     .setAudioAttributes(mMusicAudioAttrib)
465                     .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
466                     .setForceDucking(false)
467                     .setWillPauseWhenDucked(false)
468                     .setAcceptsDelayedFocusGain(true)
469                     .build();
470             int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
471             if (delayedFocusRequestResults == AUDIOFOCUS_REQUEST_GRANTED) {
472                 startDelayedMediaPlayerLocked();
473                 return;
474             }
475             if (delayedFocusRequestResults == AUDIOFOCUS_REQUEST_DELAYED) {
476                 if (DBG) Log.d(TAG, "Media With Delayed Focus delayed focus granted");
477                 mDelayedStatusText.setText(R.string.player_delayed);
478                 return;
479             }
480             mMediaWithDelayedFocusListener = null;
481             mDelayedFocusRequest = null;
482             mDelayedStatusText.setText(R.string.player_not_started);
483         }
484         if (DBG) Log.d(TAG, "Media With Delayed Focus focus rejected");
485     }
486 
startDelayedMediaPlayerLocked()487     private void startDelayedMediaPlayerLocked() {
488         if (!mMusicPlayerWithDelayedFocus.isPlaying()) {
489             if (DBG) Log.d(TAG, "Media With Delayed Focus starting player");
490             mMusicPlayerWithDelayedFocus.start(false, true,
491                     AUDIOFOCUS_GAIN);
492             mDelayedStatusText.setText(R.string.player_started);
493             return;
494         }
495         if (DBG) Log.d(TAG, "Media With Delayed Focus player already started");
496     }
497 
handleDelayedMediaStop()498     private void handleDelayedMediaStop() {
499         synchronized (mLock) {
500             if (mDelayedFocusRequest != null)  {
501                 int requestResults = mAudioManager.abandonAudioFocusRequest(mDelayedFocusRequest);
502                 if (DBG) {
503                     Log.d(TAG, "Media With Delayed Focus abandon focus " + requestResults);
504                 }
505                 mDelayedFocusRequest = null;
506                 mMediaWithDelayedFocusListener = null;
507                 stopDelayedMediaPlayerLocked();
508             }
509         }
510     }
511 
stopDelayedMediaPlayerLocked()512     private void stopDelayedMediaPlayerLocked() {
513         mDelayedStatusText.setText(R.string.player_not_started);
514         if (mMusicPlayerWithDelayedFocus.isPlaying()) {
515             if (DBG) Log.d(TAG, "Media With Delayed Focus stopping player");
516             mMusicPlayerWithDelayedFocus.stop();
517             return;
518         }
519         if (DBG) Log.d(TAG, "Media With Delayed Focus already stopped");
520     }
521 
pauseDelayedMediaPlayerLocked()522     private void pauseDelayedMediaPlayerLocked() {
523         mDelayedStatusText.setText(R.string.player_paused);
524         if (mMusicPlayerWithDelayedFocus.isPlaying()) {
525             if (DBG) Log.d(TAG, "Media With Delayed Focus pausing player");
526             mMusicPlayerWithDelayedFocus.stop();
527             return;
528         }
529         if (DBG) Log.d(TAG, "Media With Delayed Focus already stopped");
530     }
531 
setUpDeviceAddressLayoutView(View view)532     private void setUpDeviceAddressLayoutView(View view) {
533         mDeviceAddressLayout = view.findViewById(R.id.audio_select_device_address_layout);
534 
535         mDeviceAddressSpinner = view.findViewById(R.id.device_address_spinner);
536         mDeviceAddressSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
537             @Override
538             public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
539                 handleDeviceAddressSelection();
540             }
541 
542             @Override
543             public void onNothingSelected(AdapterView<?> parent) {
544             }
545         });
546     }
547 
setUpZoneSpinnerView(View view)548     private void setUpZoneSpinnerView(View view) {
549         mZoneSpinner = view.findViewById(R.id.zone_spinner);
550         mZoneSpinner.setEnabled(false);
551         mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
552             @Override
553             public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
554                 handleZoneSelection();
555             }
556 
557             @Override
558             public void onNothingSelected(AdapterView<?> parent) {
559             }
560         });
561     }
562 
handleZoneSelection()563     public void handleZoneSelection() {
564         int position = mZoneSpinner.getSelectedItemPosition();
565         int zone = mZoneAdapter.getItem(position);
566         Log.d(TAG, "Zone Selected: " + zone);
567         if (IS_EMULATOR && zone != PRIMARY_AUDIO_ZONE) {
568             setZoneToPlayOnSpeaker(zone);
569         }
570     }
571 
handleSetUpZoneSelection()572     private void handleSetUpZoneSelection() {
573         if (!IS_EMULATOR || !mCarAudioManager.isAudioFeatureEnabled(
574                 AUDIO_FEATURE_DYNAMIC_ROUTING)) {
575             return;
576         }
577         //take care of zone selection
578         List<Integer> zoneList = mCarAudioManager.getAudioZoneIds();
579         Integer[] zoneArray = zoneList.stream()
580                 .filter(i -> i != PRIMARY_AUDIO_ZONE).toArray(Integer[]::new);
581         mZoneAdapter = new ArrayAdapter<>(mContext,
582                 simple_spinner_item, zoneArray);
583         mZoneAdapter.setDropDownViewResource(
584                 simple_spinner_dropdown_item);
585         mZoneSpinner.setAdapter(mZoneAdapter);
586         mZoneSpinner.setEnabled(true);
587     }
588 
setUpTrackToneSpinnerView(View view)589     private void setUpTrackToneSpinnerView(View view) {
590         Spinner toneSpinner = view.findViewById(R.id.tone_spinner);
591         toneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
592             @Override
593             public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
594                 handleTrackToneSelection(pos);
595             }
596 
597             @Override
598             public void onNothingSelected(AdapterView<?> parent) {
599             }
600         });
601         ArrayAdapter<Integer> toneAdaper = new ArrayAdapter<Integer>(mContext,
602                 simple_spinner_item, TONES);
603         toneAdaper.setDropDownViewResource(
604                 simple_spinner_dropdown_item);
605         toneSpinner.setAdapter(toneAdaper);
606     }
607 
handleTrackToneSelection(int pos)608     private void handleTrackToneSelection(int pos) {
609         boolean isPlaying = false;
610         int tone = getTone(pos);
611         synchronized (mLock) {
612             if (mAudioTrackPlayer.isPlaying()) {
613                 if (DBG) {
614                     Log.d(TAG,
615                             "Audio Track currently playing, stopping. Switching to frequency: "
616                                     + tone);
617                 }
618                 isPlaying = true;
619                 mAudioTrackPlayer.stop();
620             }
621             mAudioTrackPlayer = new AudioTrackPlayer(tone);
622 
623             if (isPlaying) {
624                 if (DBG) {
625                     Log.d(TAG,
626                             "Audio Track was playing, starting again. Switched to frequency: "
627                             + tone);
628                 }
629                 mAudioTrackPlayer.start();
630             }
631         }
632     }
633 
getTone(int index)634     private static int getTone(int index) {
635         Preconditions.checkArgumentInRange(index, 0, TONES.length, "index");
636         return TONES[index];
637     }
638 
setUpCarSoundsLayouts(View view)639     private void setUpCarSoundsLayouts(View view) {
640         mPlayerTabLayout = view.findViewById(R.id.audio_player_tabs);
641         mViewPager = view.findViewById(R.id.zones_player_view_pager);
642         mAudioPlayerZoneAdapter = new CarAudioZoneTabAdapter(getChildFragmentManager());
643         mViewPager.setAdapter(mAudioPlayerZoneAdapter);
644         mAudioPlayerZoneAdapter.addFragment(
645                 new AudioSystemPlayerFragment(mCarAudioManager, mAudioManager),
646                 "System Players");
647         mAudioPlayerZoneAdapter.addFragment(new AudioTransientPlayersFragment(),
648                 "Transient Sound Players");
649         mAudioPlayerZoneAdapter.notifyDataSetChanged();
650         mPlayerTabLayout.setupWithViewPager(mViewPager);
651     }
652 
handleNavStart()653     private void handleNavStart() {
654         if (mAppFocusManager == null) {
655             Log.e(TAG, "mAppFocusManager is null");
656             return;
657         }
658         if (DBG) {
659             Log.i(TAG, "Nav start");
660         }
661         mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, mOwnershipCallbacks);
662         mAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib,
663                 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
664     }
665 
handleNavEnd()666     private void handleNavEnd() {
667         if (mAppFocusManager == null) {
668             Log.e(TAG, "mAppFocusManager is null");
669             return;
670         }
671         if (DBG) {
672             Log.i(TAG, "Nav end");
673         }
674         mAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib);
675         mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_NAVIGATION);
676     }
677 
findTunerDevice(Context context)678     private AudioDeviceInfo findTunerDevice(Context context) {
679         AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
680         AudioDeviceInfo[] devices = am.getDevices(GET_DEVICES_INPUTS);
681         for (AudioDeviceInfo device : devices) {
682             if (device.getType() == AudioDeviceInfo.TYPE_FM_TUNER) {
683                 return device;
684             }
685         }
686         return null;
687     }
688 
handleHwAudioSourceStart()689     private void handleHwAudioSourceStart() {
690         if (mHwAudioSource != null && !mHwAudioSource.isPlaying()) {
691             mHwAudioSource.start();
692         }
693     }
694 
handleHwAudioSourceStop()695     private void handleHwAudioSourceStop() {
696         if (mHwAudioSource != null) {
697             mHwAudioSource.stop();
698         }
699     }
700 
handleVrStart()701     private void handleVrStart() {
702         if (mAppFocusManager == null) {
703             Log.e(TAG, "mAppFocusManager is null");
704             return;
705         }
706         if (DBG) {
707             Log.i(TAG, "VR start");
708         }
709         mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_VOICE_COMMAND, mOwnershipCallbacks);
710         mAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib,
711                 AUDIOFOCUS_GAIN_TRANSIENT, 0);
712     }
713 
handleVrEnd()714     private void handleVrEnd() {
715         if (mAppFocusManager == null) {
716             Log.e(TAG, "mAppFocusManager is null");
717             return;
718         }
719         if (DBG) {
720             Log.i(TAG, "VR end");
721         }
722         mAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib);
723         mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_VOICE_COMMAND);
724     }
725 
handleRadioStart()726     private void handleRadioStart() {
727         if (DBG) {
728             Log.i(TAG, "Radio start");
729         }
730         mAudioManager.requestAudioFocus(mRadioFocusListener, mRadioAudioAttrib, AUDIOFOCUS_GAIN, 0);
731     }
732 
handleRadioEnd()733     private void handleRadioEnd() {
734         if (DBG) {
735             Log.i(TAG, "Radio end");
736         }
737         mAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib);
738     }
739 
setUpDeviceAddressPlayer()740     private void setUpDeviceAddressPlayer() {
741         if (!mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
742             mDeviceAddressLayout.setVisibility(View.GONE);
743             return;
744         }
745         mDeviceAddressLayout.setVisibility(View.VISIBLE);
746         List<CarAudioZoneDeviceInfo> deviceList = new ArrayList<>();
747         for (int audioZoneId: mCarAudioManager.getAudioZoneIds()) {
748             AudioDeviceInfo deviceInfo = mCarAudioManager
749                     .getOutputDeviceForUsage(audioZoneId, USAGE_MEDIA);
750             CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = new CarAudioZoneDeviceInfo();
751             carAudioZoneDeviceInfo.mDeviceInfo = deviceInfo;
752             carAudioZoneDeviceInfo.mAudioZoneId = audioZoneId;
753             deviceList.add(carAudioZoneDeviceInfo);
754             if (DBG) {
755                 Log.d(TAG, "Found device address"
756                         + carAudioZoneDeviceInfo.mDeviceInfo.getAddress()
757                         + " for audio zone id " + audioZoneId);
758             }
759 
760         }
761 
762         CarAudioZoneDeviceInfo[] deviceArray =
763                 deviceList.stream().toArray(CarAudioZoneDeviceInfo[]::new);
764         mDeviceAddressAdapter = new ArrayAdapter<>(mContext,
765                 simple_spinner_item, deviceArray);
766         mDeviceAddressAdapter.setDropDownViewResource(
767                 simple_spinner_dropdown_item);
768         mDeviceAddressSpinner.setAdapter(mDeviceAddressAdapter);
769         createDeviceAddressAudioPlayer();
770     }
771 
createDeviceAddressAudioPlayer()772     private void createDeviceAddressAudioPlayer() {
773         CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = mDeviceAddressAdapter.getItem(
774                 mDeviceAddressSpinner.getSelectedItemPosition());
775         Log.d(TAG, "Setting Bundle to zone " + carAudioZoneDeviceInfo.mAudioZoneId);
776         Bundle bundle = new Bundle();
777         bundle.putInt(AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID, carAudioZoneDeviceInfo.mAudioZoneId);
778         mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder()
779                 .setUsage(USAGE_MEDIA)
780                 .addBundle(bundle)
781                 .build();
782 
783         mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext,
784                 well_worth_the_wait,
785                 mMusicAudioAttribForDeviceAddress,
786                 carAudioZoneDeviceInfo.mDeviceInfo);
787     }
788 
startDeviceAudio()789     private void startDeviceAudio() {
790         Log.d(TAG, "Starting device address audio");
791         mMusicPlayerForSelectedDeviceAddress.start(true, false,
792                 AUDIOFOCUS_GAIN_TRANSIENT);
793     }
794 
handleDeviceAddressSelection()795     public void handleDeviceAddressSelection() {
796         if (mMusicPlayerForSelectedDeviceAddress != null
797                 && mMusicPlayerForSelectedDeviceAddress.isPlaying()) {
798             mMusicPlayerForSelectedDeviceAddress.stop();
799         }
800         createDeviceAddressAudioPlayer();
801     }
802 
803     /**
804      * Sets the left speaker to output sound from zoneId
805      * @param zoneId zone id to set left speakers output
806      * @Note this should only be used with emulator where the zones are separated into right
807      * and left speaker, other platforms would have real devices where audio is routed.
808      */
setZoneToPlayOnSpeaker(int zoneId)809     private void setZoneToPlayOnSpeaker(int zoneId) {
810         String selectedZoneKeyValueString = AAE_PARAMETER_KEY_FOR_SELECTED_ZONE + "=" + zoneId;
811         // send key value  parameter list to audio HAL
812         mAudioManager.setParameters(selectedZoneKeyValueString);
813         Log.d(TAG, "setZoneToPlayOnSpeaker : " + zoneId);
814     }
815 
816 
817     private class FocusHandler {
818         private static final String AUDIO_FOCUS_STATE_GAIN = "gain";
819         private static final String AUDIO_FOCUS_STATE_RELEASED_UNKNOWN = "released / unknown";
820 
821         private final RadioGroup mRequestSelection;
822         private final TextView mText;
823         private final AudioFocusListener mFocusListener;
824         private AudioFocusRequest mFocusRequest;
825 
FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text)826         public FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text) {
827             mText = text;
828             mRequestSelection = radioGroup;
829             mRequestSelection.check(R.id.focus_gain);
830             setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
831             mFocusListener = new AudioFocusListener();
832             requestButton.setOnClickListener(v -> {
833                 int selectedButtonId = mRequestSelection.getCheckedRadioButtonId();
834                 int focusRequest;
835                 switch (selectedButtonId) {
836                     case R.id.focus_gain:
837                         focusRequest = AUDIOFOCUS_GAIN;
838                         break;
839                     case R.id.focus_gain_transient:
840                         focusRequest = AUDIOFOCUS_GAIN_TRANSIENT;
841                         break;
842                     case R.id.focus_gain_transient_duck:
843                         focusRequest = AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
844                         break;
845                     case R.id.focus_gain_transient_exclusive:
846                         focusRequest = AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE;
847                         break;
848                     case R.id.focus_release:
849                     default:
850                         abandonAudioFocus();
851                         return;
852                 }
853                 mFocusRequest = new AudioFocusRequest.Builder(focusRequest)
854                         .setAudioAttributes(new AudioAttributes.Builder()
855                                 .setUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
856                                 .build())
857                         .setOnAudioFocusChangeListener(mFocusListener)
858                         .build();
859                 int ret = mAudioManager.requestAudioFocus(mFocusRequest);
860                 Log.i(TAG, "requestAudioFocus returned " + ret);
861                 if (ret == AUDIOFOCUS_REQUEST_GRANTED) {
862                     setFocusText(AUDIO_FOCUS_STATE_GAIN);
863                 }
864             });
865         }
866 
release()867         public void release() {
868             abandonAudioFocus();
869         }
870 
abandonAudioFocus()871         private void abandonAudioFocus() {
872             if (DBG) {
873                 Log.i(TAG, "abandonAudioFocus");
874             }
875             if (mFocusRequest != null) {
876                 mAudioManager.abandonAudioFocusRequest(mFocusRequest);
877                 mFocusRequest = null;
878             } else {
879                 Log.i(TAG, "mFocusRequest is already null");
880             }
881             setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN);
882         }
883 
setFocusText(String msg)884         private void setFocusText(String msg) {
885             mText.setText("focus state:" + msg);
886         }
887 
888         private class AudioFocusListener implements OnAudioFocusChangeListener {
889             @Override
onAudioFocusChange(int focusChange)890             public void onAudioFocusChange(int focusChange) {
891                 Log.i(TAG, "onAudioFocusChange " + focusChange);
892                 if (focusChange == AUDIOFOCUS_GAIN) {
893                     setFocusText(AUDIO_FOCUS_STATE_GAIN);
894                 } else if (focusChange == AUDIOFOCUS_LOSS) {
895                     setFocusText("loss");
896                 } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
897                     setFocusText("loss,transient");
898                 } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
899                     setFocusText("loss,transient,duck");
900                 }
901             }
902         }
903     }
904 
905     private final class MediaWithDelayedFocusListener implements OnAudioFocusChangeListener {
906         @Override
onAudioFocusChange(int focusChange)907         public void onAudioFocusChange(int focusChange) {
908             if (DBG) Log.d(TAG, "Media With Delayed Focus focus change:" + focusChange);
909             synchronized (mLock) {
910                 switch (focusChange) {
911                     case AUDIOFOCUS_GAIN:
912                     case AUDIOFOCUS_GAIN_TRANSIENT:
913                     case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
914                     case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
915                         startDelayedMediaPlayerLocked();
916                         break;
917                     case AUDIOFOCUS_LOSS_TRANSIENT:
918                     case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
919                         pauseDelayedMediaPlayerLocked();
920                         break;
921                     case AUDIOFOCUS_LOSS:
922                     default:
923                         stopDelayedMediaPlayerLocked();
924                         mDelayedFocusRequest = null;
925                         mMediaWithDelayedFocusListener = null;
926                         break;
927                 }
928             }
929         }
930     }
931 
932     private class CarAudioZoneDeviceInfo {
933         AudioDeviceInfo mDeviceInfo;
934         int mAudioZoneId;
935 
936         @Override
toString()937         public String toString() {
938             StringBuilder builder = new StringBuilder();
939             builder.append("Device Address : ");
940             builder.append(mDeviceInfo.getAddress());
941             builder.append(", Audio Zone Id: ");
942             builder.append(mAudioZoneId);
943             return builder.toString();
944         }
945     }
946 
getAudioLogTag(Class clazz)947     static String getAudioLogTag(Class clazz) {
948         return TAG + "." + clazz.getSimpleName();
949     }
950 }
951