Android14输入系统架构分析:图解源码从驱动层到应用层的完整传递链路
- IT业界
- 2025-09-04 01:03:02

一、资料快车
1、深入了解Android输入系统: blog.csdn.net/innost/article/details/47660387
2、书籍 - Android系统源代码情景分析
二、Perface1、参考:
2、系统程序分析方法
1)加入log,并跟着log一步步分析 -logcat;
2)利用ChatGPT提供基础概念解析 & 代码解析 & 设计原理;
3、目标
1)提供查阅代码的线索、思路;
2)能够根据日志进行快读的代码分析;
3)区分代码层次,为定制系统提供思路;
4)站在前人的肩膀上进一步探究;
5)熟悉架构后,我们应能 如何添加自定义按键、拦截keyevent并添加自己的处理逻辑;
6)Android源代码兼容多种设备,比如一开始Android针对手机设计(因此很多代码都有手机的身影),后面适应不同的设备,不同设备的处理流程不同,注意分辨;
4、action
务必根据本文提供的线索 去看源代码。
带着疑问去了解
1、kl/kcm作用是什么?在哪里会被读取?
2、定义CVTE快捷键,应该怎么做?
3、如何跟中控和TVAPI联合起来?
三、代码目录 所在层次名称代码路径Application应用(调用系统库,实现用户逻辑)实现android中对应的类及方法,进行inputevent处理frameworkandroid (此部分链接到应用)/android/frameworks/base/services/java/com/android/server/SystemServer.java/android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java/android/frameworks/base/core/java/*/android/frameworks/base/core/java/com/android/internal/policy/DecorView.java/android/frameworks/base/core/java/android/app/Activity.java/android/frameworks/base/core/java/android/view/ViewRootImpl.java/android/frameworks/base/core/java/android/view/View.java/android/frameworks/base/core/java/android/view/KeyEvent.java/android/frameworks/base/core/java/android/view/InputChannel.javaservice (系统服务,服务端集中管理)Framework java基础服务/android/frameworks/base/services/java/com/android/server/SystemServer.javaFramework android核心服务/android/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javaJNI(过渡到native)一、libservices.core - 静态库模块/android/frameworks/base/services/core/jni//android/frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp二、libandroid_runtime - 静态库模块/android/frameworks/base/core/jni//android/frameworks/base/core/jni/android_view_InputEventReceiver.cppNDK一、libinput - 静态库模块/android/frameworks/native/libs/input//android/frameworks/native/libs/input/InputTransport.cpp二、libinputflinger/libinputflinger_base - 动态库模块/android/frameworks/native/services/inputflinger//android/frameworks/native/services/inputflinger/InputManager.cpp三、lib/android/system/core/libutils/*/android/system/core/libutils/Looper.cppcvte/android/frameworks/base/core/java/com/cvte/key/*kernelinput子系统 四、系统框架流程1、粗略框图 - 来自深入了解Android
2、代码层级
3、framework部分
4、下面针对各个部分进行拆解分析
五、基础概念 1、APP界面构成关系1)Application 、Activity、Windows 、Decro(ration) 、View
一个Application有多个Activity,Activity中的界面部分为Windows(PhoneWindow),是我们所看到的当前整个界面,windows界面由Decro(样式) 来描述,Decro由各种view组件(TextView、button)组成。
2)从代码角度看Decro与View的树状关系
3)小结:
android里: 1个application, 有1个或多个activity 1个activity, 有1个PhoneWindow 1个window, 有1个decor 1个decor, 有多个viewgroup/layout viewgroup/layout中, 有多个view
2、Activity组件与ViewRoot对象的关系
Android设计思想 - 抽象分工
一个应用程序窗口是由一个Activity来描述(描述控件组成、布局等等)- 策略者;
每一个Activity组件都有一个关联的ViewRoot对象(除了绘制具体的窗口,还负责分发键盘事件) - 具体实施者;
Activity和ViewRoot通过DecorView和PhoneWindow对象来关联起来;
需要跟踪代码才能掌握理解以上关系
Android-Activity与Window与View之间的关系: github /jeanboydev/Android-ReadTheFuckingSourceCode/blob/master/article/android/framework/Android-Activity%E4%B8%8EWindow%E4%B8%8EView%E4%B9%8B%E9%97%B4%E7%9A%84%E5%85%B3%E7%B3%BB.md
2、输入事件输入事件有多种类型(触摸、拖动、鼠标、按键等等)
3、key相关概念1)*.kl : keylayout,linux的scancode对应的Android按键映射;
2)*.kcm:key code map,Android按键对应的字符映射;
3)*.idc : input device configutation 输入设备配置文件;
以上文件在板卡都可以找到
4、socketpair 与 bindersocketpair 与 binder 为Android的跨进程通信技术模块,两者结合可以实现双向的跨进程通信。
5、inotify和epollinotify模块:用于监测某目录下的文件变化
epoll模块:用于监测文件内容变化
六、Linux kernel 1、input输入子系统 2、IR驱动blog.csdn.net/STCNXPARM/article/details/134235394
七、Andorid系统部分 1、WMS 2、InputManagerService(java)java层的InputManagerService对native层的InputManager进一步封装,很多实现都在native层。
1)Reader(CPP) 1、Reader角色 Reader主要工作 1)从设备里读取输入事件; 2)根据kcm/kl文件,进行按键映射处理; 3)将RawEvent传递给dispatcher处理; 这块代码比较固定,也很少去适配,了解下即可 android\frameworks\native\services\inputflinger\* 1、InputReader对象启动线程 android\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; } 2、InputThread对,提供一个线程模板 android\frameworks\native\services\inputflinger\InputThread.cpp InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake) : mName(name), mThreadWake(wake) { mThread = sp<InputThreadImpl>::make(loop); mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); } 3、读取输入事件 //主要使用inotify监测/dev/input,使用epoll监测有无数据 void InputReader::loopOnce() { //作为函数参数传给InputThread std::vector<RawEvent> events = mEventHub->getEvents(timeoutMillis); //读取event设备节点 ... mQueuedListener.flush(); //flush队列,通知dispatcher线程 ... } 2、InputReader和EventHub的分工 1)InputReader(上层角色)1)如果设备刚接入,则先addDeviceLocked() 添加设备
2)如果设备已存在且有输入事件产生,则processEventLocked()
3)处理事件对象,这里以KeyboardInputMapper为例
2)EventHub(底层角色)EventHub读取设备节点,获取输入事件RawEvent
1)读取输入设备驱动信息(底层原理是使用inotify/epoll机制进行监测),加载idc/kcm/kl文件(驱动设备提供路径);
2)最终转换成RawEvent,交给InputReader继续处理;
EventHub是linux device交互的功能类
1、openDeviceLocked android\frameworks\native\services\inputflinger\reader\EventHub.cpp void EventHub::openDeviceLocked(const std::string& devicePath) { ... //打开设备 int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); if (fd < 0) { ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno)); return; } ... //ioctl得到信号 // Get device name. if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name = buffer; } ... //加载idc//kl/kcm if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK | InputDeviceClass::SENSOR)) { // Load the keymap for the device. keyMapStatus = device->loadKeyMapLocked(); } } 3)InputReader和EventHub的分工介绍(分层思想) 3、复杂的底层数据结构介绍1)RawEvent/input_event/Device/InputDevice
1、android\frameworks\native\services\inputflinger\reader\include\EventHub.h struct RawEvent { // // Time when the event happened nsecs_t when; // Time when the event was read by EventHub. Only populated for input events. // For other events (device added/removed/etc), this value is undefined and should not be read. nsecs_t readTime; int32_t deviceId; int32_t type; int32_t code; int32_t value; }; 2、include/linux/input.h struct input_event{ struct timeval time; __u16 type; __u16 code; __s32 value; }; 3、Device //注意不是linux device,不要混淆 android\frameworks\native\services\inputflinger\reader\include\EventHub.h struct Device { int fd; // may be -1 if device is closed const int32_t id; const std::string path; const InputDeviceIdentifier identifier; //输入设备的基本信息 ... std::string configurationFile; //对应idc配置文件 std::unique_ptr<PropertyMap> configuration; std::unique_ptr<VirtualKeyMap> virtualKeyMap; KeyMap keyMap; //对应两种配置文件,kcm->kl,先找kcm映射,找不到再找kl映射 } 关于Device信息,Android上可以敲dumpsys input查看所有的input设备信息 定义 F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\EventHub.h std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices; 4、 F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\InputDevice.h class InputDevice { //绑定EventHub的Device,目的是封装Device的复杂操作,给上层提供更方便的操作 private: InputReaderContext* mContext; int32_t mId; int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; std::unordered_map<int32_t, DevicePair> mDevices; } //定义 F:\1.code\MTK9256_AN14\android\frameworks\native\services\inputflinger\reader\include\InputReader.h std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices GUARDED_BY(mLock); 5、 /* Types of input device configuration files. */ enum class InputDeviceConfigurationFileType : int32_t { CONFIGURATION = 0, /* .idc file */ KEY_LAYOUT = 1, /* .kl file */ KEY_CHARACTER_MAP = 2, /* .kcm file */ };2)KeyMap-KeyLayoutMap-KeyCharacterMap
1、 F:\1.code\MTK9256_AN14\android\frameworks\native\include\input\Keyboard.h class KeyMap { public: std::string keyLayoutFile; std::shared_ptr<KeyLayoutMap> keyLayoutMap; std::string keyCharacterMapFile; std::shared_ptr<KeyCharacterMap> keyCharacterMap; } 2、 class KeyLayoutMap { private: struct Key { int32_t keyCode; uint32_t flags; }; } kl文件格式举例 key 305 BUTTON_B // key <linux key> <Android key> 3、 class KeyCharacterMap { private: struct Behavior { /* The meta key modifiers for this behavior. */ int32_t metaState = 0; /* The character to insert. */ char16_t character = 0; /* The fallback keycode if the key is not handled. */ int32_t fallbackKeyCode = 0; /* The replacement keycode if the key has to be replaced outright. */ int32_t replacementKeyCode = 0; }; struct Key { /* The single character label printed on the key, or 0 if none. */ char16_t label = 0; /* The number or symbol character generated by the key, or 0 if none. */ char16_t number = 0; /* The list of key behaviors sorted from most specific to least specific * meta key binding. */ std::list<Behavior> behaviors; }; class Parser {} //加载kcm文件时用此class来解析 } kcm文件格式举例: 1、 key B { label: 'B' # 印在按键上的文字 base: 'b' # 如果没有其他按键(shift, ctrl等)同时按下,此按键对应的字符是'b' shift, capslock: 'B' # 组合按键,如shift+base='B', capslock+base='B' } 2、 key SPACE { label: ' ' base: ' ' alt, meta: fallback SEARCH #组合按键 ctrl: fallback LANGUAGE_SWITCH #组件按键 } 解析组合按键构造三个Behavior Behavior.metaState=alt Behavior.fallbackKeyCode=AKEYCODE_SEARCH Behavior.metaState=meta Behavior.fallbackKeyCode=AKEYCODE_SEARCH Behavior.metaState=ctrl Behavior.fallbackKeyCode=AKEYCODE_LANGUAGE_SWITCH 放到链表std::list<Behavior> behaviors3)涉及的数据结构总览
2)Dispatcher(CPP) 1、Dispatcher的工作要点解析
1)输入事件之按键分类: Global key(一键启动 - 快捷键) system key(静音、音量、home键) user key(普通输入按键 ) 2)inboundQueue/outboundQueue(双端队列 - 用于储存输入事件); 出队:mInboundQueue.pop_front(); 入队:mInboundQueue.push_front(); 3)mCommandQueue(用于储存Command): 出队:mCommandQueue.pop_front(); 入队:mCommandQueue.push_back(command); 2、代码时序图输入事件有多种类型(触摸、拖动、鼠标等等),这里以输入按键为例(从底层->上层)
3、关键点分析 1)reader与dispatcher的交互实现1、总体框图
InputFlinger进程
android\frameworks\native\services\inputflinger\host\main.cpp2、InputManager的构造函数中建立联系,通过mQueuedListener,Reader获得Dispatcher的指针;
android\frameworks\native\services\inputflinger\InputManager.cpp /** * The event flow is via the "InputListener" interface, as follows: * InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher */ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mProcessor = std::make_unique<InputProcessor>(*mDispatcher); mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mProcessor); mReader = createInputReader(readerPolicy, *mBlocker); }3、处理流 InputReader -> UnwantedInteractionBlocker -> InputProcessor -> InputDispatcher
类图
4、InputListenerInterface
android\frameworks\native\services\inputflinger\InputListener.cpp 1、根据NotifyArgs的类型执行对应的函数-泛型思想 void InputListenerInterface::notify(const NotifyArgs& generalArgs) { Visitor v{ [&](const NotifyInputDevicesChangedArgs& args) { notifyInputDevicesChanged(args); }, [&](const NotifyConfigurationChangedArgs& args) { notifyConfigurationChanged(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); } void QueuedInputListener::notifyKey(const NotifyKeyArgs& args) { traceEvent(__func__, args.id); mArgsQueue.emplace_back(args); } 2、flush - 逐个将mArgsQueue中的事件通过队列传递给dispatcher处理 - 泛型的简洁 void QueuedInputListener::flush() { for (const NotifyArgs& args : mArgsQueue) { mInnerListener.notify(args); } mArgsQueue.clear(); } 2)高优先级的按键处理介绍在PhoneWindowManager.java类中会两个地方会对输入按键进行干预处理
为什么这样设计?因为需要优先处理高优先级的按键(如global/system key)
1、mPolicy.interceptKeyBeforeQueueing();
1)分类-global / system /user key
2)处理紧急事件-比如来电显示
2、mPolicy.interceptKeyBeforeDispatching()
1)响应处理global/system key
2)user key则正常传递到app处理
3、在frameworks中进行按键拦截,也主要在PhoneWindowManager下面两个函数加逻辑
interceptKeyBeforeDispatching()
interceptKeyBeforeQueueing()
3)不同事件按键的处理介绍 不同事件按键会走不同的代码分支,这里介绍下重点 场景分析1-global key(如AKEYCODE_TV - 快捷键) 场景分析2-system key(如AKEYCODE_VOLUME_DOWM - 音量按键) 场景分析3-user key(如AKEYCODE_A、上下左右 - 普通按键) 一、第一次预处理-interceptKeyBeforeQueueing interceptKeyBeforeQueueing的处理结果会储存到事件结构体entry中,后续在InputDispatcher.cpp处理会用到 android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java interceptKeyBeforeQueueing entry->interceptKeyResult == KeyEntry::InterceptKeyResult::UNKNOWN //对于没被处理的输入按键事件都是UNKNOWN & POLICY_FLAG_PASS_TO_USER entry->policyFlags & POLICY_FLAG_PASS_TO_USER 二、第二次预处理 android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java interceptKeyBeforeDispatching 1、对于system key直接处理,如 switch(keyCode) { case KeyEvent.KEYCODE_HOME: return handleHomeShortcuts(displayId, focusedToken, event); case KeyEvent.KEYCODE_MENU: ... } return key_consumed; 2、对于global key会发送广播,如 if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { return key_consumed; } return key_consumed; 3、对于user key, 在此函数内不处理,直接返回 return key_not_consumed; 三、第二次预处理后的几种返回值 android/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp void InputDispatcher::doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, KeyEntry& entry) { const KeyEvent event = createKeyEvent(entry); nsecs_t delay = 0; { // release lock scoped_unlock unlock(mLock); android::base::Timer t; delay = mPolicy.interceptKeyBeforeDispatching(focusedWindowToken, event, entry.policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", std::to_string(t.duration().count()).c_str()); } } // acquire lock if (delay < 0) { entry.interceptKeyResult = KeyEntry::InterceptKeyResult::SKIP; //对应system key/global key } else if (delay == 0) { entry.interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; //对应user key } else { entry.interceptKeyResult = KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER; entry.interceptKeyWakeupTime = now() + delay; } } 四、user key的处理 android/frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp 对于user key而言,dispatchOnce会执行两次,其中第一个循环用于处理global/system key(因为优先级更高一些),第二循环则处理user key(发布到app),代码设计如此,可自行根据代码琢磨。 if (runCommandsLockedInterruptable()) { nextWakeupTime = LLONG_MIN; } mLooper->pollOnce(timeoutMillis); //下一轮循环将处理user key,最终发布到app 4)涉及的Server和Service介绍 1、SystemServer 2、 3、WindowManagerService wm = WindowManagerService.main(context, inputManager, !mFirstBoot, new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); 4、 public class PhoneWindowManager implements WindowManagerPolicy { static final String TAG = "WindowManager"; } 5、LocalServices.addService(WindowManagerPolicy.class, mPolicy); 3、Reader和Dispatcher的创建和启动了解到Reader和Dispatcher的作用,再来从宏观上看看创建和启动过程
1)流程图注意,dispatcher线程要优先与reader线程启动,那样保证消息能第一次时间被处理
3、SystemServer开机流程:Android系统源码开机启动过程
/android/frameworks/base/services/java/com/android/server/SystemServer.java 八、APP与输入系统的联系 1、总体交互框图1)SystemServer通过(connect →inputchannel → socketpair) 将输入事件传递给app
2、inputchannel 1、场景分析 - inputchannel创建过程分析 1)流程图 2)值得注意的点 1、APP远程调用WM服务 产生远程调用的语句:mWindowSession.addToDisplay()细节分析 追溯下mWindowSession 1、构造函数中又调用自身的构造函数 mWindowSession在构造函数中赋值 public ViewRootImpl(Context context, Display display) { this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout()); } public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, WindowLayout windowLayout) { mContext = context; mWindowSession = session; ... } 2、获取server端session android\frameworks\base\core\java\android\view\WindowManagerGlobal.java public static IWindowSession getWindowSession() { synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { // Emulate the legacy behavior. The global instance of InputMethodManager // was instantiated here. // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary(); IWindowManager windowManager = getWindowManagerService(); sWindowSession = windowManager.openSession( //获取session句柄,后续使用session访问server接口 new IWindowSessionCallback.Stub() { @Override public void onAnimatorScaleChanged(float scale) { ValueAnimator.setDurationScale(scale); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } return sWindowSession; } } public static IWindowManager getWindowManagerService() { synchronized (WindowManagerGlobal.class) { if (sWindowManagerService == null) { sWindowManagerService = IWindowManager.Stub.asInterface( //binder 中 client调用server接口 ServiceManager.getService("window")); ... } return sWindowManagerService; } } 3、哪里会new ViewRootImpl实例? android\frameworks\base\core\java\android\view\WindowManagerGlobal.java mGlobal.addView()中会new一个ViewRootImpl实例 root = new ViewRootImpl(view.getContext(), display); 小结:如何找Framework中的远程调用?Binder细节很多,我们只需要关注以下几点即可找到 1、client调用:mWindowSession.addToDisplay() 2、根据IWindowManager类推断出存在aidl文件:IWindowSession.aidl -> out目录下找IWindowSession.java 3、确定server 函数位置:server端必定会extends :IWindowSession.Stub,并根据函数名来双重确定 java/C++层的inputChannel java/C++层都对应一个 inputChannel类(包含socketpair fd),用于参数传递 1、java层 android\frameworks\base\core\java\android\view\InputChannel.java 2、C++层 android\frameworks\native\libs\input\InputTransport.cpp 3、JNI中的转换 jobject inputChannelObj = android_view_InputChannel_createJavaObject(env, std::move(*inputChannel)); C++层inputchannel类 声明:android\frameworks\native\include\input\InputTransport.h 定义:android\frameworks\native\libs\input\InputTransport.cpp 继承于Parcelable,即用于进程间通信的类 1、几个变量 std::string mName; android::base::unique_fd mFd; //保存socker文件句柄 sp<IBinder> mToken; 2、创建client server 对应的两个inputChannel,及对应两个socker static status_t openInputChannelPair(const std::string& name, std::unique_ptr<InputChannel>& outServerChannel, std::unique_ptr<InputChannel>& outClientChannel); 3、status_t sendMessage(const InputMessage* msg); //service向fd写msg(按键信息) 4、status_t receiveMessage(InputMessage* msg); //client读fd的msg(按键信息) sockerpair 传递 android\frameworks\native\libs\input\InputTransport.cpp 1、返回给应用程序的fd需要深拷贝,否则返回后会被释放 base::unique_fd InputChannel::dupFd() const { android::base::unique_fd newFd(::dup(getFd())); ... } return newFd; } 3、APP监听输入事件APP启动时会,先获取到connection,再设置启动监听输入事件,流程如下
4、Looper将输入事件传递给APP1)从底层传递到上层,流程图如下
2)重点讲解
1、Response、Request结构体 struct Response { SequenceNumber seq; int events; Request request; }; struct Request { int fd; int ident; int events; sp<LooperCallback> callback; void* data; uint32_t getEpollEvents() const; }; 2、onInputEvent()是java层处理输入事件的总入口 九、APP层的处理 1、stage在onInputEvent()函数处理中,还有一系列的传递,用stage来表示各个阶段的处理,其中有针对系统输入法(InputMethodManager)的处理
1)术语 1、ime :input method(可以理解为系统内置输入法),实现
android/frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
经过输入法处理(app),输出的应是一个kcm对应的字符。
2、synthetic - 综合处理
2)Android层-ViewrootImpl
当前焦点应用的ViewrootImpl对象会收到该消息,并对消息进行分发处理,最终将其发送到对应的View对象中进行界面响应。
3)注意,对于按键类事件:
1、我们关注ime之后的即可;
2、view处理不了再传递给Activity处理;
3、stage主要关注ViewPreImeInputStage/ViewPostImeInputStage;
2、Framework层的类图1)跟踪代码需要理清以上类的关系
3、代码时序图重点说明
1、mView/mFocused 以TextView为例
2、按下一个按键的过程(APP)
完成一次按键,按下再松开
按下事件:onkeypreIme -> onkey(setOnKeyListener) -> onkeydown(view) → onkeydown(activity)
松开事件:onkeypreIme -> onkey(setOnKeyListener) -> onkeyup(view) ->onkeyup (activity)
3、mView一般是一个DecorView (extends View) 表示
Activity.java
ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes());
4、A1 没处理交给 A2,依次类推A3 A4;
十、项目应用 1、系统定制部分-中控-TVAPI1)从整体的输入系统可知,可以在PhoneWindowManager 中interceptKeyBeforeQueueing/interceptKeyBeforeDispatching加入截取逻辑;
2)PhoneWindowManager 加入中控钩子截取处理,中控处理不了再调用tvapi接口继续处理
3)中控是为了进一步优化系统性能,避免过多的按键影响性能(从整个输入系统来看,流程是相当之多和复杂的,代价就是耗时);
4)在没有中控之前,直接在PhoneWindowManager远程调用tvapi;
1、 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { ... //CVTE Android Patch Begin if(com.cvte.os.CtvManagerInternal.callGeneric(com.cvte.os.ICtvDataDefine.WHAT_COMMON_HOTKEYMANAGER, "cvteInterceptKeyBeforeQueueing", false, event)){ return 0; } //CVTE Android Patch End } android\vendor\cvte\cvte-central-control\src\com\cvte\server\manager\CvteHotkeyManager.java public boolean cvteInterceptKeyBeforeQueueing(Object... obj) { ... //中控能处理的先处理,处理不了再转发给tvapi if (mHotkeyService != null) { if(event.getKeyCode() == CtvPerformanceService.CVTE_KEYEVENT_BASE){ LLog.i(TAG,"TvApi cvteInterceptKeyBeforeQueueing CvtKeyEvent.KEYCODE_UNKNOWN no need process"); return true;//skip CvtKeyEvent.KEYCODE_UNKNOWN, no need process } try { long begin = SystemClock.uptimeMillis(); boolean ret = mHotkeyService.handleKeyEventsBefore(event) || (IS_KERNEL_AT && isKeyPadEvent(event)); //转到tvapi处理 LLog.i(TAG, "TvApi cvteInterceptKeyBeforeQueueing " + KeyEvent.keyCodeToString(event.getKeyCode()) + " : " + KeyEvent.actionToString(event.getAction()) + ", ret:" + ret + ", cost:" + (SystemClock.uptimeMillis() - begin) + "ms"); return ret; } catch (Exception e) { LLog.e(TAG, "Unknown Exception", e); } } else { bindService(); return isIntercept(event) || (IS_KERNEL_AT && isKeyPadEvent(event)); } } 2、 public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags) { //CVTE Android Patch Begin if (com.cvte.os.CtvManagerInternal.callGeneric(com.cvte.os.ICtvDataDefine.WHAT_COMMON_HOTKEYMANAGER,"cvteInterceptKeyBeforeDispatching", false, event)) { return -1; } } android\vendor\cvte\cvte-central-control\src\com\cvte\server\manager\CvteHotkeyManager.java public boolean cvteInterceptKeyBeforeDispatching(Object... obj) { KeyEvent event = (KeyEvent) obj[0]; ... //中控能处理的先处理,处理不了再转发给tvapi if (mHotkeyService != null) { try { // LLog.d(TAG, "enter cvteInterceptKeyBeforeDispatching onGlobalKeyEvent"); long begin = SystemClock.uptimeMillis(); boolean ret = mHotkeyService.handleKeyEvents(event); //转到tvapi处理 LLog.i(TAG, "TvApi KeyBeforeDispatching " + KeyEvent.keyCodeToString(event.getKeyCode()) + " : " + KeyEvent.actionToString(event.getAction()) + ", ret:" + ret + ", cost:" + (SystemClock.uptimeMillis() - begin) + "ms"); return ret; } catch (Exception e) { LLog.e(TAG, "Unknown Exception", e); } } else { bindService(); return isIntercept(event); } return false; } 2、CVTE - 无焦点应用优化处理 1、android\frameworks\base\core\java\android\view\ViewRootImpl.java private int processKeyEvent(QueuedInputEvent q) { final KeyEvent event = (KeyEvent)q.mEvent; //cvte add begin if(FocusAdapterUtil.getInstance().reflectProcessKeyEventMethodCvte(mView, event)){ return FINISH_HANDLED; } //cvte add end } 2、 android/frameworks/base/core/java/com/cvte/specialapkviewfocus/FocusAdapterUtil.java android/frameworks/base/core/java/com/cvte/specialapkviewfocus/CvteSpecialApkViewFocusHandlerImpl.java 这个类的主要功能是处理特定海外主流APK在某些界面上没有焦点的问题。代码中提到了多个特定的应用程序,如Amazon PrimeVideo、Netflix、Gaana和Twitter等,并为这些应用提供了特定的焦点处理逻辑。 比如处理Netflix的主页和播放界面焦点问题,Amazon PrimeVideo的播放和主页焦点问题,以及Gaana和Twitter的登录界面焦点问题,它通过模拟用户操作和系统事件来解决焦点问题,提升用户体验。 3、增加按键映射tvgit.gz.cvte /q/message:PT230025-77
4、APP应用编写代码只需简单两步即可实现input事件处理
1、实现onKey处理逻辑 public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { public interface OnKeyListener { boolean onKey(View v, int keyCode, KeyEvent event); } } 2、setOnKeyListener(new View.OnkeyListener(){....}); 十一、调试 1、getevent(linux) linux_key 遥控码 /dev/input/event1: 0004 0004 0088908d /dev/input/event1: 0001 0071 00000001 getevent -l /dev/input/event1: EV_MSC MSC_SCAN 00889011 /dev/input/event1: EV_KEY KEY_MUTE DOWN IR:底层没有配置,则为KEY_UNKNOW BT:如果缺少hid,会设为默认值Linux ( KEY_UNKNOWN ),此时需要去配HID 2、sendevent (linux) sendevent <目标设备号> <码值> sendevent /dev/input/event5 1 42 1 3、dumpsys (android) dumpsys input //获取当前设备使用的kl和kcm 注意:IR和BT吃的kl不一样,连接蓝牙设备后 dumpsys input会变! 4、input (linux) input keyevent "num" //模拟遥控器、键盘、鼠标各种输入设备操作Android14输入系统架构分析:图解源码从驱动层到应用层的完整传递链路由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Android14输入系统架构分析:图解源码从驱动层到应用层的完整传递链路”
下一篇
【记忆化搜索】猜数字游戏Ⅱ