Android12 Input子系统解析

安卓系统Input读取与分发流程解析。

Views: 12
0 0
Read Time:18 Minute, 27 Second

大家有没有想过,为什么我们在安卓设备上点击屏幕,对应的菜单就会出现,屏幕上的按钮会出现相应的特效,这一过程到底是怎么实现的,这里就需要涉及到Input事件的分发与处理了。今天这篇文章将会针对这个问题进行分析。

Input概述

从第一段中我们引申出的问题,我们需要自下而上考虑这些问题:

1.系统Driver如何读取设备上的事件,这些设备有可能是鼠标、键盘、触摸屏等

2.从系统Driver到Native Framework如何传递,乃至分发事件

3.分发事件后如何匹配到对应的窗口

4.在多个Layer的情况下如何保证同一区域下对应Layer的处理

5.对应窗口对应Layer时,是如何具体处理Input的

6.Input事件与View的关���处理是如何实现的

首先我们先看一张图,其囊括了Android Input子系统的主体部分:

这里面涉及到的几个重要部分如下:

SystemServer:Android中整个Input子系统的Entry,由SystemServer拉起InputManager使得整个Input子系统开始运作。

InputManagerService:向下会接入com_android_server_input_InputManagerService,这部分属于是JNI接口。

NativeInputManager:属于 com_android_server_input_InputManagerService 的核心部分,源码位于frameworks/base/services/core/jni/com_android_server_InputManagerService.cpp,其核心实现为NativeInputManager类。

InputManager:属于Native C++层的管理入口

InputReader:由InputManager进行创建,将从Eventhub中获取到的事件(通过调用Eventhub中的getEvents)进行加工处理,并将数据传入InputDispatcher进行分发。

EventHub:用于管理/dev/input目录下的字符设备,通过epoll机制实现事件监听,并通过ioctl,read等系统调用获取input设备信息并获取input设备的具体事件。

这里我们摘录部分关键性代码,来串联整个调用链。

首先是整个Input系统的初始化:

//frameworks/base/services/java/com/android/server/SystemServer.java 
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
   t.traceBegin("startOtherServices");
   final Context context = mSystemContext;
   DynamicSystemService dynamicSystem = null;
   IStorageManager storageManager = null;
   NetworkManagementService networkManagement = null;
   IpSecService ipSecService = null;
   VpnManagerService vpnManager = null;
   VcnManagementService vcnManagement = null;
   NetworkStatsService networkStats = null;
   NetworkPolicyManagerService networkPolicy = null;
   NsdService serviceDiscovery = null;
   WindowManagerService wm = null;
   SerialService serial = null;
   NetworkTimeUpdateService networkTimeUpdater = null;
   InputManagerService inputManager = null;
   TelephonyRegistry telephonyRegistry = null;
   ConsumerIrService consumerIr = null;
   MmsServiceBroker mmsService = null;
   HardwarePropertiesManagerService hardwarePropertiesService = null;
   PacProxyService pacProxyService = null;
   ......
   //启动framwork Java InputManagerService
   t.traceBegin("StartInputManagerService");
   inputManager = new InputManagerService(context);
   t.traceEnd();
   .....
}

//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java 
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);
       //通过nativeInit
        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());
 }

//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp 
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;
    }
    //通过new NativeInputManager对象进行native framework 初始化,从而进入到inputfinger
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
    JNIEnv* env = jniEnv();

    mServiceObj = env->NewGlobalRef(serviceObj);

    {
        AutoMutex _l(mLock);
        mLocked.systemUiLightsOut = false;
        mLocked.pointerSpeed = 0;
        mLocked.pointerGesturesEnabled = true;
        mLocked.showTouches = false;
        mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
    }
    mInteractive = true;

    InputManager* im = new InputManager(this, this);
    mInputManager = im;
    defaultServiceManager()->addService(String16("inputflinger"), im);
}

//frameworks/native/services/inputflinger/InputManager.cpp
InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    //创建InputDispatcher,该对象主要用于事件分发
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    //
    mClassifier = new InputClassifier(mDispatcher);
    //创建InputReader,该对象主要用于事件读取
    mReader = createInputReader(readerPolicy, mClassifier);
}

