Skip to content

Commit 0d3b202

Browse files
Dianne HackbornAndroid Code Review
authored andcommitted
Merge "Fix Memory Leak When Switching Input Methods"
2 parents 1be46d7 + 0c33ed2 commit 0d3b202

File tree

4 files changed

+62
-21
lines changed

4 files changed

+62
-21
lines changed

core/java/android/inputmethodservice/IInputMethodSessionWrapper.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
3131
private static final int DO_UPDATE_CURSOR = 95;
3232
private static final int DO_APP_PRIVATE_COMMAND = 100;
3333
private static final int DO_TOGGLE_SOFT_INPUT = 105;
34-
35-
final HandlerCaller mCaller;
36-
final InputMethodSession mInputMethodSession;
34+
private static final int DO_FINISH_SESSION = 110;
35+
36+
HandlerCaller mCaller;
37+
InputMethodSession mInputMethodSession;
3738

3839
// NOTE: we should have a cache of these.
3940
static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
@@ -111,6 +112,10 @@ public void executeMessage(Message msg) {
111112
mInputMethodSession.toggleSoftInput(msg.arg1, msg.arg2);
112113
return;
113114
}
115+
case DO_FINISH_SESSION: {
116+
mInputMethodSession = null;
117+
return;
118+
}
114119
}
115120
Log.w(TAG, "Unhandled message code: " + msg.what);
116121
}
@@ -158,4 +163,8 @@ public void appPrivateCommand(String action, Bundle data) {
158163
public void toggleSoftInput(int showFlags, int hideFlags) {
159164
mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
160165
}
166+
167+
public void finishSession() {
168+
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
169+
}
161170
}

core/java/android/inputmethodservice/IInputMethodWrapper.java

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import java.io.FileDescriptor;
2525
import java.io.PrintWriter;
26+
import java.lang.ref.WeakReference;
2627
import java.util.concurrent.CountDownLatch;
2728
import java.util.concurrent.TimeUnit;
2829

@@ -48,9 +49,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
4849
private static final int DO_SHOW_SOFT_INPUT = 60;
4950
private static final int DO_HIDE_SOFT_INPUT = 70;
5051

51-
final AbstractInputMethodService mTarget;
52+
final WeakReference<AbstractInputMethodService> mTarget;
5253
final HandlerCaller mCaller;
53-
final InputMethod mInputMethod;
54+
final WeakReference<InputMethod> mInputMethod;
5455

