Android InputManagerService

165 阅读7分钟

流程图

image.png

image.png

image.png

startOtherServices 注册 ServiceManager

InputManager 和 WMS 紧密相关 WMS.main 传入 inputManager

    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
            t.traceBegin("startOtherServices");
 
            // 初始化InputManagerService
            t.traceBegin("StartInputManagerService");
            inputManager = new InputManagerService(context);
            t.traceEnd();
 
            t.traceBegin("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            t.traceEnd();
 
            t.traceBegin("SetWindowManagerService");
            mActivityManagerService.setWindowManager(wm);
            t.traceEnd();
 
             t.traceBegin("StartInputManager");
            inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
            // 初始化
            inputManager.start();
            t.traceEnd();
}

InputManagerService.setWindowManagerCallbacks

WindowManagerCallbacks 是 InputManagerService 内部定义的一个接口类,用于 WindowManagerService 和 InputManagerService 之间的交互, 由 InputManagerCallback 实现包括通知ANR,通知系统配置变更,通知焦点改变,拦截按键/触屏消息的分发等在内的接口功能,可以通过 WindowManagerService.getInputManagerCallback() 获取;

[-> InputManagerService.java]

// WindowManagerService策略类,由PhoneWindowManager实现
WindowManagerPolicy mPolicy;

private WindowManagerCallbacks mWindowManagerCallbacks;

public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
    mWindowManagerCallbacks = callbacks;
}

// Native callback.
private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
    return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
[-> InputManagerCallback.java]

@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);
}
final InputManagerCallback mInputManagerCallback = new InputManagerCallback(this);

public InputManagerCallback getInputManagerCallback() {
    return mInputManagerCallback;
}

这里以 interceptKeyBeforeDispatching 为例说明:native层在分发按键消息时会调用Java层此方法,而InputManagerService并不直接处理而是交由之前设置的 InputManagerCallback 来处理,它又会将此事件交由 WindowManagerService 的策略类 PhoneWindowManager 进行专门处理。

inputManager.start();

    public InputManagerService(Context context) {
        this.mContext = context;
        this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());
 
        mStaticAssociations = loadStaticInputPortAssociations();
        mUseDevInputEventForAudioJack =
                context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
        Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack="
                + mUseDevInputEventForAudioJack);
        // 调用nativeini jni 函数接口
        mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
 
        String doubleTouchGestureEnablePath = context.getResources().getString(
                R.string.config_doubleTouchGestureEnableFile);
        mDoubleTouchGestureEnableFile = TextUtils.isEmpty(doubleTouchGestureEnablePath) ? null :
            new File(doubleTouchGestureEnablePath);
 
        LocalServices.addService(InputManagerInternal.class, new LocalService());
    }
 
    public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
        mWindowManagerCallbacks = callbacks;
    }
 
    public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
        mWiredAccessoryCallbacks = callbacks;
    }
 
    public void start() {
        Slog.i(TAG, "Starting input manager");
        // 调用nativeStart  jni 函数接口
        nativeStart(mPtr);
 
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
 
        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();
        registerAccessibilityLargePointerSettingObserver();
        registerLongPressTimeoutObserver();
 
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                updatePointerSpeedFromSettings();
                updateShowTouchesFromSettings();
                updateAccessibilityLargePointerFromSettings();
                updateDeepPressStatusFromSettings("user switched");
            }
        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
 
        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
        updateAccessibilityLargePointerFromSettings();
        updateDeepPressStatusFromSettings("just booted");
    }

nativeInit -> nativeStart(mPtr) ->

static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == nullptr) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
 
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}
 
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
    status_t result = im->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

InputManager::start() -> mDispatcher->start() -> mReader->start()

    InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    // 创建事件分发器
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    // 过滤
    mClassifier = new InputClassifier(mDispatcher);
    // 事件读取
    mReader = createInputReader(readerPolicy, mClassifier);
}
 
status_t InputManager::start() {
    status_t result = mDispatcher->start();
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
 
    result = mReader->start();
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);
        mDispatcher->stop();
        return result;
    }
    return OK;
}

InputDispatcher

InputReader -> mEventHub

InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& listener)
      : mContext(this),
      //  evenhub 
        mEventHub(eventHub),
        mPolicy(policy),
        mGlobalMetaState(0),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);
 
    { // acquire lock
        AutoMutex _l(mLock);
 
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}
 
InputReader::~InputReader() {}
 