//frameworks/native/services/inputflinger/reader/InputReaderFactory.cpp 
sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                           const sp<InputListenerInterface>& listener) {
    return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

//frameworks/native/services/inputflinger/reader/InputReader.cpp
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& listener)
      : mContext(this),
        mEventHub(eventHub),
        mPolicy(policy),
        mGlobalMetaState(0),
        mLedMetaState(AMETA_NUM_LOCK_ON),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        std::scoped_lock _l(mLock);

        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

//frameworks/native/services/inputflinger/reader/EventHub.cpp
EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {

       .....
}

初始化完成之后,通过一连串的start接口,开始Input子系统的运行

//frameworks/base/services/java/com/android/server/SystemServer.java 
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
  .....
  t.traceBegin("StartInputManager");
  inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
  //通过start开始运行
  inputManager.start();
  t.traceEnd();
  .....
}
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public void start() {
        Slog.i(TAG, "Starting input manager");
        //通过nativeStart进行启动
        nativeStart(mPtr);

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();
        registerAccessibilityLargePointerSettingObserver();
        registerLongPressTimeoutObserver();
        registerMaximumObscuringOpacityForTouchSettingObserver();
        registerBlockUntrustedTouchesModeSettingObserver();

        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");
        updateMaximumObscuringOpacityForTouchFromSettings();
        updateBlockUntrustedTouchesModeFromSettings();
}
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp 
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.");
    }
}
//frameworks/native/services/inputflinger/InputManager.cpp
status_t InputManager::start() {
    //开启dispatcher
    status_t result = mDispatcher->start();
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
    //开启reader
    result = mReader->start();
    if (result) {
        ALOGE("Could not start InputReader due to error %d.", result);
        mDispatcher->stop();
        return result;
    }
    return OK;
}
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
status_t InputDispatcher::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
    return OK;
}
//frameworks/native/services/inputflinger/reader/InputReader.cpp
status_t InputReader::start() {
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

在我们自上而下了解了整个Input系统的初始化与运行流程后,接下来我们针对每个单独的模块来进行分析,去了解这里面更多的细节。

事件读取-InputReader

从名字上我们就能大概知道Eventhub的具体作用,这里我们先看看Eventhub的初始化过程:

//frameworks/native/services/inputflinger/reader/EventHub.cpp
EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {
    ensureProcessCanBlockSuspend();
    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
    mINotifyFd = inotify_init();
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,
                        strerror(errno));
    if (isV4lScanningEnabled()) {
        mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
        LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
                            VIDEO_DEVICE_PATH, strerror(errno));
    } else {
        mVideoWd = -1;
        ALOGI("Video device scanning disabled");
    }
    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = mINotifyFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
    int wakeFds[2];
    result = pipe(wakeFds);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
                        errno);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
                        errno);
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                        errno);
}

在Eventhub初始化过程中,最重要的是利用了inotify与epoll两个系统调用。前者用于监听/dev/input下的input设备节点添加或者删除的变化,后者用于监听某个具体Input设备节点,具体可见代码:

//frameworks/native/services/inputflinger/reader/EventHub.cpp
EventHub::EventHub(void){
.....
    //通过inotify_init接口创建inotify实例,返回值为文件描述符
    mINotifyFd = inotify_init();
    //通过inotify_add_watch接口将/dev/input目录加入监听,返回值为另外的文件描述符,称为Watch 
    //descriptor,当存在input设备节点添加或删除时,在epoll中会监听到该信息
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
    LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH,strerror(errno));
    if (isV4lScanningEnabled()) {
        mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE);
        LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s",
                            VIDEO_DEVICE_PATH, strerror(errno));
    } else {
        mVideoWd = -1;
        ALOGI("Video device scanning disabled");
    }
