Merge "Fix local Binder call scenario of IMMS#addClient()"
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e9acdc3..d24b822 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -753,10 +754,20 @@
throw new IllegalStateException(e);
}
final InputMethodManager imm = new InputMethodManager(service, displayId, looper);
+ // InputMethodManagerService#addClient() relies on Binder.getCalling{Pid, Uid}() to
+ // associate PID/UID with each IME client. This means:
+ // A. if this method call will be handled as an IPC, there is no problem.
+ // B. if this method call will be handled as an in-proc method call, we need to
+ // ensure that Binder.getCalling{Pid, Uid}() return Process.my{Pid, Uid}()
+ // Either ways we can always call Binder.{clear, restore}CallingIdentity() because
+ // 1) doing so has no effect for A and 2) doing so is sufficient for B.
+ final long identity = Binder.clearCallingIdentity();
try {
service.addClient(imm.mClient, imm.mIInputContext, displayId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
return imm;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e37153e..e3a1a91 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1770,6 +1770,12 @@
@Override
public void addClient(IInputMethodClient client, IInputContext inputContext,
int selfReportedDisplayId) {
+ // Here there are two scenarios where this method is called:
+ // A. IMM is being instantiated in a different process and this is an IPC from that process
+ // B. IMM is being instantiated in the same process but Binder.clearCallingIdentity() is
+ // called in the caller side if necessary.
+ // In either case the following UID/PID should be the ones where InputMethodManager is
+ // actually running.
final int callerUid = Binder.getCallingUid();
final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
@@ -1778,7 +1784,7 @@
if (state.uid == callerUid && state.pid == callerPid
&& state.selfReportedDisplayId == selfReportedDisplayId) {
throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
- + " is already registered");
+ + "/displayId=" + selfReportedDisplayId + " is already registered.");
}
}
final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);