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