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