status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    // 启动线程。
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}
loopOnce
 void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    { // acquire lock
    // 获取Event  事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
 
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
 
        if (count) {
            //
            processEventsLocked(mEventBuffer, count);
        }
 
        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
#if DEBUG_RAW_EVENTS
                ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }
 
        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock
 
    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
 
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    mQueuedListener->flush();
}
EventHub->getEvents
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
 
    AutoMutex _l(mLock);
 
    struct input_event readBuffer[bufferSize];
 
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;
 
            ALOGI("Reopening all input devices due to a configuration change.");
 
            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }
 
        // Report any devices that had last been added/removed.
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = (device->id == mBuiltInKeyboardId)
                    ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                    : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
 
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
 
        while (mOpeningDevices != nullptr) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
 
        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
 
        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.fd == mINotifyFd) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }
 
            if (eventItem.data.fd == mWakeReadPipeFd) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                          eventItem.events);
                }
                continue;
            }
 
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            if (!device) {
                ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events,
                      eventItem.data.fd);
                ALOG_ASSERT(!DEBUG);
                continue;
            }
            if (device->videoDevice && eventItem.data.fd == device->videoDevice->getFd()) {
                if (eventItem.events & EPOLLIN) {
                    size_t numFrames = device->videoDevice->readAndQueueFrames();
                    if (numFrames == 0) {
                        ALOGE("Received epoll event for video device %s, but could not read frame",
                              device->videoDevice->getName().c_str());
                    }
                } else if (eventItem.events & EPOLLHUP) {
                    // TODO(b/121395353) - consider adding EPOLLRDHUP
                    ALOGI("Removing video device %s due to epoll hang-up event.",
                          device->videoDevice->getName().c_str());
                    unregisterVideoDeviceFromEpollLocked(*device->videoDevice);
                    device->videoDevice = nullptr;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
                          device->videoDevice->getName().c_str());
                    ALOG_ASSERT(!DEBUG);
                }
                continue;
            }
            // This must be an input event
            if (eventItem.events & EPOLLIN) {
                int32_t readSize =
                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                          " bufferSize: %zu capacity: %zu errno: %d)\n",
                          device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
 
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                      device->identifier.name.c_str());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events,
                      device->identifier.name.c_str());
            }
        }
 
        // readNotify() will modify the list of devices so this must be done after
        // processing all other events to ensure that we read all remaining events
        // before closing the devices.
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }
 
        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }
 
        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }
 
        // Poll for events.
        // When a device driver has pending (unread) events, it acquires
        // a kernel wake lock.  Once the last pending event has been read, the device
        // driver will release the kernel wake lock, but the epoll will hold the wakelock,
        // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
        // is called again for the same fd that produced the event.
        // Thus the system can only sleep if there are no events pending or
        // currently being processed.
        //
        // The timeout is advisory only.  If the device is asleep, it will not wake just to
        // service the timeout.
        mPendingEventIndex = 0;
 
        mLock.unlock(); // release lock before poll
        //  epoll_wait 函数获取有事件fd。
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
        mLock.lock(); // reacquire lock after poll
 
        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }
 
        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;
 
            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }
 
    // All done, return the number of events we read.
    return event - buffer;
}
evenhub 通过epool_create 、epoll_add、 epoll_wait 监测/dev/input/eventX ,是否有事件。
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
            int32_t deviceId = rawEvent->deviceId;
            while (batchSize < count) {
                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT ||
                    rawEvent[batchSize].deviceId != deviceId) {
                    break;
                }
                batchSize += 1;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
            //处理事件。
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            switch (rawEvent->type) {
                case EventHubInterface::DEVICE_ADDED:
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}
void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return;
    }
    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    device->process(rawEvents, count);
}

image.png

JNI 注册

static const JNINativeMethod gInputManagerMethods[] = {
        /* name, signature, funcPtr */
        {"nativeInit",
         "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/"
         "MessageQueue;)J",
         (void*)nativeInit},
        {"nativeStart", "(J)V", (void*)nativeStart},
        {"nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
         (void*)nativeSetDisplayViewports},
        {"nativeGetScanCodeState", "(JIII)I", (void*)nativeGetScanCodeState},
        {"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState},
        {"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState},
        {"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys},
        {"nativeRegisterInputChannel", "(JLandroid/view/InputChannel;)V",
         (void*)nativeRegisterInputChannel},
        {"nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V",
         (void*)nativeRegisterInputMonitor},
        {"nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V",
         (void*)nativeUnregisterInputChannel},
        {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
        {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
        {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
        {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I",
         (void*)nativeInjectInputEvent},
        {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
         (void*)nativeVerifyInputEvent},
        {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock},
        {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved},
        {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V",
         (void*)nativeSetFocusedApplication},
        {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay},
        {"nativeSetPointerCapture", "(JZ)V", (void*)nativeSetPointerCapture},
        {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
        {"nativeSetSystemUiVisibility", "(JI)V", (void*)nativeSetSystemUiVisibility},
        {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
         (void*)nativeTransferTouchFocus},
        {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
        {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
        {"nativeSetInteractive", "(JZ)V", (void*)nativeSetInteractive},
        {"nativeReloadCalibration", "(J)V", (void*)nativeReloadCalibration},
        {"nativeVibrate", "(JI[JII)V", (void*)nativeVibrate},
        {"nativeCancelVibrate", "(JII)V", (void*)nativeCancelVibrate},
        {"nativeReloadKeyboardLayouts", "(J)V", (void*)nativeReloadKeyboardLayouts},
        {"nativeReloadDeviceAliases", "(J)V", (void*)nativeReloadDeviceAliases},
        {"nativeDump", "(J)Ljava/lang/String;", (void*)nativeDump},
        {"nativeMonitor", "(J)V", (void*)nativeMonitor},
        {"nativeIsInputDeviceEnabled", "(JI)Z", (void*)nativeIsInputDeviceEnabled},
        {"nativeEnableInputDevice", "(JI)V", (void*)nativeEnableInputDevice},
        {"nativeDisableInputDevice", "(JI)V", (void*)nativeDisableInputDevice},
        {"nativeSetPointerIconType", "(JI)V", (void*)nativeSetPointerIconType},
        {"nativeReloadPointerIcons", "(J)V", (void*)nativeReloadPointerIcons},
        {"nativeSetCustomPointerIcon", "(JLandroid/view/PointerIcon;)V",
         (void*)nativeSetCustomPointerIcon},
        {"nativeCanDispatchToDisplay", "(JII)Z", (void*)nativeCanDispatchToDisplay},
        {"nativeNotifyPortAssociationsChanged", "(J)V", (void*)nativeNotifyPortAssociationsChanged},
        {"nativeSetMotionClassifierEnabled", "(JZ)V", (void*)nativeSetMotionClassifierEnabled},
        { "nativedispatchMouse", "(FFIIJ)V", (void*) android_server_InputManager_nativedispatchMouse },
        { "nativedispatchMouseByCd", "(FFJ)V", (void*) android_server_InputManager_nativedispatchMouseByCd },
};