Skip to content

Commit 33b8ee5

Browse files
author
Dianne Hackborn
committed
Fix issue #5756204: Crespo IME briefly appears shortened when...
...rotating to landscape When doing spell checking in the same process as the spell checker, we need to make sure it is still done asynchronously. Putting this in I noticed quite a few threading issues in this code, so I also addressed those (which became very obviously a problem with the async stuff here now). Also tweaked the service side to run spell checking at background priority. Change-Id: I01bafe3bec6bceeca911d6bf2f61a486a2fd4c48
1 parent 19a06fe commit 33b8ee5

File tree

2 files changed

+124
-86
lines changed

2 files changed

+124
-86
lines changed

core/java/android/service/textservice/SpellCheckerService.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import android.content.Intent;
2525
import android.os.Bundle;
2626
import android.os.IBinder;
27+
import android.os.Process;
2728
import android.os.RemoteException;
2829
import android.util.Log;
2930
import android.view.textservice.SuggestionsInfo;
@@ -187,23 +188,39 @@ public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener
187188
@Override
188189
public void onGetSuggestionsMultiple(
189190
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
191+
int pri = Process.getThreadPriority(Process.myTid());
190192
try {
193+
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
191194
mListener.onGetSuggestions(
192195
mSession.onGetSuggestionsMultiple(
193196
textInfos, suggestionsLimit, sequentialWords));
194197
} catch (RemoteException e) {
198+
} finally {
199+
Process.setThreadPriority(pri);
195200
}
196201
}
197202

198203
@Override
199204
public void onCancel() {
200-
mSession.onCancel();
205+
int pri = Process.getThreadPriority(Process.myTid());
206+
try {
207+
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
208+
mSession.onCancel();
209+
} finally {
210+
Process.setThreadPriority(pri);
211+
}
201212
}
202213

203214
@Override
204215
public void onClose() {
205-
mSession.onClose();
206-
mListener = null;
216+
int pri = Process.getThreadPriority(Process.myTid());
217+
try {
218+
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
219+
mSession.onClose();
220+
} finally {
221+
Process.setThreadPriority(pri);
222+
mListener = null;
223+
}
207224
}
208225

209226
public String getLocale() {

core/java/android/view/textservice/SpellCheckerSession.java

Lines changed: 104 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
import com.android.internal.textservice.ITextServicesManager;
2222
import com.android.internal.textservice.ITextServicesSessionListener;
2323

24+
import android.os.Binder;
2425
import android.os.Handler;
26+
import android.os.HandlerThread;
2527
import android.os.Message;
28+
import android.os.Process;
2629
import android.os.RemoteException;
2730
import android.util.Log;
2831
import 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

Comments
 (0)