Merge "NetInitiatedActivity: support AUTO response feature for SUPL IOT"
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index dea708a..96864c4 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -17,6 +17,7 @@
import android.media.AudioFormat;
import android.media.AudioTrack;
+import android.text.TextUtils;
import android.util.Log;
import java.util.Iterator;
@@ -25,6 +26,7 @@
class AudioPlaybackHandler {
private static final String TAG = "TTS.AudioPlaybackHandler";
+ private static final boolean DBG_THREADING = false;
private static final boolean DBG = false;
private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
@@ -64,70 +66,105 @@
* Stops all synthesis for a given {@code token}. If the current token
* is currently being processed, an effort will be made to stop it but
* that is not guaranteed.
+ *
+ * NOTE: This assumes that all other messages in the queue with {@code token}
+ * have been removed already.
+ *
+ * NOTE: Must be called synchronized on {@code AudioPlaybackHandler.this}.
*/
- synchronized public void stop(MessageParams token) {
+ private void stop(MessageParams token) {
if (token == null) {
return;
}
- removeMessages(token);
+ if (DBG) Log.d(TAG, "Stopping token : " + token);
if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
AudioTrack current = ((SynthesisMessageParams) token).getAudioTrack();
if (current != null) {
// Stop the current audio track if it's still playing.
- // The audio track is thread safe in this regard.
+ // The audio track is thread safe in this regard. The current
+ // handleSynthesisDataAvailable call will return soon after this
+ // call.
current.stop();
}
+ // This is safe because PlaybackSynthesisCallback#stop would have
+ // been called before this method, and will no longer enqueue any
+ // audio for this token.
+ //
+ // (Even if it did, all it would result in is a warning message).
mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY));
- } else {
- final MessageParams current = getCurrentParams();
-
- if (current != null) {
- if (token.getType() == MessageParams.TYPE_AUDIO) {
- ((AudioMessageParams) current).getPlayer().stop();
- } else if (token.getType() == MessageParams.TYPE_SILENCE) {
- ((SilenceMessageParams) current).getConditionVariable().open();
- }
- }
+ } else if (token.getType() == MessageParams.TYPE_AUDIO) {
+ ((AudioMessageParams) token).getPlayer().stop();
+ // No cleanup required for audio messages.
+ } else if (token.getType() == MessageParams.TYPE_SILENCE) {
+ ((SilenceMessageParams) token).getConditionVariable().open();
+ // No cleanup required for silence messages.
}
}
+ // -----------------------------------------------------
+ // Methods that add and remove elements from the queue. These do not
+ // need to be synchronized strictly speaking, but they make the behaviour
+ // a lot more predictable. (though it would still be correct without
+ // synchronization).
+ // -----------------------------------------------------
+
synchronized public void removePlaybackItems(String callingApp) {
+ if (DBG_THREADING) Log.d(TAG, "Removing all callback items for : " + callingApp);
removeMessages(callingApp);
- stop(getCurrentParams());
+
+ final MessageParams current = getCurrentParams();
+ if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+ stop(current);
+ }
}
synchronized public void removeAllItems() {
+ if (DBG_THREADING) Log.d(TAG, "Removing all items");
removeAllMessages();
stop(getCurrentParams());
}
/**
+ * @return false iff the queue is empty and no queue item is currently
+ * being handled, true otherwise.
+ */
+ public boolean isSpeaking() {
+ return (mQueue.peek() != null) || (mCurrentParams != null);
+ }
+
+ /**
* Shut down the audio playback thread.
*/
synchronized public void quit() {
+ removeAllMessages();
stop(getCurrentParams());
mQueue.add(new ListEntry(SHUTDOWN, null, HIGH_PRIORITY));
}
- void enqueueSynthesisStart(SynthesisMessageParams token) {
+ synchronized void enqueueSynthesisStart(SynthesisMessageParams token) {
+ if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis start : " + token);
mQueue.add(new ListEntry(SYNTHESIS_START, token));
}
- void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
+ synchronized void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
+ if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis data available : " + token);
mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token));
}
- void enqueueSynthesisDone(SynthesisMessageParams token) {
+ synchronized void enqueueSynthesisDone(SynthesisMessageParams token) {
+ if (DBG_THREADING) Log.d(TAG, "Enqueuing synthesis done : " + token);
mQueue.add(new ListEntry(SYNTHESIS_DONE, token));
}
- void enqueueAudio(AudioMessageParams token) {
+ synchronized void enqueueAudio(AudioMessageParams token) {
+ if (DBG_THREADING) Log.d(TAG, "Enqueuing audio : " + token);
mQueue.add(new ListEntry(PLAY_AUDIO, token));
}
- void enqueueSilence(SilenceMessageParams token) {
+ synchronized void enqueueSilence(SilenceMessageParams token) {
+ if (DBG_THREADING) Log.d(TAG, "Enqueuing silence : " + token);
mQueue.add(new ListEntry(PLAY_SILENCE, token));
}
@@ -172,26 +209,6 @@
}
/*
- * Remove all messages from the queue that contain the supplied token.
- * Note that the Iterator is thread safe, and other methods can safely
- * continue adding to the queue at this point.
- */
- synchronized private void removeMessages(MessageParams token) {
- if (token == null) {
- return;
- }
-
- Iterator<ListEntry> it = mQueue.iterator();
-
- while (it.hasNext()) {
- final ListEntry current = it.next();
- if (current.mMessage == token) {
- it.remove();
- }
- }
- }
-
- /*
* Atomically clear the queue of all messages.
*/
synchronized private void removeAllMessages() {
@@ -255,6 +272,13 @@
}
private void setCurrentParams(MessageParams p) {
+ if (DBG_THREADING) {
+ if (p != null) {
+ Log.d(TAG, "Started handling :" + p);
+ } else {
+ Log.d(TAG, "End handling : " + mCurrentParams);
+ }
+ }
mCurrentParams = p;
}
@@ -345,7 +369,7 @@
private void handleSynthesisDataAvailable(MessageParams msg) {
final SynthesisMessageParams param = (SynthesisMessageParams) msg;
if (param.getAudioTrack() == null) {
- Log.w(TAG, "Error : null audio track in handleDataAvailable.");
+ Log.w(TAG, "Error : null audio track in handleDataAvailable : " + param);
return;
}
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
index 4c1b6d2..e7d6da3 100644
--- a/core/java/android/speech/tts/MessageParams.java
+++ b/core/java/android/speech/tts/MessageParams.java
@@ -38,5 +38,10 @@
return mCallingApp;
}
+ @Override
+ public String toString() {
+ return "MessageParams[" + hashCode() + "]";
+ }
+
abstract int getType();
}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
index 04bd745..7dbf1ac 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -90,12 +90,10 @@
Log.w(TAG, "stop() called twice");
return;
}
+
// mToken will be null if the engine encounters
// an error before it called start().
- if (mToken != null) {
- mAudioTrackHandler.stop(mToken);
- mToken = null;
- } else {
+ if (mToken == null) {
// In all other cases, mAudioTrackHandler.stop() will
// result in onComplete being called.
mLogger.onWriteData();
@@ -158,7 +156,7 @@
}
synchronized (mStateLock) {
- if (mToken == null) {
+ if (mToken == null || mStopped) {
return TextToSpeech.ERROR;
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 32ca226..5126e48 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -109,6 +109,11 @@
/**
* Broadcast Action: The TextToSpeech synthesizer has completed processing
* of all the text in the speech queue.
+ *
+ * Note that this notifies callers when the <b>engine</b> has finished has
+ * processing text data. Audio playback might not have completed (or even started)
+ * at this point. If you wish to be notified when this happens, see
+ * {@link OnUtteranceCompletedListener}.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
@@ -576,6 +581,14 @@
service.setCallback(getPackageName(), null);
service.stop(getPackageName());
mServiceConnection.disconnect();
+ // Context#unbindService does not result in a call to
+ // ServiceConnection#onServiceDisconnected. As a result, the
+ // service ends up being destroyed (if there are no other open
+ // connections to it) but the process lives on and the
+ // ServiceConnection continues to refer to the destroyed service.
+ //
+ // This leads to tons of log spam about SynthThread being dead.
+ mServiceConnection = null;
mCurrentEngine = null;
return null;
}
@@ -796,7 +809,10 @@
}
/**
- * Checks whether the TTS engine is busy speaking.
+ * Checks whether the TTS engine is busy speaking. Note that a speech item is
+ * considered complete once it's audio data has been sent to the audio mixer, or
+ * written to a file. There might be a finite lag between this point, and when
+ * the audio hardware completes playback.
*
* @return {@code true} if the TTS engine is speaking.
*/
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 010c155..1926c92 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -282,6 +282,8 @@
if (current != null) {
current.stop();
}
+
+ // The AudioPlaybackHandler will be destroyed by the caller.
}
/**
@@ -337,6 +339,8 @@
}
removeCallbacksAndMessages(callingApp);
+ // This stops writing data to the file / or publishing
+ // items to the audio playback handler.
SpeechItem current = setCurrentSpeechItem(null);
if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
current.stop();
@@ -628,9 +632,7 @@
@Override
protected void stopImpl() {
- if (mToken != null) {
- mAudioPlaybackHandler.stop(mToken);
- }
+ // Do nothing.
}
}
@@ -657,9 +659,7 @@
@Override
protected void stopImpl() {
- if (mToken != null) {
- mAudioPlaybackHandler.stop(mToken);
- }
+ // Do nothing.
}
}
@@ -719,7 +719,7 @@
}
public boolean isSpeaking() {
- return mSynthHandler.isSpeaking();
+ return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking();
}
public int stop(String callingApp) {
@@ -767,10 +767,6 @@
mCallbacks.setCallback(packageName, cb);
}
- private boolean isDefault(String lang, String country, String variant) {
- return Locale.getDefault().equals(new Locale(lang, country, variant));
- }
-
private String intern(String in) {
// The input parameter will be non null.
return in.intern();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 5a5330d..0251baf 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -58,8 +58,10 @@
}
NuPlayer::HTTPLiveSource::~HTTPLiveSource() {
- mLiveSession->disconnect();
- mLiveLooper->stop();
+ if (mLiveSession != NULL) {
+ mLiveSession->disconnect();
+ mLiveLooper->stop();
+ }
}
void NuPlayer::HTTPLiveSource::start() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 7cd8b6c..c6fca2c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -118,9 +118,15 @@
mPlayer->start();
if (mStartupSeekTimeUs >= 0) {
- mPlayer->seekToAsync(mStartupSeekTimeUs);
+ if (mStartupSeekTimeUs == 0) {
+ notifySeekComplete();
+ } else {
+ mPlayer->seekToAsync(mStartupSeekTimeUs);
+ }
+
mStartupSeekTimeUs = -1;
}
+
break;
}
case PLAYING:
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index ca61b3d..73b3d5b 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -494,6 +494,12 @@
bool firstTime = (mPlaylist == NULL);
+ if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) {
+ // If we switch bandwidths, do not pay any heed to whether
+ // playlists changed since the last time...
+ mPlaylist.clear();
+ }
+
bool unchanged;
sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged);
if (playlist == NULL) {