......
}
//frameworks/native/services/inputflinger/reader/EventHub.cpp
status_t EventHub::readNotifyLocked() {
......
    ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd);
    //读取inotify event
    res = read(mINotifyFd, event_buf, sizeof(event_buf));
    if (res < (int)sizeof(*event)) {
        if (errno == EINTR) return 0;
        ALOGW("could not get event, %s\n", strerror(errno));
        return -1;
    }
    while (res >= (int)sizeof(*event)) {
        event = (struct inotify_event*)(event_buf + event_pos);
        if (event->len) {
            //如果inotify event中所携带的watch descriptor为对应的input设备目录
            //则进行CREATE 或 DELETE的判断
            if (event->wd == mInputWd) {
                std::string filename = std::string(DEVICE_PATH) + "/" + event->name;
                //如果是新建设备节点,进行处理
                if (event->mask & IN_CREATE) {
                    openDeviceLocked(filename);
                } else {
                    ALOGI("Removing device '%s' due to inotify event\n", filename.c_str());
                    closeDeviceByPathLocked(filename);
                }
}
......
}

在上面的代码中我们可以看到,当/dev/input目录下创建了新的设备节点时,将会进入openDeviceLocked函数,在该函数内会通过ioctl接口完成设备信息的读取:

void EventHub::openDeviceLocked(const std::string& devicePath) {
//open系统调用
int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK);
.....
// Get device name.获取设备名称
char buffer[80];
ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer)
// Get device driver version.获取驱动版本
int driverVersion;
ioctl(fd, EVIOCGVERSION, &driverVersion)
// Get device identifier.获取设备标识
struct input_id inputId;
ioctl(fd, EVIOCGID, &inputId)
// Get device physical location.
ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer)
// Get device unique id.
ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer)
// Load the configuration file for the device.加载Input设备配置
device->loadConfigurationLocked();
.....
device->configureFd();
.....
addDeviceLocked(std::move(device));
}

这里面比较重要的一些步骤包括Input设备的配置文件加载解析,针对特定设备的一些设置以及设备加载完成后的处理,重点包括loadConfigurationLocked函数、configureFd函数和addDeviceLocked函数,其源码如下:

//frameworks/native/services/inputflinger/reader/EventHub.cpp
void EventHub::Device::loadConfigurationLocked()
{
   configurationFile=getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier,
                                                                       InputDeviceConfigurationFileType::
                                                                             CONFIGURATION);
    if (configurationFile.empty())
    {
        ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str());
    }
    else
    {
        android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
                     PropertyMap::load(configurationFile.c_str());
        if (!propertyMap.ok())
        {
            ALOGE("Error loading input device configuration file for device '%s'.  "
                  "Using default configuration.",
                  identifier.name.c_str());
        }
        else
        {
            configuration = std::move(*propertyMap);
        }
    }
}
//frameworks/native/libs/input/InputDevice.cpp 
std::string getInputDeviceConfigurationFilePathByDeviceIdentifier(
        const InputDeviceIdentifier& deviceIdentifier,
        InputDeviceConfigurationFileType type) {
    if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) {
        if (deviceIdentifier.version != 0) {
            // Try vendor product version.
            std::string versionPath = getInputDeviceConfigurationFilePathByName(
                    StringPrintf("Vendor_%04x_Product_%04x_Version_%04x",
                            deviceIdentifier.vendor, deviceIdentifier.product,
                            deviceIdentifier.version),
                    type);
            if (!versionPath.empty()) {
                return versionPath;
            }
        }

        // Try vendor product.
        std::string productPath = getInputDeviceConfigurationFilePathByName(
                StringPrintf("Vendor_%04x_Product_%04x",
                        deviceIdentifier.vendor, deviceIdentifier.product),
                type);
        if (!productPath.empty()) {
            return productPath;
        }
    }

    // Try device name.
    return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type);
}

//针对Keyboard键盘,设置repeatRate;针对Sensor设备,设置Timestamp的时钟源
void EventHub::Device::configureFd() {                                                                                                                                                                      
    // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type
    if (classes.test(InputDeviceClass::KEYBOARD)) {
        // Disable kernel key repeat since we handle it ourselves
        unsigned int repeatRate[] = {0, 0};
        if (ioctl(fd, EVIOCSREP, repeatRate)) {
            ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno));
        }    
    }    

    // Tell the kernel that we want to use the monotonic clock for reporting 
    // timestamps
    // associated with input events.  This is important because the input system
    // uses the timestamps extensively and assumes they were recorded using the 
    // monotonic clock.
    int clockId = CLOCK_MONOTONIC;
    if (classes.test(InputDeviceClass::SENSOR)) {
        // Each new sensor event should use the same time base as
        // SystemClock.elapsedRealtimeNanos().
        clockId = CLOCK_BOOTTIME;
    }    
    bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);
    ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
}

