Physical Address:
ChongQing,China.
WebSite:
安卓系统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系统的初始化与运行流程后,接下来我们针对每个单独的模块来进行分析,去了解这里面更多的细节。
从名字上我们就能大概知道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中,这一步又是如何实现的呢。
首先我们来看看事件是如何从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是如何接收事件并进行处理的,后面有机会可以另外写一篇文章进行分析。