Snap for 4781566 from cbed6abf33fd300f3bf2e7f1659b12c97aa2d093 to pi-release

Change-Id: I061974033182a10b922700eb49bc8fc8b00365f6
diff --git a/src/com/android/car/messenger/MapMessageMonitor.java b/src/com/android/car/messenger/MapMessageMonitor.java
index b191e4b..3c427dd 100644
--- a/src/com/android/car/messenger/MapMessageMonitor.java
+++ b/src/com/android/car/messenger/MapMessageMonitor.java
@@ -35,7 +35,6 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -49,7 +48,6 @@
 
 import com.android.car.apps.common.LetterTileDrawable;
 import com.android.car.messenger.tts.TTSHelper;
-
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.request.RequestOptions;
 import com.bumptech.glide.request.target.SimpleTarget;
@@ -94,8 +92,6 @@
     private final Map<SenderKey, NotificationInfo> mNotificationInfos = new HashMap<>();
     private final TTSHelper mTTSHelper;
     private final HashMap<String, Boolean> mReplyFeatureMap = new HashMap<>();
-    private final AudioManager mAudioManager;
-    private final AudioManager.OnAudioFocusChangeListener mNoOpAFChangeListener = (f) -> {};
 
     MapMessageMonitor(Context context) {
         mContext = context;
@@ -104,8 +100,6 @@
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mTTSHelper = new TTSHelper(mContext);
-
-        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
     }
 
     public boolean isPlaying() {
@@ -348,34 +342,29 @@
         ttsMessages.add(mContext.getString(R.string.tts_sender_says, notificationInfo.mSenderName));
         ttsMessages.add(ttsMessage);
 
-        int result = mAudioManager.requestAudioFocus(mNoOpAFChangeListener,
-                // Use the music stream.
-                AudioManager.STREAM_MUSIC,
-                // Request permanent focus.
-                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
-            mTTSHelper.requestPlay(ttsMessages,
-                    new TTSHelper.Listener() {
-                        @Override
-                        public void onTTSStarted() {
-                            Intent intent = new Intent(ACTION_MESSAGE_PLAY_START);
-                            mContext.sendBroadcast(intent);
-                        }
+        mTTSHelper.requestPlay(ttsMessages,
+                new TTSHelper.Listener() {
+                    @Override
+                    public void onTTSStarted() {
+                        Intent intent = new Intent(ACTION_MESSAGE_PLAY_START);
+                        mContext.sendBroadcast(intent);
+                    }
 
-                        @Override
-                        public void onTTSStopped(boolean error) {
-                            mAudioManager.abandonAudioFocus(mNoOpAFChangeListener);
-                            Intent intent = new Intent(ACTION_MESSAGE_PLAY_STOP);
-                            mContext.sendBroadcast(intent);
-                            if (error) {
-                                Toast.makeText(mContext, R.string.tts_failed_toast,
-                                        Toast.LENGTH_SHORT).show();
-                            }
+                    @Override
+                    public void onTTSStopped(boolean error) {
+                        Intent intent = new Intent(ACTION_MESSAGE_PLAY_STOP);
+                        mContext.sendBroadcast(intent);
+                        if (error) {
+                            Toast.makeText(mContext, R.string.tts_failed_toast,
+                                    Toast.LENGTH_SHORT).show();
                         }
-                    });
-        } else {
-            Log.w(TAG, "failed to require audio focus.");
-        }
+                    }
+
+                    @Override
+                    public void onAudioFocusFailed() {
+                        Log.w(TAG, "failed to require audio focus.");
+                    }
+                });
     }
 
     void stopPlayout() {
diff --git a/src/com/android/car/messenger/PlayMessageActivity.java b/src/com/android/car/messenger/PlayMessageActivity.java
index 90dcf29..7bfc5f9 100644
--- a/src/com/android/car/messenger/PlayMessageActivity.java
+++ b/src/com/android/car/messenger/PlayMessageActivity.java
@@ -159,6 +159,11 @@
                         }
                         finish();
                     }
+
+                    @Override
+                    public void onAudioFocusFailed() {
+                        Log.w(TAG, "failed to require audio focus.");
+                    }
                 });
     }
 
diff --git a/src/com/android/car/messenger/tts/AndroidTTSEngine.java b/src/com/android/car/messenger/tts/AndroidTTSEngine.java
index b6a7988..70d0aff 100644
--- a/src/com/android/car/messenger/tts/AndroidTTSEngine.java
+++ b/src/com/android/car/messenger/tts/AndroidTTSEngine.java
@@ -54,4 +54,9 @@
         mTextToSpeech.shutdown();
         mTextToSpeech = null;
     }
+
+    @Override
+    public int getStream() {
+        return TextToSpeech.Engine.DEFAULT_STREAM;
+    }
 }
diff --git a/src/com/android/car/messenger/tts/FakeTTSEngine.java b/src/com/android/car/messenger/tts/FakeTTSEngine.java
index 0870379..6af64e4 100644
--- a/src/com/android/car/messenger/tts/FakeTTSEngine.java
+++ b/src/com/android/car/messenger/tts/FakeTTSEngine.java
@@ -53,6 +53,11 @@
         mOnInitListener = null;
     }
 
+    @Override
+    public int getStream() {
+        return TextToSpeech.Engine.DEFAULT_STREAM;
+    }
+
     void startRequest(String utteranceId) {
         mProgressListener.onStart(utteranceId);
     }