在Input设备的配置文件加载过程中,首先会在系统目录/system/product、/system/system_ext、/system/odm与/system/vendor下进行查找,查找时依据FILE_DIR+device_name+FILE_EXTENSION这样的形式进行,其中FILE_DIR定义次级目录的名称,其可选值为idc、keylayouts、keychars,相应的FILE_EXTENSION定义配置文件后缀名,其可选值为.idc、.kl、.kcm。这三种类型的配置文件,适用于不同类型的input设备:

*.kl:按键布局配置,用于将Linux 按键代码和坐标轴代码映射到 Android系统;通俗点来讲,就好比我们把键盘上的每一���按键都进行一个编号,该编号都有一个值与之对应,用户态拿到这个编号,也就知道了对应按下哪个键,由此完成后续处理。Android系统会在/odm/usr/keylayout、/vendor/usr/keylayout、/system/usr/keylayout、/data/system/devices/keylayout内进行查找,系统默认的配置文件位于 /system/usr/keylayout 内,如下所示:

trout_x86:/system/usr/keylayout # ls -la
total 692
drwxr-xr-x 2 root root 8192 2009-01-01 08:00 .
drwxr-xr-x 7 root root 4096 2009-01-01 08:00 ..
-rw-r--r-- 1 root root  811 2009-01-01 08:00 AVRCP.kl
-rw-r--r-- 1 root root 9453 2009-01-01 08:00 Generic.kl
-rw-r--r-- 1 root root  810 2009-01-01 08:00 Vendor_0079_Product_0011.kl
-rw-r--r-- 1 root root 1644 2009-01-01 08:00 Vendor_0079_Product_18d4.kl
-rw-r--r-- 1 root root 1645 2009-01-01 08:00 Vendor_044f_Product_b326.kl
-rw-r--r-- 1 root root 1543 2009-01-01 08:00 Vendor_045e_Product_028e.kl
-rw-r--r-- 1 root root 1644 2009-01-01 08:00 Vendor_045e_Product_028f.kl
-rw-r--r-- 1 root root 1548 2009-01-01 08:00 Vendor_045e_Product_02a1.kl
-rw-r--r-- 1 root root 1568 2009-01-01 08:00 Vendor_045e_Product_02d1.kl
-rw-r--r-- 1 root root 1568 2009-01-01 08:00 Vendor_045e_Product_02dd.kl
-rw-r--r-- 1 root root 1402 2009-01-01 08:00 Vendor_045e_Product_02e0.kl

其中General.kl是通用的,我们可以看看文件内的具体内容:

# Copyright (C) 2010 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.

#
# Generic key layout file for full alphabetic US English PC style external keyboards.
#
# This file is intentionally very generic and is intended to support a broad range of keyboards.
# Do not edit the generic key layout to support a specific keyboard; instead, create
# a new key layout file with the required keyboard configuration.
#

key 1     ESCAPE
key 2     1
key 3     2
key 4     3
key 5     4
key 6     5
key 7     6
key 8     7
key 9     8
key 10    9
key 11    0
key 12    MINUS
key 13    EQUALS
key 14    DEL
key 15    TAB

更多信息可以参考该链接

*.kcm:按键字符映射配置,用于将Android的按键键值(包含辅助键)映射为Unicode字符。也就是当你在键盘上按压某个键时,你在屏幕上所看到的具体字符(是A还是B,是A还是a等),Android12默认的字符映射配置位于/system/usr/keychars内,如下所示:

trout_x86:/system/usr/keychars $ 
trout_x86:/system/usr/keychars $ ls -la
total 88
drwxr-xr-x 2 root root  4096 2009-01-01 08:00 .
drwxr-xr-x 7 root root  4096 2009-01-01 08:00 ..
-rw-r--r-- 1 root root 15891 2009-01-01 08:00 Generic.kcm
-rw-r--r-- 1 root root  1234 2009-01-01 08:00 Vendor_18d1_Product_0200.kcm
-rw-r--r-- 1 root root  8318 2009-01-01 08:00 Vendor_18d1_Product_5018.kcm
-rw-r--r-- 1 root root 15666 2009-01-01 08:00 Virtual.kcm
-rw-r--r-- 1 root root 15840 2009-01-01 08:00 qwerty.kcm
-rw-r--r-- 1 root root 15959 2009-01-01 08:00 qwerty2.kcm
trout_x86:/system/usr/keychars $ 

