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