2121import com .android .internal .textservice .ITextServicesManager ;
2222import com .android .internal .textservice .ITextServicesSessionListener ;
2323
24+ import android .os .Binder ;
2425import android .os .Handler ;
26+ import android .os .HandlerThread ;
2527import android .os .Message ;
28+ import android .os .Process ;
2629import android .os .RemoteException ;
2730import android .util .Log ;
2831import android .view .textservice .SpellCheckerInfo ;
@@ -205,6 +208,8 @@ private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessio
205208
206209 private boolean mOpened ;
207210 private ISpellCheckerSession mISpellCheckerSession ;
211+ private HandlerThread mThread ;
212+ private Handler mAsyncHandler ;
208213
209214 public SpellCheckerSessionListenerImpl (Handler handler ) {
210215 mOpened = false ;
@@ -216,6 +221,7 @@ private static class SpellCheckerParams {
216221 public final TextInfo [] mTextInfos ;
217222 public final int mSuggestionsLimit ;
218223 public final boolean mSequentialWords ;
224+ public ISpellCheckerSession mSession ;
219225 public SpellCheckerParams (int what , TextInfo [] textInfos , int suggestionsLimit ,
220226 boolean sequentialWords ) {
221227 mWhat = what ;
@@ -225,27 +231,86 @@ public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
225231 }
226232 }
227233
228- private void processTask (SpellCheckerParams scp ) {
229- switch (scp .mWhat ) {
230- case TASK_CANCEL :
231- processCancel ();
232- break ;
233- case TASK_GET_SUGGESTIONS_MULTIPLE :
234- processGetSuggestionsMultiple (scp );
235- break ;
236- case TASK_CLOSE :
237- processClose ();
238- break ;
234+ private void processTask (ISpellCheckerSession session , SpellCheckerParams scp ,
235+ boolean async ) {
236+ if (async || mAsyncHandler == null ) {
237+ switch (scp .mWhat ) {
238+ case TASK_CANCEL :
239+ if (DBG ) {
240+ Log .w (TAG , "Cancel spell checker tasks." );
241+ }
242+ try {
243+ session .onCancel ();
244+ } catch (RemoteException e ) {
245+ Log .e (TAG , "Failed to cancel " + e );
246+ }
247+ break ;
248+ case TASK_GET_SUGGESTIONS_MULTIPLE :
249+ if (DBG ) {
250+ Log .w (TAG , "Get suggestions from the spell checker." );
251+ }
252+ try {
253+ session .onGetSuggestionsMultiple (scp .mTextInfos ,
254+ scp .mSuggestionsLimit , scp .mSequentialWords );
255+ } catch (RemoteException e ) {
256+ Log .e (TAG , "Failed to get suggestions " + e );
257+ }
258+ break ;
259+ case TASK_CLOSE :
260+ if (DBG ) {
261+ Log .w (TAG , "Close spell checker tasks." );
262+ }
263+ try {
264+ session .onClose ();
265+ } catch (RemoteException e ) {
266+ Log .e (TAG , "Failed to close " + e );
267+ }
268+ break ;
269+ }
270+ } else {
271+ // The interface is to a local object, so need to execute it
272+ // asynchronously.
273+ scp .mSession = session ;
274+ mAsyncHandler .sendMessage (Message .obtain (mAsyncHandler , 1 , scp ));
275+ }
276+
277+ if (scp .mWhat == TASK_CLOSE ) {
278+ // If we are closing, we want to clean up our state now even
279+ // if it is pending as an async operation.
280+ synchronized (this ) {
281+ mISpellCheckerSession = null ;
282+ mHandler = null ;
283+ if (mThread != null ) {
284+ mThread .quit ();
285+ }
286+ mThread = null ;
287+ mAsyncHandler = null ;
288+ }
239289 }
240290 }
241291
242292 public synchronized void onServiceConnected (ISpellCheckerSession session ) {
243- mISpellCheckerSession = session ;
244- mOpened = true ;
293+ synchronized (this ) {
294+ mISpellCheckerSession = session ;
295+ if (session .asBinder () instanceof Binder && mThread == null ) {
296+ // If this is a local object, we need to do our own threading
297+ // to make sure we handle it asynchronously.
298+ mThread = new HandlerThread ("SpellCheckerSession" ,
299+ Process .THREAD_PRIORITY_BACKGROUND );
300+ mThread .start ();
301+ mAsyncHandler = new Handler (mThread .getLooper ()) {
302+ @ Override public void handleMessage (Message msg ) {
303+ SpellCheckerParams scp = (SpellCheckerParams )msg .obj ;
304+ processTask (scp .mSession , scp , true );
305+ }
306+ };
307+ }
308+ mOpened = true ;
309+ }
245310 if (DBG )
246311 Log .d (TAG , "onServiceConnected - Success" );
247312 while (!mPendingTasks .isEmpty ()) {
248- processTask (mPendingTasks .poll ());
313+ processTask (session , mPendingTasks .poll (), false );
249314 }
250315 }
251316
@@ -277,87 +342,43 @@ public boolean isDisconnected() {
277342 return mOpened && mISpellCheckerSession == null ;
278343 }
279344
280- public boolean checkOpenConnection () {
281- if (mISpellCheckerSession != null ) {
282- return true ;
283- }
284- Log .e (TAG , "not connected to the spellchecker service." );
285- return false ;
286- }
287-
288345 private void processOrEnqueueTask (SpellCheckerParams scp ) {
289346 if (DBG ) {
290347 Log .d (TAG , "process or enqueue task: " + mISpellCheckerSession );
291348 }
292- SpellCheckerParams closeTask = null ;
293- if (mISpellCheckerSession == null ) {
294- if (scp .mWhat == TASK_CANCEL ) {
295- while (!mPendingTasks .isEmpty ()) {
296- final SpellCheckerParams tmp = mPendingTasks .poll ();
297- if (tmp .mWhat == TASK_CLOSE ) {
298- // Only one close task should be processed, while we need to remove all
299- // close tasks from the queue
300- closeTask = tmp ;
349+ ISpellCheckerSession session ;
350+ synchronized (this ) {
351+ session = mISpellCheckerSession ;
352+ if (session == null ) {
353+ SpellCheckerParams closeTask = null ;
354+ if (scp .mWhat == TASK_CANCEL ) {
355+ while (!mPendingTasks .isEmpty ()) {
356+ final SpellCheckerParams tmp = mPendingTasks .poll ();
357+ if (tmp .mWhat == TASK_CLOSE ) {
358+ // Only one close task should be processed, while we need to remove all
359+ // close tasks from the queue
360+ closeTask = tmp ;
361+ }
301362 }
302363 }
364+ mPendingTasks .offer (scp );
365+ if (closeTask != null ) {
366+ mPendingTasks .offer (closeTask );
367+ }
368+ return ;
303369 }
304- mPendingTasks .offer (scp );
305- if (closeTask != null ) {
306- mPendingTasks .offer (closeTask );
307- }
308- } else {
309- processTask (scp );
310- }
311- }
312-
313- private void processCancel () {
314- if (!checkOpenConnection ()) {
315- return ;
316- }
317- if (DBG ) {
318- Log .w (TAG , "Cancel spell checker tasks." );
319- }
320- try {
321- mISpellCheckerSession .onCancel ();
322- } catch (RemoteException e ) {
323- Log .e (TAG , "Failed to cancel " + e );
324- }
325- }
326-
327- private void processClose () {
328- if (!checkOpenConnection ()) {
329- return ;
330- }
331- if (DBG ) {
332- Log .w (TAG , "Close spell checker tasks." );
333- }
334- try {
335- mISpellCheckerSession .onClose ();
336- mISpellCheckerSession = null ;
337- mHandler = null ;
338- } catch (RemoteException e ) {
339- Log .e (TAG , "Failed to close " + e );
340- }
341- }
342-
343- private void processGetSuggestionsMultiple (SpellCheckerParams scp ) {
344- if (!checkOpenConnection ()) {
345- return ;
346- }
347- if (DBG ) {
348- Log .w (TAG , "Get suggestions from the spell checker." );
349- }
350- try {
351- mISpellCheckerSession .onGetSuggestionsMultiple (
352- scp .mTextInfos , scp .mSuggestionsLimit , scp .mSequentialWords );
353- } catch (RemoteException e ) {
354- Log .e (TAG , "Failed to get suggestions " + e );
355370 }
371+ processTask (session , scp , false );
356372 }
357373
358374 @ Override
359375 public void onGetSuggestions (SuggestionsInfo [] results ) {
360- mHandler .sendMessage (Message .obtain (mHandler , MSG_ON_GET_SUGGESTION_MULTIPLE , results ));
376+ synchronized (this ) {
377+ if (mHandler != null ) {
378+ mHandler .sendMessage (Message .obtain (mHandler ,
379+ MSG_ON_GET_SUGGESTION_MULTIPLE , results ));
380+ }
381+ }
361382 }
362383 }
363384
0 commit comments