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