am c89704a7: merge from open-source master

Merge commit 'c89704a77f9c51ebd8c1cc6863e15ff402a56c86' into kraken

* commit 'c89704a77f9c51ebd8c1cc6863e15ff402a56c86':
  Fix Memory Leak When Switching Input Methods
This commit is contained in:
The Android Open Source Project
2010-03-29 11:03:33 -07:00
committed by Android Git Automerger
4 changed files with 62 additions and 21 deletions

View File

@@ -47,9 +47,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
private static final int DO_UPDATE_CURSOR = 95; private static final int DO_UPDATE_CURSOR = 95;
private static final int DO_APP_PRIVATE_COMMAND = 100; private static final int DO_APP_PRIVATE_COMMAND = 100;
private static final int DO_TOGGLE_SOFT_INPUT = 105; private static final int DO_TOGGLE_SOFT_INPUT = 105;
private static final int DO_FINISH_SESSION = 110;
final HandlerCaller mCaller; HandlerCaller mCaller;
final InputMethodSession mInputMethodSession; InputMethodSession mInputMethodSession;
// NOTE: we should have a cache of these. // NOTE: we should have a cache of these.
static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback { static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
@@ -127,6 +128,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2); mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
return; return;
} }
case DO_FINISH_SESSION: {
mInputMethodSession = null;
return;
}
} }
Log.w(TAG, "Unhandled message code: " + msg.what); Log.w(TAG, "Unhandled message code: " + msg.what);
} }
@@ -174,4 +179,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
public void toggleSoftInput(int showFlags, int hideFlags) { public void toggleSoftInput(int showFlags, int hideFlags) {
mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags)); mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
} }
public void finishSession() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
}
} }

View File

