From 9c4012b31b0c09cb14689bd96a71aae42c8a00cd Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczepaniak Date: Wed, 16 Jan 2013 17:24:44 +0000 Subject: [PATCH] Make TextToSpeech.shutdown() work before init callback. TextToSpeech.shutdown() never worked properly if was called before receiving onServiceConnected in connection object. Also, due to recent changes, TextToSpeech.shutdown() did not work until async task created in onServiceConnected returned its result to the main thread. This change makes .shutdown() work in all those cases. To allow that runAction can now execute code with connection that's not fully setuped - so we can shutt it down. Also, newly created connection is now hold in new member variable mConnectingServiceConnection, so it can be closed before receiving onServiceConnected callback. Also, I changed name of OnServiceConnectedAsyncTask to SetupConnectionAsyncTask, I find it more descriptive. Bug: 8003757 Change-Id: I41d84cfdb8fa28fe44235fb4a9764fa8f3d0643c --- .../java/android/speech/tts/TextToSpeech.java | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 30a862609d066..e2dc5d5ded81a 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -479,6 +479,7 @@ public class TextToSpeech { } private final Context mContext; + private Connection mConnectingServiceConnection; private Connection mServiceConnection; private OnInitListener mInitListener; // Written from an unspecified application thread, read from @@ -554,21 +555,24 @@ public class TextToSpeech { initTts(); } - private R runActionNoReconnect(Action action, R errorResult, String method) { - return runAction(action, errorResult, method, false); + private R runActionNoReconnect(Action action, R errorResult, String method, + boolean onlyEstablishedConnection) { + return runAction(action, errorResult, method, false, onlyEstablishedConnection); } private R runAction(Action action, R errorResult, String method) { - return runAction(action, errorResult, method, true); + return runAction(action, errorResult, method, true, true); } - private R runAction(Action action, R errorResult, String method, boolean reconnect) { + private R runAction(Action action, R errorResult, String method, + boolean reconnect, boolean onlyEstablishedConnection) { synchronized (mStartLock) { if (mServiceConnection == null) { Log.w(TAG, method + " failed: not bound to TTS engine"); return errorResult; } - return mServiceConnection.runAction(action, errorResult, method, reconnect); + return mServiceConnection.runAction(action, errorResult, method, reconnect, + onlyEstablishedConnection); } } @@ -631,6 +635,7 @@ public class TextToSpeech { return false; } else { Log.i(TAG, "Sucessfully bound to " + engine); + mConnectingServiceConnection = connection; return true; } } @@ -654,6 +659,16 @@ public class TextToSpeech { * so the TextToSpeech engine can be cleanly stopped. */ public void shutdown() { + // Special case, we are asked to shutdown connection that did finalize its connection. + synchronized (mStartLock) { + if (mConnectingServiceConnection != null) { + mContext.unbindService(mConnectingServiceConnection); + mConnectingServiceConnection = null; + return; + } + } + + // Post connection case runActionNoReconnect(new Action() { @Override public Void run(ITextToSpeechService service) throws RemoteException { @@ -671,7 +686,7 @@ public class TextToSpeech { mCurrentEngine = null; return null; } - }, null, "shutdown"); + }, null, "shutdown", false); } /** @@ -1310,7 +1325,9 @@ public class TextToSpeech { private class Connection implements ServiceConnection { private ITextToSpeechService mService; - private OnServiceConnectedAsyncTask mOnServiceConnectedAsyncTask; + private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask; + + private boolean mEstablished; private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() { @Override @@ -1338,13 +1355,11 @@ public class TextToSpeech { } }; - private class OnServiceConnectedAsyncTask extends AsyncTask { + private class SetupConnectionAsyncTask extends AsyncTask { private final ComponentName mName; - private final ITextToSpeechService mConnectedService; - public OnServiceConnectedAsyncTask(ComponentName name, IBinder service) { + public SetupConnectionAsyncTask(ComponentName name) { mName = name; - mConnectedService = ITextToSpeechService.Stub.asInterface(service); } @Override @@ -1355,8 +1370,8 @@ public class TextToSpeech { } try { - mConnectedService.setCallback(getCallerIdentity(), mCallback); - String[] defaultLanguage = mConnectedService.getClientDefaultLanguage(); + mService.setCallback(getCallerIdentity(), mCallback); + String[] defaultLanguage = mService.getClientDefaultLanguage(); mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]); mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]); @@ -1374,13 +1389,10 @@ public class TextToSpeech { @Override protected void onPostExecute(Integer result) { synchronized(mStartLock) { - if (mOnServiceConnectedAsyncTask == this) { - mOnServiceConnectedAsyncTask = null; + if (mOnSetupConnectionAsyncTask == this) { + mOnSetupConnectionAsyncTask = null; } - - mServiceConnection = Connection.this; - mService = mConnectedService; - + mEstablished = true; dispatchOnInit(result); } } @@ -1389,14 +1401,20 @@ public class TextToSpeech { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mStartLock) { + mConnectingServiceConnection = null; + Log.i(TAG, "Connected to " + name); - if (mOnServiceConnectedAsyncTask != null) { - mOnServiceConnectedAsyncTask.cancel(false); + if (mOnSetupConnectionAsyncTask != null) { + mOnSetupConnectionAsyncTask.cancel(false); } - mOnServiceConnectedAsyncTask = new OnServiceConnectedAsyncTask(name, service); - mOnServiceConnectedAsyncTask.execute(); + mService = ITextToSpeechService.Stub.asInterface(service); + mServiceConnection = Connection.this; + + mEstablished = false; + mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name); + mOnSetupConnectionAsyncTask.execute(); } } @@ -1407,14 +1425,14 @@ public class TextToSpeech { /** * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set. * - * @return true if we cancel mOnServiceConnectedAsyncTask in progress. + * @return true if we cancel mOnSetupConnectionAsyncTask in progress. */ private boolean clearServiceConnection() { synchronized(mStartLock) { boolean result = false; - if (mOnServiceConnectedAsyncTask != null) { - result = mOnServiceConnectedAsyncTask.cancel(false); - mOnServiceConnectedAsyncTask = null; + if (mOnSetupConnectionAsyncTask != null) { + result = mOnSetupConnectionAsyncTask.cancel(false); + mOnSetupConnectionAsyncTask = null; } mService = null; @@ -1445,13 +1463,22 @@ public class TextToSpeech { clearServiceConnection(); } - public R runAction(Action action, R errorResult, String method, boolean reconnect) { + public boolean isEstablished() { + return mService != null && mEstablished; + } + + public R runAction(Action action, R errorResult, String method, + boolean reconnect, boolean onlyEstablishedConnection) { synchronized (mStartLock) { try { if (mService == null) { Log.w(TAG, method + " failed: not connected to TTS engine"); return errorResult; } + if (onlyEstablishedConnection && isEstablished()) { + Log.w(TAG, method + " failed: TTS engine connection not fully setuped"); + return errorResult; + } return action.run(mService); } catch (RemoteException ex) { Log.e(TAG, method + " failed", ex);