其中Generic.kcm用于标准外部键盘的字符映射,Virtual.kcm用于虚拟键盘的字符映射。以Vendor开头的则属于硬件厂商所生产的键盘字符映射。我们可以打开看看文件内的具体内容:

# Copyright (C) 2010 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.

#
# Key character map for a built-in generic virtual keyboard primarily used
# for instrumentation and testing purposes.
#

type FULL

### Basic QWERTY keys ###

key A {
    label:                              'A'
    base:                               'a'
    shift, capslock:                    'A'
    shift+capslock:                     'a'
}

key B {
    label:                              'B'
    base:                               'b'
    shift, capslock:                    'B'
    shift+capslock:                     'b'
}

key C {
    label:                              'C'
    base:                               'c'
    shift, capslock:                    'C'
    alt:                                '\u00e7'
    shift+alt:                          '\u00c7'
    shift+capslock:                     'c'
}

更多信息可参考链接

*.idc:输入设备配置文件,包含设备专用的属性配置,这些通常是一些输入设备所特有的、非通用的配置,Android12中我们可以在/system/usr/idc目录下找到相关配置,如下所示:

trout_x86:/system/usr/idc $ ls -la
total 36
drwxr-xr-x 2 root root 4096 2009-01-01 08:00 .
drwxr-xr-x 7 root root 4096 2009-01-01 08:00 ..
-rw-r--r-- 1 root root  634 2009-01-01 08:00 AVRCP.idc
-rw-r--r-- 1 root root 1396 2009-01-01 08:00 Vendor_054c_Product_05c4.idc
-rw-r--r-- 1 root root 1396 2009-01-01 08:00 Vendor_054c_Product_09cc.idc
-rw-r--r-- 1 root root  782 2009-01-01 08:00 Vendor_0957_Product_0001.idc
-rw-r--r-- 1 root root  916 2009-01-01 08:00 Vendor_248a_Product_8266.idc
-rw-r--r-- 1 root root  869 2009-01-01 08:00 qwerty.idc
-rw-r--r-- 1 root root  870 2009-01-01 08:00 qwerty2.idc
trout_x86:/system/usr/idc $ 

以上是监听设备挂载、加载设备配置与内部实例化的过程。接下来我们看看事件读取的具体过程。事件的读取入口位于Eventhub.cpp的getEvents函数,这也是Eventhub中最为核心最为重要的函数,鉴于该函数比较复杂,我们将其分为几部分逐一进行分析。

1.首先是判断是否存在设备添加、移除等事件,如存在,则将这些事件上报至InputReader:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize){
    .....
    // 存在设备被关闭,进行上报, event->type = DEVICE_REMOVED
    for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();)
    {
        std::unique_ptr<Device> device = std::move(*it);
        ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str());
        event->when = now;
        event->deviceId = (device->id == mBuiltInKeyboardId) ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id;
        event->type = DEVICE_REMOVED;
        event += 1;
        it = mClosingDevices.erase(it);
        mNeedToSendFinishedDeviceScan = true;
        if (--capacity == 0)
        {
                break;
         }
        }
        if (mNeedToScanDevices)
        {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }
        //存在设备被打开,进行上报,event->type = DEVICE_ADDED;
        while (!mOpeningDevices.empty())
        {
            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
            mOpeningDevices.pop_back();
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            // Try to find a matching video device by comparing device names
            for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
                 it++)
            {
                std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
                if (tryAddVideoDeviceLocked(*device, videoDevice))
                {
                    // videoDevice was transferred to 'device'
                    it = mUnattachedVideoDevices.erase(it);
                    break;
                }
            }
            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
            if (!inserted)
            {
                ALOGW("Device id %d exists, replaced.", device->id);
            }
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0)
            {
                break;
            }
        }
        //设备扫描结束,进行上报,event->type = FINISHED_DEVICE_SCAN;
        if (mNeedToSendFinishedDeviceScan)
        {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0)
            {
                break;
            }
        }
        .....
}