@@ -39,6 +39,7 @@ import android.view.inputmethod.InputMethodSession;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -64,9 +65,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_SHOW_SOFT_INPUT = 60; private static final int DO_SHOW_SOFT_INPUT = 60;
private static final int DO_HIDE_SOFT_INPUT = 70; private static final int DO_HIDE_SOFT_INPUT = 70;
final AbstractInputMethodService mTarget; final WeakReference<AbstractInputMethodService> mTarget;
final HandlerCaller mCaller; final HandlerCaller mCaller;
final InputMethod mInputMethod; final WeakReference<InputMethod> mInputMethod;
static class Notifier { static class Notifier {
boolean notified; boolean notified;
@@ -96,21 +97,32 @@ class IInputMethodWrapper extends IInputMethod.Stub
public IInputMethodWrapper(AbstractInputMethodService context, public IInputMethodWrapper(AbstractInputMethodService context,
InputMethod inputMethod) { InputMethod inputMethod) {
mTarget = context; mTarget = new WeakReference<AbstractInputMethodService>(context);
mCaller = new HandlerCaller(context, this); mCaller = new HandlerCaller(context.getApplicationContext(), this);
mInputMethod = inputMethod; mInputMethod = new WeakReference<InputMethod>(inputMethod);
} }
public InputMethod getInternalInputMethod() { public InputMethod getInternalInputMethod() {
return mInputMethod; return mInputMethod.get();
} }
public void executeMessage(Message msg) { public void executeMessage(Message msg) {
InputMethod inputMethod = mInputMethod.get();
// Need a valid reference to the inputMethod for everything except a dump.
if (inputMethod == null && msg.what != DO_DUMP) {
Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
return;
}
switch (msg.what) { switch (msg.what) {
case DO_DUMP: { case DO_DUMP: {
AbstractInputMethodService target = mTarget.get();
if (target == null) {
return;
}
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
try { try {
mTarget.dump((FileDescriptor)args.arg1, target.dump((FileDescriptor)args.arg1,
(PrintWriter)args.arg2, (String[])args.arg3); (PrintWriter)args.arg2, (String[])args.arg3);
} catch (RuntimeException e) { } catch (RuntimeException e) {
((PrintWriter)args.arg2).println("Exception: " + e); ((PrintWriter)args.arg2).println("Exception: " + e);
@@ -122,22 +134,22 @@ class IInputMethodWrapper extends IInputMethod.Stub
} }
case DO_ATTACH_TOKEN: { case DO_ATTACH_TOKEN: {
mInputMethod.attachToken((IBinder)msg.obj); inputMethod.attachToken((IBinder)msg.obj);
return; return;
} }
case DO_SET_INPUT_CONTEXT: { case DO_SET_INPUT_CONTEXT: {
mInputMethod.bindInput((InputBinding)msg.obj); inputMethod.bindInput((InputBinding)msg.obj);
return; return;
} }
case DO_UNSET_INPUT_CONTEXT: case DO_UNSET_INPUT_CONTEXT:
mInputMethod.unbindInput(); inputMethod.unbindInput();
return; return;
case DO_START_INPUT: { case DO_START_INPUT: {
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj; HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
IInputContext inputContext = (IInputContext)args.arg1; IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null; ? new InputConnectionWrapper(inputContext) : null;
mInputMethod.startInput(ic, (EditorInfo)args.arg2); inputMethod.startInput(ic, (EditorInfo)args.arg2);
return; return;
} }
case DO_RESTART_INPUT: { case DO_RESTART_INPUT: {
@@ -145,33 +157,37 @@ class IInputMethodWrapper extends IInputMethod.Stub
IInputContext inputContext = (IInputContext)args.arg1; IInputContext inputContext = (IInputContext)args.arg1;
InputConnection ic = inputContext != null InputConnection ic = inputContext != null
? new InputConnectionWrapper(inputContext) : null; ? new InputConnectionWrapper(inputContext) : null;
mInputMethod.restartInput(ic, (EditorInfo)args.arg2); inputMethod.restartInput(ic, (EditorInfo)args.arg2);
return; return;
} }
case DO_CREATE_SESSION: { case DO_CREATE_SESSION: {
mInputMethod.createSession(new InputMethodSessionCallbackWrapper( inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mCaller.mContext, (IInputMethodCallback)msg.obj)); mCaller.mContext, (IInputMethodCallback)msg.obj));
return; return;
} }
case DO_SET_SESSION_ENABLED: case DO_SET_SESSION_ENABLED:
mInputMethod.setSessionEnabled((InputMethodSession)msg.obj, inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
msg.arg1 != 0); msg.arg1 != 0);
return; return;
case DO_REVOKE_SESSION: case DO_REVOKE_SESSION:
mInputMethod.revokeSession((InputMethodSession)msg.obj); inputMethod.revokeSession((InputMethodSession)msg.obj);
return; return;
case DO_SHOW_SOFT_INPUT: case DO_SHOW_SOFT_INPUT:
mInputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj); inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return; return;
case DO_HIDE_SOFT_INPUT: case DO_HIDE_SOFT_INPUT:
mInputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj); inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
return; return;
} }
Log.w(TAG, "Unhandled message code: " + msg.what); Log.w(TAG, "Unhandled message code: " + msg.what);
} }
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) AbstractInputMethodService target = mTarget.get();
if (target == null) {
return;
}
if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) { != PackageManager.PERMISSION_GRANTED) {
fout.println("Permission Denial: can't dump InputMethodManager from from pid=" fout.println("Permission Denial: can't dump InputMethodManager from from pid="

View File

@@ -48,4 +48,6 @@ oneway interface IInputMethodSession {
void appPrivateCommand(String action, in Bundle data); void appPrivateCommand(String action, in Bundle data);
void toggleSoftInput(int showFlags, int hideFlags); void toggleSoftInput(int showFlags, int hideFlags);
void finishSession();
} }

View File

@@ -890,12 +890,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
} }
} }
private void finishSession(SessionState sessionState) {
if (sessionState != null && sessionState.session != null) {
try {
sessionState.session.finishSession();
} catch (RemoteException e) {
Log.w(TAG, "Session failed to close due to remote exception", e);
}
}
}
void clearCurMethodLocked() { void clearCurMethodLocked() {
if (mCurMethod != null) { if (mCurMethod != null) {
for (ClientState cs : mClients.values()) { for (ClientState cs : mClients.values()) {
cs.sessionRequested = false; cs.sessionRequested = false;
finishSession(cs.curSession);
cs.curSession = null; cs.curSession = null;
} }
finishSession(mEnabledSession);
mEnabledSession = null;
mCurMethod = null; mCurMethod = null;
} }
mStatusBar.setIconVisibility(mInputMethodIcon, false); mStatusBar.setIconVisibility(mInputMethodIcon, false);