Android InputManagerService 第二篇 adb shell input

115 阅读2分钟

adb shell

命令都继承 ShellCommand.java

adb shell input

跳过 inputReader 的过程封装 MotionEvent 到 InputDispatcher

InputShellCommand

frameworks/base/services/core/java/com/android/server/input/InputShellCommand.java

image.png

 /**
     * Builds a MotionEvent and injects it into the event stream.
     *
     * @param inputSource the InputDevice.SOURCE_* sending the input event
     * @param action the MotionEvent.ACTION_* for the event
     * @param downTime the value of the ACTION_DOWN event happened
     * @param when the value of SystemClock.uptimeMillis() at which the event happened
     * @param x x coordinate of event
     * @param y y coordinate of event
     * @param pressure pressure of event
     */
    private void injectMotionEvent(int inputSource, int action, long downTime, long when,
            float x, float y, float pressure, int displayId) {
        final int pointerCount = 1;
        MotionEvent.PointerProperties[] pointerProperties =
                new MotionEvent.PointerProperties[pointerCount];
        MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount];
        for (int i = 0; i < pointerCount; i++) {
            pointerProperties[i] = new MotionEvent.PointerProperties();
            pointerProperties[i].id = i;
            pointerProperties[i].toolType = getToolType(inputSource);
            pointerCoords[i] = new MotionEvent.PointerCoords();
            pointerCoords[i].x = x;
            pointerCoords[i].y = y;
            pointerCoords[i].pressure = pressure;
            pointerCoords[i].size = DEFAULT_SIZE;
        }
        if (displayId == INVALID_DISPLAY
                && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
            displayId = DEFAULT_DISPLAY;
        }
        MotionEvent event = MotionEvent.obtain(downTime, when, action, pointerCount,
                pointerProperties, pointerCoords, DEFAULT_META_STATE, DEFAULT_BUTTON_STATE,
                DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, getInputDeviceId(inputSource),
                DEFAULT_EDGE_FLAGS, inputSource, displayId, DEFAULT_FLAGS);
        InputManagerGlobal.getInstance().injectInputEvent(event,
                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
    }

InputManagerGlobal

aospxref.com/android-14.…

/**
 * Manages communication with the input manager service on behalf of
 * an application process. You're probably looking for { @link InputManager}.
 *
 * @hide
 */
public final class InputManagerGlobal {

    ...
    /**
     * @see InputManager#injectInputEvent(InputEvent, int, int)
     */

    public boolean injectInputEvent(InputEvent event, int mode, int targetUid) {
        Objects.requireNonNull(event , "event must not be null");

        if (mode != InputEventInjectionSync.NONE
                && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
                && mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
            throw new IllegalArgumentException("mode is invalid");
        }

        try {
            return mIm.injectInputEventToTarget(event, mode, targetUid);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

InputManagerService

image.png


    @Override // Binder call
    public boolean injectInputEventToTarget(InputEvent event, int mode, int targetUid) {
        if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
                "injectInputEvent()", true /*checkInstrumentationSource*/)) {
            throw new SecurityException(
                    "Injecting input events requires the caller (or the source of the "
                            + "instrumentation, if any) to have the INJECT_EVENTS permission.");
        }
        // We are not checking if targetUid matches the callingUid, since having the permission
        // already means you can inject into any window.
        Objects.requireNonNull(event, "event must not be null");
        if (mode != InputEventInjectionSync.NONE
                && mode != InputEventInjectionSync.WAIT_FOR_FINISHED
                && mode != InputEventInjectionSync.WAIT_FOR_RESULT) {
            throw new IllegalArgumentException("mode is invalid");
        }

        final int pid = Binder.getCallingPid();
        final long ident = Binder.clearCallingIdentity();
        final boolean injectIntoUid = targetUid != Process.INVALID_UID;
        final int result;
        try {
            result = mNative.injectInputEvent(event, injectIntoUid,
                    targetUid, mode, INJECTION_TIMEOUT_MILLIS,
                    WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        switch (result) {
            case InputEventInjectionResult.SUCCEEDED:
                return true;
            case InputEventInjectionResult.TARGET_MISMATCH:
                if (!injectIntoUid) {
                    throw new IllegalStateException("Injection should not result in TARGET_MISMATCH"
                            + " when it is not targeted into to a specific uid.");
                }
                throw new IllegalArgumentException(
                    "Targeted input event injection from pid " + pid
                            + " was not directed at a window owned by uid "
                            + targetUid + ".");
            case InputEventInjectionResult.TIMED_OUT:
                Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
                return false;
            case InputEventInjectionResult.FAILED:
            default:
                Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
                return false;
        }
    }

NativeInputManagerService

aospxref.com/android-14.…

       @Override
        public native int injectInputEvent(InputEvent event, boolean injectIntoUid, int uid,
                int syncMode, int timeoutMillis, int policyFlags);

InputDispatcher.cpp

image.png

总结

通过 injectMotionEvent 方法传递触摸事件都只跳过了 inputReader , 进入 inputDisptecher , 要通过 WindowManagerService 的过滤策略

image.png