From 8f3957cb91a9e1465fa11aaf4d4286d4c5a59ba7 Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczepaniak Date: Wed, 7 Nov 2012 14:03:53 +0000 Subject: TTS onServiceConnection moved to async task ITextToSpeechService.setCallback (service rpc call) is no longer executed on UI thread. I kept OnInitListener.onInit being called on the UI thread. It's not specified explicitly, but I don't want to introduce subtle bugs. +bonus import fix. Bug: 6540404 Change-Id: I0136e7efeb374b605ed29ee8b3f550ec2bd2c356 --- core/java/android/speech/tts/TextToSpeech.java | 106 ++++++++++++++++----- .../android/speech/tts/TextToSpeechService.java | 1 - 2 files changed, 81 insertions(+), 26 deletions(-) (limited to 'core/java/android/speech/tts') diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 9d570fc..f4c401b 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.ServiceConnection; import android.media.AudioManager; import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -1276,9 +1277,11 @@ public class TextToSpeech { return mEnginesHelper.getEngines(); } - private class Connection implements ServiceConnection { private ITextToSpeechService mService; + + private OnServiceConnectedAsyncTask mOnServiceConnectedAsyncTask; + private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { @Override public void onDone(String utteranceId) { @@ -1305,23 +1308,59 @@ public class TextToSpeech { } }; + private class OnServiceConnectedAsyncTask extends AsyncTask { + private final ComponentName mName; + private final ITextToSpeechService mConnectedService; + + public OnServiceConnectedAsyncTask(ComponentName name, IBinder service) { + mName = name; + mConnectedService = ITextToSpeechService.Stub.asInterface(service); + } + + @Override + protected Integer doInBackground(Void... params) { + synchronized(mStartLock) { + if (isCancelled()) { + return null; + } + + try { + mConnectedService.setCallback(getCallerIdentity(), mCallback); + Log.i(TAG, "Setuped connection to " + mName); + return SUCCESS; + } catch (RemoteException re) { + Log.e(TAG, "Error connecting to service, setCallback() failed"); + return ERROR; + } + } + } + + @Override + protected void onPostExecute(Integer result) { + synchronized(mStartLock) { + if (mOnServiceConnectedAsyncTask == this) { + mOnServiceConnectedAsyncTask = null; + } + + mServiceConnection = Connection.this; + mService = mConnectedService; + + dispatchOnInit(result); + } + } + } + @Override public void onServiceConnected(ComponentName name, IBinder service) { - Log.i(TAG, "Connected to " + name); synchronized(mStartLock) { - if (mServiceConnection != null) { - // Disconnect any previous service connection - mServiceConnection.disconnect(); - } - mServiceConnection = this; - mService = ITextToSpeechService.Stub.asInterface(service); - try { - mService.setCallback(getCallerIdentity(), mCallback); - dispatchOnInit(SUCCESS); - } catch (RemoteException re) { - Log.e(TAG, "Error connecting to service, setCallback() failed"); - dispatchOnInit(ERROR); + Log.i(TAG, "Connected to " + name); + + if (mOnServiceConnectedAsyncTask != null) { + mOnServiceConnectedAsyncTask.cancel(false); } + + mOnServiceConnectedAsyncTask = new OnServiceConnectedAsyncTask(name, service); + mOnServiceConnectedAsyncTask.execute(); } } @@ -1329,28 +1368,45 @@ public class TextToSpeech { return mCallback; } - @Override - public void onServiceDisconnected(ComponentName name) { + /** + * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set. + * + * @return true if we cancel mOnServiceConnectedAsyncTask in progress. + */ + private boolean clearServiceConnection() { synchronized(mStartLock) { + boolean result = false; + if (mOnServiceConnectedAsyncTask != null) { + result = mOnServiceConnectedAsyncTask.cancel(false); + mOnServiceConnectedAsyncTask = null; + } + mService = null; // If this is the active connection, clear it if (mServiceConnection == this) { mServiceConnection = null; } + return result; + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.i(TAG, "Asked to disconnect from " + name); + if (clearServiceConnection()) { + /* We need to protect against a rare case where engine + * dies just after successful connection - and we process onServiceDisconnected + * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels + * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit + * with ERROR as argument. + */ + dispatchOnInit(ERROR); } } public void disconnect() { mContext.unbindService(this); - - synchronized (mStartLock) { - mService = null; - // If this is the active connection, clear it - if (mServiceConnection == this) { - mServiceConnection = null; - } - - } + clearServiceConnection(); } public R runAction(Action action, R errorResult, String method, boolean reconnect) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index d124e68..3ebe029 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -34,7 +34,6 @@ import android.text.TextUtils; import android.util.Log; import java.io.File; -import java.io.IOException; import java.util.HashMap; import java.util.Locale; import java.util.Set; -- cgit v1.1