• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         int message;
133         synchronized (mLock) {
134             if (mFocusRequest != null) {
135                 return;
136             }
137             mMediaFocusListener = new AutoStartFocusListener();
138             mFocusRequest = new AudioFocusRequest
139                     .Builder(AUDIOFOCUS_GAIN)
140                     .setAudioAttributes(MUSIC_AUDIO_ATTRIBUTES)
141                     .setOnAudioFocusChangeListener(mMediaFocusListener)
142                     .setForceDucking(false)
143                     .setWillPauseWhenDucked(false)
144                     .setAcceptsDelayedFocusGain(true)
145                     .build();
146             delayedFocusRequestResults = mAudioManager.requestAudioFocus(mFocusRequest);
147         }
148 
149         switch (delayedFocusRequestResults) {
150             case AUDIOFOCUS_REQUEST_GRANTED:
151                 if (Log.isLoggable(TAG, DEBUG)) {
152                     Log.d(TAG, "Audio Auto Start Player granted");
153                 }
154                 mHandler.post(() -> startMediaPlayer());
155                 break;
156             case AUDIOFOCUS_REQUEST_DELAYED:
157                 if (Log.isLoggable(TAG, DEBUG)) {
158                     Log.d(TAG, "Media With Delayed Focus delayed");
159                 }
160                 mHandler.post(() -> delayMediaPlayer());
161                 break;
162             case AUDIOFOCUS_REQUEST_FAILED:
163             default:
164                 if (Log.isLoggable(TAG, DEBUG)) {
165                     Log.d(TAG, "Audio Auto Start Player rejected");
166                 }
167                 mHandler.post(()  -> stopMediaPlayer());
168                 synchronized (mLock) {
169                     mMediaFocusListener = null;
170                     mFocusRequest = null;
171                 }
172         }
173     }
174 
createPlayer()175     private MediaPlayer createPlayer() throws IOException {
176         MediaPlayer mediaPlayer = new MediaPlayer();
177         AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.testmp3_2);
178         mediaPlayer.setAudioAttributes(MUSIC_AUDIO_ATTRIBUTES);
179         mediaPlayer.setDataSource(afd);
180         mediaPlayer.setLooping(true);
181         mediaPlayer.setVolume(1.0f, 1.0f);
182         mediaPlayer.prepare();
183         return mediaPlayer;
184     }
185 
stopMediaPlayer()186     private void stopMediaPlayer() {
187         synchronized (mLock) {
188             mPlayerStatusText.setText(R.string.player_stopped);
189             if (mPlayer == null) {
190                 Log.i(TAG, "Can not stop non existent player");
191                 return;
192             }
193             mPlayer.stop();
194             mPlayer.release();
195             mPlayer = null;
196         }
197     }
198 
pauseMediaPlayer()199     private void pauseMediaPlayer() {
200         synchronized (mLock) {
201             mPlayerStatusText.setText(R.string.player_paused);
202             if (mPlayer == null) {
203                 Log.i(TAG, "Can not pause non existent player");
204                 return;
205             }
206             mPlayer.pause();
207         }
208     }
209 
startMediaPlayer()210     private void startMediaPlayer() {
211         synchronized (mLock) {
212             if (mPlayer == null) {
213                 try {
214                     mPlayer = createPlayer();
215                 } catch (IOException e) {
216                     Log.e(TAG, "Can not media start player", e);
217                     return;
218                 }
219             }
220             mPlayer.start();
221             mPlayerStatusText.setText(R.string.player_started);
222         }
223     }
224 
delayMediaPlayer()225     private void delayMediaPlayer() {
226         synchronized (mLock) {
227             mPlayerStatusText.setText(R.string.player_delayed);
228         }
229     }
230 
231     private class AutoStartFocusListener implements OnAudioFocusChangeListener {
232 
233         @Override
onAudioFocusChange(int focusChange)234         public void onAudioFocusChange(int focusChange) {
235             if (Log.isLoggable(TAG, DEBUG)) {
236                 Log.d(TAG, "Audio Auto Start Player Focus change:" + focusChange);
237             }
238             switch (focusChange) {
239                 case AUDIOFOCUS_GAIN:
240                 case AUDIOFOCUS_GAIN_TRANSIENT:
241                 case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
242                 case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
243                     mHandler.post(() -> startMediaPlayer());
244                     return;
245                 case AUDIOFOCUS_LOSS_TRANSIENT:
246                 case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
247                     mHandler.post(() -> pauseMediaPlayer());
248                     return;
249                 case AUDIOFOCUS_LOSS:
250                 default:
251                     mHandler.post(() -> stopMediaPlayer());
252                     synchronized (mLock) {
253                         mFocusRequest = null;
254                         mMediaFocusListener = null;
255                     }
256                     return;
257             }
258         }
259     }
260 }
261