Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33b8ee50 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

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
parent 19a06fe9
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.textservice.SuggestionsInfo;
@@ -187,24 +188,40 @@ public abstract class SpellCheckerService extends Service {
        @Override
        public void onGetSuggestionsMultiple(
                TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
            int pri = Process.getThreadPriority(Process.myTid());
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                mListener.onGetSuggestions(
                        mSession.onGetSuggestionsMultiple(
                                textInfos, suggestionsLimit, sequentialWords));
            } catch (RemoteException e) {
            } finally {
                Process.setThreadPriority(pri);
            }
        }

        @Override
        public void onCancel() {
            int pri = Process.getThreadPriority(Process.myTid());
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                mSession.onCancel();
            } finally {
                Process.setThreadPriority(pri);
            }
        }

        @Override
        public void onClose() {
            int pri = Process.getThreadPriority(Process.myTid());
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                mSession.onClose();
            } finally {
                Process.setThreadPriority(pri);
                mListener = null;
            }
        }

        public String getLocale() {
            return mLocale;
+104 −83
Original line number Diff line number Diff line
@@ -21,8 +21,11 @@ import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesManager;
import com.android.internal.textservice.ITextServicesSessionListener;

import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.textservice.SpellCheckerInfo;
@@ -205,6 +208,8 @@ public class SpellCheckerSession {

        private boolean mOpened;
        private ISpellCheckerSession mISpellCheckerSession;
        private HandlerThread mThread;
        private Handler mAsyncHandler;

        public SpellCheckerSessionListenerImpl(Handler handler) {
            mOpened = false;
@@ -216,6 +221,7 @@ public class SpellCheckerSession {
            public final TextInfo[] mTextInfos;
            public final int mSuggestionsLimit;
            public final boolean mSequentialWords;
            public ISpellCheckerSession mSession;
            public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
                    boolean sequentialWords) {
                mWhat = what;
@@ -225,27 +231,86 @@ public class SpellCheckerSession {
            }
        }

        private void processTask(SpellCheckerParams scp) {
        private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
                boolean async) {
            if (async || mAsyncHandler == null) {
                switch (scp.mWhat) {
                    case TASK_CANCEL:
                    processCancel();
                        if (DBG) {
                            Log.w(TAG, "Cancel spell checker tasks.");
                        }
                        try {
                            session.onCancel();
                        } catch (RemoteException e) {
                            Log.e(TAG, "Failed to cancel " + e);
                        }
                        break;
                    case TASK_GET_SUGGESTIONS_MULTIPLE:
                    processGetSuggestionsMultiple(scp);
                        if (DBG) {
                            Log.w(TAG, "Get suggestions from the spell checker.");
                        }
                        try {
                            session.onGetSuggestionsMultiple(scp.mTextInfos,
                                    scp.mSuggestionsLimit, scp.mSequentialWords);
                        } catch (RemoteException e) {
                            Log.e(TAG, "Failed to get suggestions " + e);
                        }
                        break;
                    case TASK_CLOSE:
                    processClose();
                        if (DBG) {
                            Log.w(TAG, "Close spell checker tasks.");
                        }
                        try {
                            session.onClose();
                        } catch (RemoteException e) {
                            Log.e(TAG, "Failed to close " + e);
                        }
                        break;
                }
            } else {
                // The interface is to a local object, so need to execute it
                // asynchronously.
                scp.mSession = session;
                mAsyncHandler.sendMessage(Message.obtain(mAsyncHandler, 1, scp));
            }

            if (scp.mWhat == TASK_CLOSE) {
                // If we are closing, we want to clean up our state now even
                // if it is pending as an async operation.
                synchronized (this) {
                    mISpellCheckerSession = null;
                    mHandler = null;
                    if (mThread != null) {
                        mThread.quit();
                    }
                    mThread = null;
                    mAsyncHandler = null;
                }
            }
        }

        public synchronized void onServiceConnected(ISpellCheckerSession session) {
            synchronized (this) {
                mISpellCheckerSession = session;
                if (session.asBinder() instanceof Binder && mThread == null) {
                    // If this is a local object, we need to do our own threading
                    // to make sure we handle it asynchronously.
                    mThread = new HandlerThread("SpellCheckerSession",
                            Process.THREAD_PRIORITY_BACKGROUND);
                    mThread.start();
                    mAsyncHandler = new Handler(mThread.getLooper()) {
                        @Override public void handleMessage(Message msg) {
                            SpellCheckerParams scp = (SpellCheckerParams)msg.obj;
                            processTask(scp.mSession, scp, true);
                        }
                    };
                }
                mOpened = true;
            }
            if (DBG)
                Log.d(TAG, "onServiceConnected - Success");
            while (!mPendingTasks.isEmpty()) {
                processTask(mPendingTasks.poll());
                processTask(session, mPendingTasks.poll(), false);
            }
        }

@@ -277,20 +342,15 @@ public class SpellCheckerSession {
            return mOpened && mISpellCheckerSession == null;
        }

        public boolean checkOpenConnection() {
            if (mISpellCheckerSession != null) {
                return true;
            }
            Log.e(TAG, "not connected to the spellchecker service.");
            return false;
        }

        private void processOrEnqueueTask(SpellCheckerParams scp) {
            if (DBG) {
                Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
            }
            ISpellCheckerSession session;
            synchronized (this) {
                session = mISpellCheckerSession;
                if (session == null) {
                    SpellCheckerParams closeTask = null;
            if (mISpellCheckerSession == null) {
                    if (scp.mWhat == TASK_CANCEL) {
                        while (!mPendingTasks.isEmpty()) {
                            final SpellCheckerParams tmp = mPendingTasks.poll();
@@ -305,59 +365,20 @@ public class SpellCheckerSession {
                    if (closeTask != null) {
                        mPendingTasks.offer(closeTask);
                    }
            } else {
                processTask(scp);
            }
        }

        private void processCancel() {
            if (!checkOpenConnection()) {
                return;
            }
            if (DBG) {
                Log.w(TAG, "Cancel spell checker tasks.");
            }
            try {
                mISpellCheckerSession.onCancel();
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to cancel " + e);
            }
        }

        private void processClose() {
            if (!checkOpenConnection()) {
                return;
            }
            if (DBG) {
                Log.w(TAG, "Close spell checker tasks.");
            }
            try {
                mISpellCheckerSession.onClose();
                mISpellCheckerSession = null;
                mHandler = null;
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to close " + e);
            }
        }

        private void processGetSuggestionsMultiple(SpellCheckerParams scp) {
            if (!checkOpenConnection()) {
                    return;
                }
            if (DBG) {
                Log.w(TAG, "Get suggestions from the spell checker.");
            }
            try {
                mISpellCheckerSession.onGetSuggestionsMultiple(
                        scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to get suggestions " + e);
            }
            processTask(session, scp, false);
        }

        @Override
        public void onGetSuggestions(SuggestionsInfo[] results) {
            mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
            synchronized (this) {
                if (mHandler != null) {
                    mHandler.sendMessage(Message.obtain(mHandler,
                            MSG_ON_GET_SUGGESTION_MULTIPLE, results));
                }
            }
        }
    }