Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf10ca05 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge changes I02a480dd,I9673b49d

* changes:
  Use binder tokens to remove its gesture monitors if a process dies
  Implement gesture monitors using spy windows
parents bf1d2de7 c8096582
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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.
+2 −1
Original line number Diff line number Diff line
@@ -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();
        }
+14 −6
Original line number Diff line number Diff line
@@ -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

}
+95 −0
Original line number Diff line number Diff line
/*
 * 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;
    }
}
+129 −21
Original line number Diff line number Diff line
@@ -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);
        }
    }

Loading