diff --git a/src/com/android/car/messenger/tts/TTSEngine.java b/src/com/android/car/messenger/tts/TTSEngine.java
index ed1313f..e789327 100644
--- a/src/com/android/car/messenger/tts/TTSEngine.java
+++ b/src/com/android/car/messenger/tts/TTSEngine.java
@@ -48,4 +48,9 @@
      * using this engine.
      */
     void shutdown();
+
+    /**
+     * Returns the stream used by this TTS engine.
+     */
+    int getStream();
 }
diff --git a/src/com/android/car/messenger/tts/TTSHelper.java b/src/com/android/car/messenger/tts/TTSHelper.java
index 8ad132e..aef20d7 100644
--- a/src/com/android/car/messenger/tts/TTSHelper.java
+++ b/src/com/android/car/messenger/tts/TTSHelper.java
@@ -17,6 +17,7 @@
 package com.android.car.messenger.tts;
 
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.Handler;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.UtteranceProgressListener;
@@ -57,6 +58,12 @@
          *              not considered an error.
          */
         void onTTSStopped(boolean error);
+
+        /**
+         * Called when request to get audio focus failed. This happens before the requested TTS
+         * is played.
+         */
+        void onAudioFocusFailed();
     }
 
     private static final String TAG = "Messenger.TTSHelper";
@@ -67,6 +74,8 @@
 
     private final Handler mHandler = new Handler();
     private final Context mContext;
+    private final AudioManager mAudioManager;
+    private final AudioManager.OnAudioFocusChangeListener mNoOpAFChangeListener = (f) -> {};
     private final long mShutdownDelayMillis;
     private TTSEngine mTTSEngine;
     private int mInitStatus;
@@ -84,6 +93,7 @@
     @VisibleForTesting
     TTSHelper(Context context, TTSEngine ttsEngine, long shutdownDelayMillis) {
         mContext = context;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mTTSEngine = ttsEngine;
         mShutdownDelayMillis = shutdownDelayMillis;
         // OnInitListener will only set to SUCCESS/ERROR. So we initialize to STOPPED.
@@ -130,6 +140,7 @@
      * until then. Only one batch is supported at a time; If a previous batch is waiting engine
      * setup, that batch is dropped. If a previous batch is playing, the play-out is stopped and
      * next one is passed to the TTS Engine. Callbacks are issued on the provided {@code listener}.
+     * Will request audio focus first, failure will trigger onAudioFocusFailed in listener.
      *
      * NOTE: Underlying engine may have limit on length of text in each element of the batch; it
      * will reject anything longer. See {@link TextToSpeech#getMaxSpeechInputLength()}.
@@ -138,16 +149,23 @@
      * @param listener Observer that will receive callbacks about play-out progress.
      */
     public void requestPlay(List<CharSequence> textToSpeak, Listener listener) {
-        if (textToSpeak == null || textToSpeak.size() < 1) {
+        if (textToSpeak == null || textToSpeak.isEmpty()) {
             throw new IllegalArgumentException("Empty/null textToSpeak");
         }
+        int result = mAudioManager.requestAudioFocus(mNoOpAFChangeListener,
+                getStream(),
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+        if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+            listener.onAudioFocusFailed();
+            return;
+        }
         initMaybeAndKeepAlive();
 
         // Check if its still initializing.
         if (mInitStatus == TextToSpeech.STOPPED) {
             // Squash any already queued request.
             if (mPendingRequest != null) {
-                mPendingRequest.mListener.onTTSStopped(false /* error */);
+                onTtsStopped(mPendingRequest.mListener, false /* error */);
             }
             mPendingRequest = new SpeechRequest(textToSpeak, listener);
         } else {
@@ -164,10 +182,16 @@
         return mTTSEngine.isSpeaking();
     }
 
+    // wrap call back to listener.onTTSStopped with adandonAudioFocus.
+    private void onTtsStopped(Listener listener, boolean error) {
+        mAudioManager.abandonAudioFocus(mNoOpAFChangeListener);
+        mHandler.post(() -> listener.onTTSStopped(error));
+    }
+
     private void playInternal(List<CharSequence> textToSpeak, Listener listener) {
         if (mInitStatus == TextToSpeech.ERROR) {
             Log.e(TAG, "TTS setup failed!");
-            mHandler.post(() -> listener.onTTSStopped(true /* error */));
+            onTtsStopped(listener, true /* error */);
             return;
         }
 
@@ -189,7 +213,7 @@
                 mTTSEngine.stop();
                 currentBatchId = null;
                 Log.e(TAG, "Queuing text failed!");
-                mHandler.post(() -> listener.onTTSStopped(true /* error */));
+                onTtsStopped(listener, true /* error */);
                 return;
             }
             index--;
@@ -207,6 +231,10 @@
         shutdownEngine();
     }
 
+    public int getStream() {
+        return mTTSEngine.getStream();
+    }
+
     private void shutdownEngine() {
         if (mTTSEngine.isInitialized()) {
             if (DBG) {
@@ -325,7 +353,7 @@
         // Handles terminal callbacks for the batch. We invoke stopped and remove ourselves.
         // No further callbacks will be handled for the batch.
         private void handleBatchFinished(Pair<String, Integer> parsedId, boolean error) {
-            mListener.onTTSStopped(error);
+            onTtsStopped(mListener, error);
             mListeners.remove(parsedId.first);
         }
     }
diff --git a/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java b/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java
index f9745f4..dbbb5e4 100644
--- a/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java
+++ b/tests/robotests/src/com/android/car/messenger/tts/TTSHelperTest.java
@@ -156,5 +156,9 @@
             mStopped = true;
             mError = error;
         }
+        
+        @Override
+        public void onAudioFocusFailed() {
+        }
     }
-}
\ No newline at end of file
+}