这部分逻辑是因为我们在读取Event事件的时候随时会发生设备关闭、拔掉、出现异常或者新增设备等情况,所以当存在这些情况时我们需要优先进行处理,及时更新设备列表,才能保障后续的Input event读取。

2.正常读取内核上报的Input事件

   if (eventItem.events & EPOLLIN) {
....
       const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
       const size_t count = size_t(readSize) / sizeof(struct input_event);
       for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        device->trackInputEvent(iev);
                        events.push_back({
                                .when = processEventTimestamp(iev),
                                .readTime = systemTime(SYSTEM_TIME_MONOTONIC),
                                .deviceId = deviceId,
                                .type = iev.type,
                                .code = iev.code,
                                .value = iev.value,
                        });
                    }
                    if (events.size() >= EVENT_BUFFER_SIZE) {
                        // 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;
                    }
}
.....

这里会将从内核中读取到的事件存储在Vector内,并返回该Vector,跟��着调用链���,我们可以看到该方法在InputReader类中的loopOnce方法中被调用,获取到这些输入事件后,进入processEventsLocked方法内。

void InputReader::loopOnce() {
...
std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis);

  { // acquire lock
        std::scoped_lock _l(mLock);
        mReaderIsAliveCondition.notify_all();

        if (!events.empty()) {
            mPendingArgs += processEventsLocked(events.data(), events.size());
        }
  }
...
}

在后续的处理中,会跳转到InputDevice类中的process方法,在该方法中会通过InputMapper传递到各个子Mapper中,这里的每一个子Mapper都对应一个特定的输入设备,如键盘Keyboard(KeyboardInputMapper)、操纵杆Joystick(JoystickInputMapper)、Cursor(CursorInputMapper)等,以键盘输入为例,我们来看看针对键盘的输入具体是如何处理的:

//frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent& rawEvent) {
...
           if (isSupportedScanCode(scanCode)) {
                out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0, scanCode,
                                  mHidUsageAccumulator.consumeCurrentHidUsage());
            }
....
}

//

实际上每个子Mapper都会依照输入设备的特性对原始的Events改写一些数据,后续会通过notify方法将改写后的数据传递到InputDispatcher中,这一步又是如何实现的呢。

事件分发-InputDispatcher

首先我们来看看事件是如何从InputReader传递到InputDispatcher中的,其实就是在创建InputReader时将InputDispatcher的Listener传入,当接收到Input事件时即通过Listener进行后续处理,具体代码如下:

InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
                           InputDispatcherPolicyInterface& dispatcherPolicy,
                           PointerChoreographerPolicyInterface& choreographerPolicy,
                           InputFilterPolicyInterface& inputFilterPolicy) {

    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mTracingStages.emplace_back(
            std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher));
    .....
    mReader = createInputReader(readerPolicy, *mTracingStages.back());
}

这里TracedInputListener继承于InputListenerInterface,InputListenerInterface用于InputReader传递事件至InputDispatcher:

/*
 * The interface used by the InputReader to notify the InputListener about input events.
 */
class InputListenerInterface {
public:
    InputListenerInterface() { }
    InputListenerInterface(const InputListenerInterface&) = delete;
    InputListenerInterface& operator=(const InputListenerInterface&) = delete;
    virtual ~InputListenerInterface() { }

    virtual void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) = 0;
    virtual void notifyKey(const NotifyKeyArgs& args) = 0;
    virtual void notifyMotion(const NotifyMotionArgs& args) = 0;
    virtual void notifySwitch(const NotifySwitchArgs& args) = 0;
    virtual void notifySensor(const NotifySensorArgs& args) = 0;
    virtual void notifyVibratorState(const NotifyVibratorStateArgs& args) = 0;
    virtual void notifyDeviceReset(const NotifyDeviceResetArgs& args) = 0;
    virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) = 0;

    void notify(const NotifyArgs& args);
};

那么具体是在什么地方进行传递的呢,这里我们回到InputReader类的loopOnce方法:

void InputReader::loopOnce() {
    .....
    // 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.
    for (const NotifyArgs& args : notifyArgs) {
        mNextListener.notify(args);
    }
    ....

}

