1 /* 2 * Copyright (C) 2021 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; 18 19 import static android.car.Car.APP_FOCUS_SERVICE; 20 import static android.car.Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER; 21 import static android.car.Car.createCar; 22 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION; 23 import static android.car.CarAppFocusManager.OnAppFocusChangedListener; 24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING; 25 import static android.media.AudioManager.AUDIOFOCUS_GAIN; 26 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; 27 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; 28 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; 29 import static android.media.AudioManager.AUDIOFOCUS_LOSS; 30 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 31 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; 32 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 33 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_FAILED; 34 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 35 import static android.util.Log.DEBUG; 36 37 import android.app.Activity; 38 import android.car.Car; 39 import android.car.CarAppFocusManager; 40 import android.car.media.CarAudioManager; 41 import android.content.pm.ApplicationInfo; 42 import android.content.pm.PackageManager; 43 import android.content.res.AssetFileDescriptor; 44 import android.media.AudioAttributes; 45 import android.media.AudioFocusRequest; 46 import android.media.AudioManager; 47 import android.media.AudioManager.OnAudioFocusChangeListener; 48 import android.media.MediaPlayer; 49 import android.os.Bundle; 50 import android.os.Handler; 51 import android.os.Looper; 52 import android.util.Log; 53 import android.widget.TextView; 54 55 import java.io.IOException; 56 57 import javax.annotation.concurrent.GuardedBy; 58 59 public final class AudioAutoStartActivity extends Activity { 60 61 private static final String TAG = "CAR.AUDIO.KS.AUTO_START"; 62 private static final AudioAttributes MUSIC_AUDIO_ATTRIBUTES = new AudioAttributes.Builder() 63 .setUsage(AudioAttributes.USAGE_MEDIA) 64 .build(); 65 66 private Handler mHandler; 67 private Car mCar; 68 private CarAppFocusManager mAppFocusManager; 69 private CarAudioManager mCarAudioManager; 70 private AudioManager mAudioManager; 71 72 private final Object mLock = new Object(); 73 @GuardedBy("mLock") 74 private AudioFocusRequest mFocusRequest; 75 @GuardedBy("mLock") 76 private OnAudioFocusChangeListener mMediaFocusListener; 77 @GuardedBy("mLock") 78 private MediaPlayer mPlayer; 79 private TextView mPlayerStatusText; 80 81 @Override onCreate(Bundle savedInstanceState)82 protected void onCreate(Bundle savedInstanceState) { 83 super.onCreate(savedInstanceState); 84 connectCar(); 85 setContentView(R.layout.audio_auto_start_activity); 86 87 mAudioManager = getSystemService(AudioManager.class); 88 89 TextView currentZoneIdTextView = findViewById(R.id.activity_current_zone); 90 setActivityCurrentZoneId(currentZoneIdTextView); 91 92 mPlayerStatusText = findViewById(R.id.player_status); 93 94 requestAudioFocus(); 95 } 96 connectCar()97 private void connectCar() { 98 mHandler = new Handler(Looper.getMainLooper(), null, true); 99 mCar = createCar(this, null, 100 CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> { 101 if (!ready) { 102 return; 103 } 104 mAppFocusManager = 105 (CarAppFocusManager) car.getCarManager(APP_FOCUS_SERVICE); 106 mAppFocusManager.addFocusListener( 107 (OnAppFocusChangedListener) (appType, active) -> { 108 109 }, 110 APP_FOCUS_TYPE_NAVIGATION); 111 112 mCarAudioManager = (CarAudioManager) car.getCarManager(AUDIO_SERVICE); 113 }); 114 } 115 setActivityCurrentZoneId(TextView currentZoneIdTextView)116 private void setActivityCurrentZoneId(TextView currentZoneIdTextView) { 117 if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) { 118 try { 119 ApplicationInfo info = getPackageManager().getApplicationInfo( 120 getPackageName(), 0); 121 int audioZoneId = mCarAudioManager.getZoneIdForUid(info.uid); 122 currentZoneIdTextView.setText(Integer.toString(audioZoneId)); 123 } catch (PackageManager.NameNotFoundException e) { 124 Log.e(TAG, "setActivityCurrentZoneId Failed to find package name " 125 + getPackageName(), e); 126 } 127 } 128 } 129 requestAudioFocus()130 private void requestAudioFocus() { 131 int delayedFocusRequestResults; 132 synchronized (mLock) { 133 if (mFocusRequest != null) { 134 return; 135 } 136 mMediaFocusListener = new AutoStartFocusListener(); 137 mFocusRequest = new AudioFocusRequest 138 .Builder(AUDIOFOCUS_GAIN) 139 .setAudioAttributes(MUSIC_AUDIO_ATTRIBUTES) 140 .setOnAudioFocusChangeListener(mMediaFocusListener) 141 .setForceDucking(false) 142 .setWillPauseWhenDucked(false) 143 .setAcceptsDelayedFocusGain(true) 144 .build(); 145 delayedFocusRequestResults = mAudioManager.requestAudioFocus(mFocusRequest); 146 } 147 148 switch (delayedFocusRequestResults) { 149 case AUDIOFOCUS_REQUEST_GRANTED: 150 if (Log.isLoggable(TAG, DEBUG)) { 151 Log.d(TAG, "Audio Auto Start Player granted"); 152 } 153 mHandler.post(() -> startMediaPlayer()); 154 break; 155 case AUDIOFOCUS_REQUEST_DELAYED: 156 if (Log.isLoggable(TAG, DEBUG)) { 157 Log.d(TAG, "Media With Delayed Focus delayed"); 158 } 159 mHandler.post(() -> delayMediaPlayer()); 160 break; 161 case AUDIOFOCUS_REQUEST_FAILED: 162 default: 163 if (Log.isLoggable(TAG, DEBUG)) { 164 Log.d(TAG, "Audio Auto Start Player rejected"); 165 } 166 mHandler.post(() -> stopMediaPlayer()); 167 synchronized (mLock) { 168 mMediaFocusListener = null; 169 mFocusRequest = null; 170 } 171 } 172 } 173 createPlayer()174 private MediaPlayer createPlayer() throws IOException { 175 MediaPlayer mediaPlayer = new MediaPlayer(); 176 AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.testmp3_2); 177 mediaPlayer.setAudioAttributes(MUSIC_AUDIO_ATTRIBUTES); 178 mediaPlayer.setDataSource(afd); 179 mediaPlayer.setLooping(true); 180 mediaPlayer.setVolume(1.0f, 1.0f); 181 mediaPlayer.prepare(); 182 return mediaPlayer; 183 } 184 stopMediaPlayer()185 private void stopMediaPlayer() { 186 synchronized (mLock) { 187 mPlayerStatusText.setText(R.string.player_stopped); 188 if (mPlayer == null) { 189 Log.i(TAG, "Can not stop non existent player"); 190 return; 191 } 192 mPlayer.stop(); 193 mPlayer.release(); 194 mPlayer = null; 195 } 196 } 197 pauseMediaPlayer()198 private void pauseMediaPlayer() { 199 synchronized (mLock) { 200 mPlayerStatusText.setText(R.string.player_paused); 201 if (mPlayer == null) { 202 Log.i(TAG, "Can not pause non existent player"); 203 return; 204 } 205 mPlayer.pause(); 206 } 207 } 208 startMediaPlayer()209 private void startMediaPlayer() { 210 synchronized (mLock) { 211 if (mPlayer == null) { 212 try { 213 mPlayer = createPlayer(); 214 } catch (IOException e) { 215 Log.e(TAG, "Can not media start player", e); 216 return; 217 } 218 } 219 mPlayer.start(); 220 mPlayerStatusText.setText(R.string.player_started); 221 } 222 } 223 delayMediaPlayer()224 private void delayMediaPlayer() { 225 synchronized (mLock) { 226 mPlayerStatusText.setText(R.string.player_delayed); 227 } 228 } 229 230 private class AutoStartFocusListener implements OnAudioFocusChangeListener { 231 232 @Override onAudioFocusChange(int focusChange)233 public void onAudioFocusChange(int focusChange) { 234 if (Log.isLoggable(TAG, DEBUG)) { 235 Log.d(TAG, "Audio Auto Start Player Focus change:" + focusChange); 236 } 237 switch (focusChange) { 238 case AUDIOFOCUS_GAIN: 239 case AUDIOFOCUS_GAIN_TRANSIENT: 240 case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 241 case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 242 mHandler.post(() -> startMediaPlayer()); 243 return; 244 case AUDIOFOCUS_LOSS_TRANSIENT: 245 case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 246 mHandler.post(() -> pauseMediaPlayer()); 247 return; 248 case AUDIOFOCUS_LOSS: 249 default: 250 mHandler.post(() -> stopMediaPlayer()); 251 synchronized (mLock) { 252 mFocusRequest = null; 253 mMediaFocusListener = null; 254 } 255 return; 256 } 257 } 258 } 259 } 260