5556
static class Notifier {
5657
boolean notified;
@@ -80,21 +81,32 @@ public void sessionCreated(InputMethodSession session) {
8081

8182
public IInputMethodWrapper(AbstractInputMethodService context,
8283
InputMethod inputMethod) {
83-
mTarget = context;
84-
mCaller = new HandlerCaller(context, this);
85-
mInputMethod = inputMethod;
84+
mTarget = new WeakReference<AbstractInputMethodService>(context);
85+
mCaller = new HandlerCaller(context.getApplicationContext(), this);
86+
mInputMethod = new WeakReference<InputMethod>(inputMethod);
8687
}
8788

8889
public InputMethod getInternalInputMethod() {
89-
return mInputMethod;
90+
return mInputMethod.get();
9091
}
9192

9293
public void executeMessage(Message msg) {
94+
InputMethod inputMethod = mInputMethod.get();
95+
// Need a valid reference to the inputMethod for everything except a dump.
96+
if (inputMethod == null && msg.what != DO_DUMP) {
97+
Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
98+
return;
99+
}
100+
93101
switch (msg.what) {
94102
case DO_DUMP: {
103+
AbstractInputMethodService target = mTarget.get();
104+
if (target == null) {
105+
return;
106+
}
95107
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
96108
try {
97-
mTarget.dump((FileDescriptor)args.arg1,
109+
target.dump((FileDescriptor)args.arg1,
98110
(PrintWriter)args.arg2, (String[])args.arg3);
99111
} catch (RuntimeException e) {
100112
((PrintWriter)args.arg2).println("Exception: " + e);
@@ -106,56 +118,60 @@ public void executeMessage(Message msg) {
106118
}
107119

108120
case DO_ATTACH_TOKEN: {
109-
mInputMethod.attachToken((IBinder)msg.obj);
121+
inputMethod.attachToken((IBinder)msg.obj);
110122
return;
111123
}
112124
case DO_SET_INPUT_CONTEXT: {
113-
mInputMethod.bindInput((InputBinding)msg.obj);
125+
inputMethod.bindInput((InputBinding)msg.obj);
114126
return;
115127
}
116128
case DO_UNSET_INPUT_CONTEXT:
117-
mInputMethod.unbindInput();
129+
inputMethod.unbindInput();
118130
return;
119131
case DO_START_INPUT: {
120132
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
121133
IInputContext inputContext = (IInputContext)args.arg1;
122134
InputConnection ic = inputContext != null
123135
? new InputConnectionWrapper(inputContext) : null;
124-
mInputMethod.startInput(ic, (EditorInfo)args.arg2);
136+
inputMethod.startInput(ic, (EditorInfo)args.arg2);
125137
return;
126138
}
127139
case DO_RESTART_INPUT: {
128140
HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
129141
IInputContext inputContext = (IInputContext)args.arg1;
130142
InputConnection ic = inputContext != null
131143
? new InputConnectionWrapper(inputContext) : null;
132-
mInputMethod.restartInput(ic, (EditorInfo)args.arg2);
144+
inputMethod.restartInput(ic, (EditorInfo)args.arg2);
133145
return;
134146
}
135147
case DO_CREATE_SESSION: {
136-
mInputMethod.createSession(new InputMethodSessionCallbackWrapper(
148+
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
137149
mCaller.mContext, (IInputMethodCallback)msg.obj));
138150
return;
139151
}
140152
case DO_SET_SESSION_ENABLED:
141-
mInputMethod.setSessionEnabled((InputMethodSession)msg.obj,
153+
inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
142154
msg.arg1 != 0);
143155
return;
144156
case DO_REVOKE_SESSION:
145-
mInputMethod.revokeSession((InputMethodSession)msg.obj);
157+
inputMethod.revokeSession((InputMethodSession)msg.obj);
146158
return;
147159
case DO_SHOW_SOFT_INPUT:
148-
mInputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
160+
inputMethod.showSoftInput(msg.arg1, (ResultReceiver)msg.obj);
149161
return;
150162
case DO_HIDE_SOFT_INPUT:
151-
mInputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
163+
inputMethod.hideSoftInput(msg.arg1, (ResultReceiver)msg.obj);
152164
return;
153165
}
154166
Log.w(TAG, "Unhandled message code: " + msg.what);
155167
}
156168

157169
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
158-
if (mTarget.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
170+
AbstractInputMethodService target = mTarget.get();
171+
if (target == null) {
172+
return;
173+
}
174+
if (target.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
159175
!= PackageManager.PERMISSION_GRANTED) {
160176

161177
fout.println("Permission Denial: can't dump InputMethodManager from from pid="

core/java/com/android/internal/view/IInputMethodSession.aidl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,6 @@ oneway interface IInputMethodSession {
4848
void appPrivateCommand(String action, in Bundle data);
4949

5050
void toggleSoftInput(int showFlags, int hideFlags);
51+
52+
void finishSession();
5153
}

services/java/com/android/server/InputMethodManagerService.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,12 +855,26 @@ void unbindCurrentMethodLocked(boolean reportToClient) {
855855
}
856856
}
857857

858+
private void finishSession(SessionState sessionState) {
859+
if (sessionState != null && sessionState.session != null) {
860+
try {
861+
sessionState.session.finishSession();
862+
} catch (RemoteException e) {
863+
Log.w(TAG, "Session failed to close due to remote exception", e);
864+
}
865+
}
866+
}
867+
858868
void clearCurMethodLocked() {
859869
if (mCurMethod != null) {
860870
for (ClientState cs : mClients.values()) {
861871
cs.sessionRequested = false;
872+
finishSession(cs.curSession);
862873
cs.curSession = null;
863874
}
875+
876+
finishSession(mEnabledSession);
877+
mEnabledSession = null;
864878
mCurMethod = null;
865879
}
866880
mStatusBar.setIconVisibility(mInputMethodIcon, false);

0 commit comments

Comments
 (0)