这里的mNextListener也就是InputDispatcher注册进来的TracedInputListener对象,通过notify方法进行Event分发:

void InputListenerInterface::notify(const NotifyArgs& generalArgs) {
    Visitor v{
            [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); },
            [&](const NotifyKeyArgs& args) { notifyKey(args); },
            [&](const NotifyMotionArgs& args) { notifyMotion(args); },
            [&](const NotifySwitchArgs& args) { notifySwitch(args); },
            [&](const NotifySensorArgs& args) { notifySensor(args); },
            [&](const NotifyVibratorStateArgs& args) { notifyVibratorState(args); },
            [&](const NotifyDeviceResetArgs& args) { notifyDeviceReset(args); },
            [&](const NotifyPointerCaptureChangedArgs& args) { notifyPointerCaptureChanged(args); },
    };
    std::visit(v, generalArgs);
}

这里实际会根据Event的类型不同进入到不同的notify子方法中,以Key事件为例子,最终我们进入到InputDispatcher中的notifyKey方法中:

void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
...
  needWake = enqueueInboundEventLocked(std::move(newEntry));
....

}

这里会将Input事件塞入队列,之后唤醒looper,通过dispatchOnce进行分发:

void InputDispatcher::dispatchOnce() {
...
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(/*byref*/ nextWakeupTime);
        }
....
}

之后会进入dispatchOnceInnerLocked方法,在这其中会根据Event类型不同进行分发,这里的Event类型包含DEVICE_RESET、FOCUS、TOUCH_MODE_CHANGED、POINTER_CAPTURE_CHANGED、KEY等等,这里我们还是以Key继续往下看:

case EventEntry::Type::KEY: {
....
      std::shared_ptr<const KeyEntry> keyEntry =
                    std::static_pointer_cast<const KeyEntry>(mPendingEvent);
      if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
                dropReason = DropReason::STALE;
      }
      if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
                dropReason = DropReason::BLOCKED;
      }
      done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
      break;
....
}

在事件分发的过程中,需要对应到具体的窗口,以便拥有该窗口的应用进行后续处理。这一步其实就是在dispatchKeyLocked中进行的:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry,
                                        DropReason* dropReason, nsecs_t& nextWakeupTime) {

  // Identify targets.
    Result<sp<WindowInfoHandle>, InputEventInjectionResult> result =
            findFocusedWindowTargetLocked(currentTime, *entry, nextWakeupTime);

    if (!result.ok()) {
        if (result.error().code() == InputEventInjectionResult::PENDING) {
            return false;
        }
        setInjectionResult(*entry, result.error().code());
        return true;
    }
    sp<WindowInfoHandle>& focusedWindow = *result;
    LOG_ALWAYS_FATAL_IF(focusedWindow == nullptr);

    setInjectionResult(*entry, InputEventInjectionResult::SUCCEEDED);

    std::vector<InputTarget> inputTargets;
    addWindowTargetLocked(focusedWindow, InputTarget::DispatchMode::AS_IS,
                          InputTarget::Flags::FOREGROUND, getDownTime(*entry), inputTargets);

    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));

    if (mTracer) {
        ensureEventTraced(*entry);
        for (const auto& target : inputTargets) {
            mTracer->dispatchToTargetHint(*entry->traceTracker, target);
        }
    }

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;

}

所有的步骤完成后,写入对应的Channel,应用就能接收到这些Input事件进行后续处理了。以按键事件为例,这里我们跳转到InputTransport类的publishKeyEvent方法:

status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
                                         int32_t source, ui::LogicalDisplayId displayId,
                                         std::array<uint8_t, 32> hmac, int32_t action,
                                         int32_t flags, int32_t keyCode, int32_t scanCode,
                                         int32_t metaState, int32_t repeatCount, nsecs_t downTime,
                                         nsecs_t eventTime) {
....
return mChannel->sendMessage(&msg);
}

以上就是整个事件读取与分发的全过程了,最后我们以下图来作为总结:

由于篇幅有限,这篇文章并未解析App是如何接收事件并进行处理的,后面有机会可以另外写一篇文章进行分析。

Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

文章: 93

留下评论

zh_CNCN