summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Winson Chung <winsonc@google.com> 2023-10-27 18:27:21 +0000
committer Winson Chung <winsonc@google.com> 2023-10-31 16:20:39 +0000
commit64d3acf7fd5e7b6572e4ce46cd8ed05f048b564d (patch)
tree4260cbcb7104c9210eff54f5a3845127de9b55cc
parenta6cb68cf9d85c6479a52dc1b084ff17b7603fc28 (diff)
Key input consumers by token and not by name
- Currently input consumers are singletons keyed by static name, which is problematic because the per-user Launcher process registers and unregisters the input consumer when SysUI binds to the current user's TouchInteractionService. However, the ordering of service create and destroy when switching users is not guaranteed, so you can end up with user 2's service being created before user 1's service destroy is called. Currently this crashes because we expect singletons. - This change changes the tracking of input consumers to be by token (which is already passed by the caller), and uses the token for identifying the input consumer to destroy. In addition, it only enforces a singleton check for input consumers registered for the same user. And when fetching an input consumer by name, the last registered consumer with that name takes precedence and is returned. This allows multiple Launcher processes to register a consumer for the current user without preventing the closing Launcher service from removing its previously registered consumer. Fixes: 300973954 Test: Verify with multiple users that there's no race between registering and destroying the recents input consumer Change-Id: I64d2453c3671747b5799f26b628448fdf03bdb77
-rw-r--r--core/java/android/view/IWindowManager.aidl4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java4
-rw-r--r--services/core/java/com/android/server/wm/InputConsumerImpl.java2
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java56
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
7 files changed, 45 insertions, 35 deletions
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c10fc9f9cb09..6c3b8ab19792 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -525,11 +525,11 @@ interface IWindowManager
out InputChannel inputChannel);
/**
- * Destroy an input consumer by name and display id.
+ * Destroy an input consumer by token and display id.
* This method will also dispose the input channels associated with that InputConsumer.
*/
@UnsupportedAppUsage
- boolean destroyInputConsumer(String name, int displayId);
+ boolean destroyInputConsumer(IBinder token, int displayId);
/**
* Return the touch region for the current IME window, or an empty region if there is none.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 8e3376f163c1..f6cab485fa2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -139,7 +139,7 @@ public class PipInputConsumer {
final InputChannel inputChannel = new InputChannel();
try {
// TODO(b/113087003): Support Picture-in-picture in multi-display.
- mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -163,7 +163,7 @@ public class PipInputConsumer {
}
try {
// TODO(b/113087003): Support Picture-in-picture in multi-display.
- mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to destroy input consumer, %s", TAG, e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index ba0a6d149707..b406e72ffb98 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -139,7 +139,7 @@ public class InputConsumerController {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
- mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
@@ -158,7 +158,7 @@ public class InputConsumerController {
public void unregisterInputConsumer() {
if (mInputEventReceiver != null) {
try {
- mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
} catch (RemoteException e) {
Log.e(TAG, "Failed to destroy input consumer", e);
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 1fa7d2a2aa13..34d765117a57 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -160,7 +160,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
if (dc == null) {
return;
}
- dc.getInputMonitor().destroyInputConsumer(mName);
+ dc.getInputMonitor().destroyInputConsumer(mToken);
unlinkFromDeathRecipient();
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 5c0bc28779a8..61fea4d9212d 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -73,6 +73,7 @@ import com.android.server.inputmethod.InputMethodManagerInternal;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Set;
import java.util.function.Consumer;
@@ -104,7 +105,7 @@ final class InputMonitor {
* The set of input consumer added to the window manager by name, which consumes input events
* for the windows below it.
*/
- private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
+ private final ArrayList<InputConsumerImpl> mInputConsumers = new ArrayList<>();
/**
* Set when recents (overview) is active as part of a shell transition. While set, any focus
@@ -164,31 +165,35 @@ final class InputMonitor {
mDisplayRemoved = true;
}
- private void addInputConsumer(String name, InputConsumerImpl consumer) {
- mInputConsumers.put(name, consumer);
+ private void addInputConsumer(InputConsumerImpl consumer) {
+ mInputConsumers.add(consumer);
consumer.linkToDeathRecipient();
consumer.layout(mInputTransaction, mDisplayWidth, mDisplayHeight);
updateInputWindowsLw(true /* force */);
}
- boolean destroyInputConsumer(String name) {
- if (disposeInputConsumer(mInputConsumers.remove(name))) {
- updateInputWindowsLw(true /* force */);
- return true;
- }
- return false;
- }
-
- private boolean disposeInputConsumer(InputConsumerImpl consumer) {
- if (consumer != null) {
- consumer.disposeChannelsLw(mInputTransaction);
- return true;
+ boolean destroyInputConsumer(IBinder token) {
+ for (int i = 0; i < mInputConsumers.size(); i++) {
+ final InputConsumerImpl consumer = mInputConsumers.get(i);
+ if (consumer != null && consumer.mToken == token) {
+ consumer.disposeChannelsLw(mInputTransaction);
+ mInputConsumers.remove(consumer);
+ updateInputWindowsLw(true /* force */);
+ return true;
+ }
}
return false;
}
InputConsumerImpl getInputConsumer(String name) {
- return mInputConsumers.get(name);
+ // Search in reverse order as the latest input consumer with the name takes precedence
+ for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+ final InputConsumerImpl consumer = mInputConsumers.get(i);
+ if (consumer.mName.equals(name)) {
+ return consumer;
+ }
+ }
+ return null;
}
void layoutInputConsumers(int dw, int dh) {
@@ -200,7 +205,7 @@ final class InputMonitor {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer");
for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
- mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+ mInputConsumers.get(i).layout(mInputTransaction, dw, dh);
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -212,15 +217,16 @@ final class InputMonitor {
// (set so by this function) and must meet some condition for visibility on each update.
void resetInputConsumers(SurfaceControl.Transaction t) {
for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
- mInputConsumers.valueAt(i).hide(t);
+ mInputConsumers.get(i).hide(t);
}
}
void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
UserHandle clientUser) {
- if (mInputConsumers.containsKey(name)) {
+ final InputConsumerImpl existingConsumer = getInputConsumer(name);
+ if (existingConsumer != null && existingConsumer.mClientUser.equals(clientUser)) {
throw new IllegalStateException("Existing input consumer found with name: " + name
- + ", display: " + mDisplayId);
+ + ", display: " + mDisplayId + ", user: " + clientUser);
}
final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
@@ -239,7 +245,7 @@ final class InputMonitor {
throw new IllegalArgumentException("Illegal input consumer : " + name
+ ", display: " + mDisplayId);
}
- addInputConsumer(name, consumer);
+ addInputConsumer(consumer);
}
@VisibleForTesting
@@ -541,11 +547,11 @@ final class InputMonitor {
}
void dump(PrintWriter pw, String prefix) {
- final Set<String> inputConsumerKeys = mInputConsumers.keySet();
- if (!inputConsumerKeys.isEmpty()) {
+ if (!mInputConsumers.isEmpty()) {
pw.println(prefix + "InputConsumers:");
- for (String key : inputConsumerKeys) {
- mInputConsumers.get(key).dump(pw, key, prefix);
+ for (int i = 0; i < mInputConsumers.size(); i++) {
+ final InputConsumerImpl consumer = mInputConsumers.get(i);
+ consumer.dump(pw, consumer.mName, prefix);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 82d4b90d06be..ef2572665281 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -1021,7 +1021,11 @@ public class RecentsAnimationController implements DeathRecipient {
synchronized (mService.getWindowManagerLock()) {
// Clear associated input consumers on runner death
final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
- inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+ final InputConsumerImpl consumer = inputMonitor.getInputConsumer(
+ INPUT_CONSUMER_RECENTS_ANIMATION);
+ if (consumer != null) {
+ inputMonitor.destroyInputConsumer(consumer.mToken);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4a074ff25c74..30808bcc840c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6553,7 +6553,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public boolean destroyInputConsumer(String name, int displayId) {
+ public boolean destroyInputConsumer(IBinder token, int displayId) {
if (!mAtmService.isCallerRecents(Binder.getCallingUid())
&& mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) {
throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission");
@@ -6562,7 +6562,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
- return display.getInputMonitor().destroyInputConsumer(name);
+ return display.getInputMonitor().destroyInputConsumer(token);
}
return false;
}