Merge changes I02a480dd,I9673b49d

* changes:
  Use binder tokens to remove its gesture monitors if a process dies
  Implement gesture monitors using spy windows
This commit is contained in:
Prabir Pradhan
2022-02-02 14:10:13 +00:00
committed by Android (Google) Code Review
6 changed files with 261 additions and 29 deletions

View File

@@ -112,7 +112,7 @@ interface IInputManager {
oneway void requestPointerCapture(IBinder inputChannelToken, boolean enabled);
/** Create an input monitor for gestures. */
InputMonitor monitorGestureInput(String name, int displayId);
InputMonitor monitorGestureInput(IBinder token, String name, int displayId);
// Add a runtime association between the input port and the display port. This overrides any
// static associations.

View File

@@ -35,6 +35,7 @@ import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.os.Binder;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibration;
@@ -1211,7 +1212,7 @@ public final class InputManager {
*/
public InputMonitor monitorGestureInput(String name, int displayId) {
try {
return mIm.monitorGestureInput(name, displayId);
return mIm.monitorGestureInput(new Binder(), name, displayId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}

View File

@@ -79,13 +79,17 @@ public final class InputMonitor implements Parcelable {
// Code below generated by codegen v1.0.7.
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
@DataClass.Generated.Member
@@ -126,7 +130,7 @@ public final class InputMonitor implements Parcelable {
@Override
@DataClass.Generated.Member
public void writeToParcel(Parcel dest, int flags) {
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -141,7 +145,7 @@ public final class InputMonitor implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ InputMonitor(Parcel in) {
/* package-private */ InputMonitor(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -167,17 +171,21 @@ public final class InputMonitor implements Parcelable {
}
@Override
public InputMonitor createFromParcel(Parcel in) {
public InputMonitor createFromParcel(@NonNull Parcel in) {
return new InputMonitor(in);
}
};
@DataClass.Generated(
time = 1571177265149L,
codegenVersion = "1.0.7",
time = 1637697281750L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.input;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import android.os.IBinder;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputMonitor;
import android.view.InputWindowHandle;
import android.view.SurfaceControl;
import android.view.WindowManager;
/**
* An internal implementation of an {@link InputMonitor} that uses a spy window.
*
* This spy window is a layer container in the SurfaceFlinger hierarchy that does not have any
* graphical buffer, but can still receive input. It is parented to the DisplayContent so
* that it can spy on any pointer events that start in the DisplayContent bounds. When the
* object is constructed, it will add itself to SurfaceFlinger.
*/
class GestureMonitorSpyWindow {
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
// The token, InputChannel, and SurfaceControl are owned by this object.
final IBinder mMonitorToken;
final InputChannel mClientChannel;
final SurfaceControl mInputSurface;
GestureMonitorSpyWindow(IBinder token, String name, int displayId, int pid, int uid,
SurfaceControl sc, InputChannel inputChannel) {
mMonitorToken = token;
mClientChannel = inputChannel;
mInputSurface = sc;
mApplicationHandle = new InputApplicationHandle(null, name,
DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mWindowHandle.visible = true;
mWindowHandle.focusable = false;
mWindowHandle.hasWallpaper = false;
mWindowHandle.paused = false;
mWindowHandle.ownerPid = pid;
mWindowHandle.ownerUid = uid;
mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.trustedOverlay = true;
mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
t.setLayer(mInputSurface, Integer.MAX_VALUE);
t.setPosition(mInputSurface, 0, 0);
t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
t.apply();
}
void remove() {
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.hide(mInputSurface);
t.remove(mInputSurface);
t.apply();
mClientChannel.dispose();
}
String dump() {
return "name='" + mWindowHandle.name + "', inputChannelToken="
+ mClientChannel.getToken() + " displayId=" + mWindowHandle.displayId;
}
}

View File

@@ -150,6 +150,8 @@ public class InputManagerService extends IInputManager.Stub
static final String TAG = "InputManager";
static final boolean DEBUG = false;
private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = false;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
@@ -277,6 +279,12 @@ public class InputManagerService extends IInputManager.Stub
@GuardedBy("mPointerDisplayIdLock")
private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
// Holds all the registered gesture monitors that are implemented as spy windows. The spy
// windows are mapped by their InputChannel tokens.
@GuardedBy("mInputMonitors")
final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>();
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
private static native void nativeStart(long ptr);
@@ -716,33 +724,77 @@ public class InputManagerService extends IInputManager.Stub
inputChannelName, Binder.getCallingPid());
}
@NonNull
private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name,
int displayId, int pid, int uid) {
final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
displayId);
if (sc == null) {
throw new IllegalArgumentException(
"Could not create gesture monitor surface on display: " + displayId);
}
final InputChannel channel = createInputChannel(name);
try {
monitorToken.linkToDeath(() -> removeSpyWindowGestureMonitor(channel.getToken()), 0);
} catch (RemoteException e) {
Slog.i(TAG, "Client died before '" + name + "' could be created.");
return null;
}
synchronized (mInputMonitors) {
mInputMonitors.put(channel.getToken(),
new GestureMonitorSpyWindow(monitorToken, name, displayId, pid, uid, sc,
channel));
}
final InputChannel outInputChannel = new InputChannel();
channel.copyTo(outInputChannel);
return outInputChannel;
}
private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) {
final GestureMonitorSpyWindow monitor;
synchronized (mInputMonitors) {
monitor = mInputMonitors.remove(inputChannelToken);
}
removeInputChannel(inputChannelToken);
if (monitor == null) return;
monitor.remove();
}
/**
* Creates an input monitor that will receive pointer events for the purposes of system-wide
* gesture interpretation.
*
* @param inputChannelName The input channel name.
* @param requestedName The input channel name.
* @param displayId Target display id.
* @return The input channel.
*/
@Override // Binder call
public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,
int displayId) {
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
"monitorInputRegion()")) {
"monitorGestureInput()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
}
Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");
Objects.requireNonNull(requestedName, "name must not be null.");
Objects.requireNonNull(monitorToken, "token must not be null.");
if (displayId < Display.DEFAULT_DISPLAY) {
throw new IllegalArgumentException("displayId must >= 0.");
}
final String name = "[Gesture Monitor] " + requestedName;
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
InputChannel inputChannel = nativeCreateInputMonitor(
mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);
InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
return new InputMonitor(inputChannel, host);
final InputChannel inputChannel =
USE_SPY_WINDOW_GESTURE_MONITORS
? createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid)
: nativeCreateInputMonitor(mPtr, displayId, true /*isGestureMonitor*/,
requestedName, pid);
return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2563,37 +2615,51 @@ public class InputManagerService extends IInputManager.Stub
String dumpStr = nativeDump(mPtr);
if (dumpStr != null) {
pw.println(dumpStr);
dumpAssociations(pw);
}
pw.println("Input Manager Service (Java) State:");
dumpAssociations(pw, " " /*prefix*/);
dumpSpyWindowGestureMonitors(pw, " " /*prefix*/);
}
private void dumpAssociations(PrintWriter pw) {
private void dumpAssociations(PrintWriter pw, String prefix) {
if (!mStaticAssociations.isEmpty()) {
pw.println("Static Associations:");
pw.println(prefix + "Static Associations:");
mStaticAssociations.forEach((k, v) -> {
pw.print(" port: " + k);
pw.print(prefix + " port: " + k);
pw.println(" display: " + v);
});
}
synchronized (mAssociationsLock) {
if (!mRuntimeAssociations.isEmpty()) {
pw.println("Runtime Associations:");
pw.println(prefix + "Runtime Associations:");
mRuntimeAssociations.forEach((k, v) -> {
pw.print(" port: " + k);
pw.print(prefix + " port: " + k);
pw.println(" display: " + v);
});
}
if (!mUniqueIdAssociations.isEmpty()) {
pw.println("Unique Id Associations:");
pw.println(prefix + "Unique Id Associations:");
mUniqueIdAssociations.forEach((k, v) -> {
pw.print(" port: " + k);
pw.print(prefix + " port: " + k);
pw.println(" uniqueId: " + v);
});
}
}
}
private void dumpSpyWindowGestureMonitors(PrintWriter pw, String prefix) {
synchronized (mInputMonitors) {
if (mInputMonitors.isEmpty()) return;
pw.println(prefix + "Gesture Monitors (implemented as spy windows):");
int i = 0;
for (final GestureMonitorSpyWindow monitor : mInputMonitors.values()) {
pw.append(prefix + " " + i++ + ": ").println(monitor.dump());
}
}
}
private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -2618,6 +2684,7 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
nativeMonitor(mPtr);
}
@@ -2686,6 +2753,11 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
private void notifyInputChannelBroken(IBinder token) {
synchronized (mInputMonitors) {
if (mInputMonitors.containsKey(token)) {
removeSpyWindowGestureMonitor(token);
}
}
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
@@ -2721,6 +2793,17 @@ public class InputManagerService extends IInputManager.Stub
// Native callback
private void notifyWindowUnresponsive(IBinder token, String reason) {
int gestureMonitorPid = -1;
synchronized (mInputMonitors) {
final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
if (gestureMonitor != null) {
gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
}
}
if (gestureMonitorPid != -1) {
mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason);
return;
}
mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
}
@@ -2731,6 +2814,17 @@ public class InputManagerService extends IInputManager.Stub
// Native callback
private void notifyWindowResponsive(IBinder token) {
int gestureMonitorPid = -1;
synchronized (mInputMonitors) {
final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
if (gestureMonitor != null) {
gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
}
}
if (gestureMonitorPid != -1) {
mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid);
return;
}
mWindowManagerCallbacks.notifyWindowResponsive(token);
}
@@ -3184,6 +3278,16 @@ public class InputManagerService extends IInputManager.Stub
* pointers such as the mouse cursor and touch spots for the given display.
*/
SurfaceControl getParentSurfaceForPointers(int displayId);
/**
* Create a {@link SurfaceControl} that can be configured to receive input over the entire
* display to implement a gesture monitor. The surface will not have a graphical buffer.
* @param name the name of the gesture monitor
* @param displayId the display to create the window in
* @return the SurfaceControl of the new layer container surface
*/
@Nullable
SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
}
/**
@@ -3258,20 +3362,24 @@ public class InputManagerService extends IInputManager.Stub
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
private final IBinder mToken;
private final IBinder mInputChannelToken;
InputMonitorHost(IBinder token) {
mToken = token;
InputMonitorHost(IBinder inputChannelToken) {
mInputChannelToken = inputChannelToken;
}
@Override
public void pilferPointers() {
nativePilferPointers(mPtr, mToken);
nativePilferPointers(mPtr, mInputChannelToken);
}
@Override
public void dispose() {
nativeRemoveInputChannel(mPtr, mToken);
if (USE_SPY_WINDOW_GESTURE_MONITORS) {
removeSpyWindowGestureMonitor(mInputChannelToken);
return;
}
nativeRemoveInputChannel(mPtr, mInputChannelToken);
}
}

View File

@@ -24,6 +24,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.PointF;
import android.os.Debug;
import android.os.IBinder;
@@ -254,6 +255,25 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
}
}
@Override
@Nullable
public SurfaceControl createSurfaceForGestureMonitor(String name, int displayId) {
synchronized (mService.mGlobalLock) {
final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
if (dc == null) {
Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ " - DisplayContent not found.");
return null;
}
return mService.makeSurfaceBuilder(dc.getSession())
.setContainerLayer()
.setName(name)
.setCallsite("createSurfaceForGestureMonitor")
.setParent(dc.getSurfaceControl())
.build();
}
}
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {