am cfcd232e: am e34f0984: Merge "Fix a reference leak in SpellCheckerSessionListenerImpl." into mnc-dev
* commit 'cfcd232e453cbb9a0ce2ac1f61250680afdb1b52': Fix a reference leak in SpellCheckerSessionListenerImpl.
This commit is contained in:
@@ -28,9 +28,6 @@ import android.os.Message;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.textservice.SpellCheckerInfo;
|
|
||||||
import android.view.textservice.SuggestionsInfo;
|
|
||||||
import android.view.textservice.TextInfo;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
@@ -226,17 +223,44 @@ public class SpellCheckerSession {
|
|||||||
private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
|
private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
|
||||||
private static final int TASK_CLOSE = 3;
|
private static final int TASK_CLOSE = 3;
|
||||||
private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
|
private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
|
||||||
private final Queue<SpellCheckerParams> mPendingTasks =
|
private static String taskToString(int task) {
|
||||||
new LinkedList<SpellCheckerParams>();
|
switch (task) {
|
||||||
|
case TASK_CANCEL:
|
||||||
|
return "STATE_WAIT_CONNECTION";
|
||||||
|
case TASK_GET_SUGGESTIONS_MULTIPLE:
|
||||||
|
return "TASK_GET_SUGGESTIONS_MULTIPLE";
|
||||||
|
case TASK_CLOSE:
|
||||||
|
return "TASK_CLOSE";
|
||||||
|
case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
|
||||||
|
return "TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE";
|
||||||
|
default:
|
||||||
|
return "Unexpected task=" + task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>();
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
|
|
||||||
private boolean mOpened;
|
private static final int STATE_WAIT_CONNECTION = 0;
|
||||||
|
private static final int STATE_CONNECTED = 1;
|
||||||
|
private static final int STATE_CLOSED_AFTER_CONNECTION = 2;
|
||||||
|
private static final int STATE_CLOSED_BEFORE_CONNECTION = 3;
|
||||||
|
private static String stateToString(int state) {
|
||||||
|
switch (state) {
|
||||||
|
case STATE_WAIT_CONNECTION: return "STATE_WAIT_CONNECTION";
|
||||||
|
case STATE_CONNECTED: return "STATE_CONNECTED";
|
||||||
|
case STATE_CLOSED_AFTER_CONNECTION: return "STATE_CLOSED_AFTER_CONNECTION";
|
||||||
|
case STATE_CLOSED_BEFORE_CONNECTION: return "STATE_CLOSED_BEFORE_CONNECTION";
|
||||||
|
default: return "Unexpected state=" + state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private int mState = STATE_WAIT_CONNECTION;
|
||||||
|
|
||||||
private ISpellCheckerSession mISpellCheckerSession;
|
private ISpellCheckerSession mISpellCheckerSession;
|
||||||
private HandlerThread mThread;
|
private HandlerThread mThread;
|
||||||
private Handler mAsyncHandler;
|
private Handler mAsyncHandler;
|
||||||
|
|
||||||
public SpellCheckerSessionListenerImpl(Handler handler) {
|
public SpellCheckerSessionListenerImpl(Handler handler) {
|
||||||
mOpened = false;
|
|
||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,12 +281,18 @@ public class SpellCheckerSession {
|
|||||||
|
|
||||||
private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
|
private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
|
||||||
boolean async) {
|
boolean async) {
|
||||||
|
if (DBG) {
|
||||||
|
synchronized (this) {
|
||||||
|
Log.d(TAG, "entering processTask:"
|
||||||
|
+ " session.hashCode()=#" + Integer.toHexString(session.hashCode())
|
||||||
|
+ " scp.mWhat=" + taskToString(scp.mWhat) + " async=" + async
|
||||||
|
+ " mAsyncHandler=" + mAsyncHandler
|
||||||
|
+ " mState=" + stateToString(mState));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (async || mAsyncHandler == null) {
|
if (async || mAsyncHandler == null) {
|
||||||
switch (scp.mWhat) {
|
switch (scp.mWhat) {
|
||||||
case TASK_CANCEL:
|
case TASK_CANCEL:
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "Cancel spell checker tasks.");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
session.onCancel();
|
session.onCancel();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -270,9 +300,6 @@ public class SpellCheckerSession {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TASK_GET_SUGGESTIONS_MULTIPLE:
|
case TASK_GET_SUGGESTIONS_MULTIPLE:
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "Get suggestions from the spell checker.");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
session.onGetSuggestionsMultiple(scp.mTextInfos,
|
session.onGetSuggestionsMultiple(scp.mTextInfos,
|
||||||
scp.mSuggestionsLimit, scp.mSequentialWords);
|
scp.mSuggestionsLimit, scp.mSequentialWords);
|
||||||
@@ -281,9 +308,6 @@ public class SpellCheckerSession {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
|
case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "Get sentence suggestions from the spell checker.");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
session.onGetSentenceSuggestionsMultiple(
|
session.onGetSentenceSuggestionsMultiple(
|
||||||
scp.mTextInfos, scp.mSuggestionsLimit);
|
scp.mTextInfos, scp.mSuggestionsLimit);
|
||||||
@@ -292,9 +316,6 @@ public class SpellCheckerSession {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TASK_CLOSE:
|
case TASK_CLOSE:
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "Close spell checker tasks.");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
session.onClose();
|
session.onClose();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
@@ -313,21 +334,62 @@ public class SpellCheckerSession {
|
|||||||
// If we are closing, we want to clean up our state now even
|
// If we are closing, we want to clean up our state now even
|
||||||
// if it is pending as an async operation.
|
// if it is pending as an async operation.
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
mISpellCheckerSession = null;
|
processCloseLocked();
|
||||||
mHandler = null;
|
|
||||||
if (mThread != null) {
|
|
||||||
mThread.quit();
|
|
||||||
}
|
|
||||||
mThread = null;
|
|
||||||
mAsyncHandler = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processCloseLocked() {
|
||||||
|
if (DBG) Log.d(TAG, "entering processCloseLocked:"
|
||||||
|
+ " session" + (mISpellCheckerSession != null ? ".hashCode()=#"
|
||||||
|
+ Integer.toHexString(mISpellCheckerSession.hashCode()) : "=null")
|
||||||
|
+ " mState=" + stateToString(mState));
|
||||||
|
mISpellCheckerSession = null;
|
||||||
|
if (mThread != null) {
|
||||||
|
mThread.quit();
|
||||||
|
}
|
||||||
|
mHandler = null;
|
||||||
|
mPendingTasks.clear();
|
||||||
|
mThread = null;
|
||||||
|
mAsyncHandler = null;
|
||||||
|
switch (mState) {
|
||||||
|
case STATE_WAIT_CONNECTION:
|
||||||
|
mState = STATE_CLOSED_BEFORE_CONNECTION;
|
||||||
|
break;
|
||||||
|
case STATE_CONNECTED:
|
||||||
|
mState = STATE_CLOSED_AFTER_CONNECTION;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "processCloseLocked is called unexpectedly. mState=" +
|
||||||
|
stateToString(mState));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void onServiceConnected(ISpellCheckerSession session) {
|
public synchronized void onServiceConnected(ISpellCheckerSession session) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
switch (mState) {
|
||||||
|
case STATE_WAIT_CONNECTION:
|
||||||
|
// OK, go ahead.
|
||||||
|
break;
|
||||||
|
case STATE_CLOSED_BEFORE_CONNECTION:
|
||||||
|
// This is possible, and not an error. The client no longer is interested
|
||||||
|
// in this connection. OK to ignore.
|
||||||
|
if (DBG) Log.i(TAG, "ignoring onServiceConnected since the session is"
|
||||||
|
+ " already closed.");
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "ignoring onServiceConnected due to unexpected mState="
|
||||||
|
+ stateToString(mState));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (session == null) {
|
||||||
|
Log.e(TAG, "ignoring onServiceConnected due to session=null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
mISpellCheckerSession = session;
|
mISpellCheckerSession = session;
|
||||||
if (session.asBinder() instanceof Binder && mThread == null) {
|
if (session.asBinder() instanceof Binder && mThread == null) {
|
||||||
|
if (DBG) Log.d(TAG, "starting HandlerThread in onServiceConnected.");
|
||||||
// If this is a local object, we need to do our own threading
|
// If this is a local object, we need to do our own threading
|
||||||
// to make sure we handle it asynchronously.
|
// to make sure we handle it asynchronously.
|
||||||
mThread = new HandlerThread("SpellCheckerSession",
|
mThread = new HandlerThread("SpellCheckerSession",
|
||||||
@@ -340,62 +402,65 @@ public class SpellCheckerSession {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
mOpened = true;
|
mState = STATE_CONNECTED;
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "processed onServiceConnected: mISpellCheckerSession.hashCode()=#"
|
||||||
|
+ Integer.toHexString(mISpellCheckerSession.hashCode())
|
||||||
|
+ " mPendingTasks.size()=" + mPendingTasks.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (DBG)
|
|
||||||
Log.d(TAG, "onServiceConnected - Success");
|
|
||||||
while (!mPendingTasks.isEmpty()) {
|
while (!mPendingTasks.isEmpty()) {
|
||||||
processTask(session, mPendingTasks.poll(), false);
|
processTask(session, mPendingTasks.poll(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "cancel");
|
|
||||||
}
|
|
||||||
processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false));
|
processOrEnqueueTask(new SpellCheckerParams(TASK_CANCEL, null, 0, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getSuggestionsMultiple(
|
public void getSuggestionsMultiple(
|
||||||
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
|
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "getSuggestionsMultiple");
|
|
||||||
}
|
|
||||||
processOrEnqueueTask(
|
processOrEnqueueTask(
|
||||||
new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos,
|
new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE, textInfos,
|
||||||
suggestionsLimit, sequentialWords));
|
suggestionsLimit, sequentialWords));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
|
public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) {
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "getSentenceSuggestionsMultiple");
|
|
||||||
}
|
|
||||||
processOrEnqueueTask(
|
processOrEnqueueTask(
|
||||||
new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
|
new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
|
||||||
textInfos, suggestionsLimit, false));
|
textInfos, suggestionsLimit, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "close");
|
|
||||||
}
|
|
||||||
processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
|
processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisconnected() {
|
public boolean isDisconnected() {
|
||||||
return mOpened && mISpellCheckerSession == null;
|
synchronized (this) {
|
||||||
|
return mState != STATE_CONNECTED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processOrEnqueueTask(SpellCheckerParams scp) {
|
private void processOrEnqueueTask(SpellCheckerParams scp) {
|
||||||
if (DBG) {
|
|
||||||
Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
|
|
||||||
}
|
|
||||||
ISpellCheckerSession session;
|
ISpellCheckerSession session;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
session = mISpellCheckerSession;
|
if (mState != STATE_WAIT_CONNECTION && mState != STATE_CONNECTED) {
|
||||||
if (session == null) {
|
Log.e(TAG, "ignoring processOrEnqueueTask due to unexpected mState="
|
||||||
|
+ taskToString(scp.mWhat)
|
||||||
|
+ " scp.mWhat=" + taskToString(scp.mWhat));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState == STATE_WAIT_CONNECTION) {
|
||||||
|
// If we are still waiting for the connection. Need to pay special attention.
|
||||||
|
if (scp.mWhat == TASK_CLOSE) {
|
||||||
|
processCloseLocked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Enqueue the task to task queue.
|
||||||
SpellCheckerParams closeTask = null;
|
SpellCheckerParams closeTask = null;
|
||||||
if (scp.mWhat == TASK_CANCEL) {
|
if (scp.mWhat == TASK_CANCEL) {
|
||||||
|
if (DBG) Log.d(TAG, "canceling pending tasks in processOrEnqueueTask.");
|
||||||
while (!mPendingTasks.isEmpty()) {
|
while (!mPendingTasks.isEmpty()) {
|
||||||
final SpellCheckerParams tmp = mPendingTasks.poll();
|
final SpellCheckerParams tmp = mPendingTasks.poll();
|
||||||
if (tmp.mWhat == TASK_CLOSE) {
|
if (tmp.mWhat == TASK_CLOSE) {
|
||||||
@@ -409,9 +474,15 @@ public class SpellCheckerSession {
|
|||||||
if (closeTask != null) {
|
if (closeTask != null) {
|
||||||
mPendingTasks.offer(closeTask);
|
mPendingTasks.offer(closeTask);
|
||||||
}
|
}
|
||||||
|
if (DBG) Log.d(TAG, "queueing tasks in processOrEnqueueTask since the"
|
||||||
|
+ " connection is not established."
|
||||||
|
+ " mPendingTasks.size()=" + mPendingTasks.size());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session = mISpellCheckerSession;
|
||||||
}
|
}
|
||||||
|
// session must never be null here.
|
||||||
processTask(session, scp, false);
|
processTask(session, scp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,9 +538,6 @@ public class SpellCheckerSession {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ISpellCheckerSession session) {
|
public void onServiceConnected(ISpellCheckerSession session) {
|
||||||
if (DBG) {
|
|
||||||
Log.w(TAG, "SpellCheckerSession connected.");
|
|
||||||
}
|
|
||||||
mParentSpellCheckerSessionListenerImpl.onServiceConnected(session);
|
mParentSpellCheckerSessionListenerImpl.onServiceConnected(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user