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