android power key 按下到 通知PowerManager亮灭屏的流程

本文深入探讨了Android系统中Input Manager Service的内部运作原理,包括关键组件如InputManagerService、EventHub、InputReader等的交互过程,以及如何处理键盘输入事件,特别是Power按键的响应流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

按键和输入有关,就涉及到InputManagerService。

有三个文件:inputManager.java,InputManagerService.java,inputManager.cpp.

InputManager.java在framwork/base/core/java/android/hardware/input/

InputManagerService.java在framwork/base/core/java/com/android/server/input

inputManager.cpp在framworks\native\services\inputflinger\

InputManager.java是客户端,通过binder向InputManagerService.java请求服务。InputManagerService.java是一个binder服务。InputManagerService.java封装cpp层的inputManager.cpp,并向其提供会回调。

SystemServer.java 在startOtherSerivce中,初始化InputManagerService,并调用器start函数。InputManagerService的构造函数中调用nativeInit().

nativeInit是什么函数呢?它有对应的native c++函数。

查找规律如下:

InputManagerService的完整包名如下:

com.android.service.input.InputManagerService.java。

将所有 .  修改为 _ ,java改为cpp。  即com_android_service_input_InputManagerService.cpp。 此文件在framwork/base/service/core/jni目录中。

InputManagerService.java的nativeInit(),对应的就是com_android_service_input_InputManagerService.cpp中的nativeInit().在此函数中初始化了NativeInputManager。NativeInputManager构造函数中初始化了InputManager.

在NativeInputManager的构造过程中,会创建一个EventHub实例,并且把这个EventHub作为参数来创建InputManager对象。EventHub类是真正执行监控键盘事件操作的地方。android是基于linux kernel的,linux的事件获取需要读/dev/input下的设备文件节点。就是EventHub读取/dev/input节点。

从EventHub.cpp和InputManager.cpp开始,目录变成framworks\native\services\inputflinger\:

其中有两个InputDispatcher,InputReader成员。其参数 dispatcherPolicy和readerPolicy其实都是NativeInputManager。

InputDispatcher类是负责把输入消息分发给当前激活的Activity窗口的,而InputReader类则是通过EventHub类来实现读取输入事件的。键盘的输入消息也是InputDispatcher负责处理。

Eventhub类:

Android所有的input设备都会在/dev/input目录下生成对应的设备节点,一旦有任何输入事件产生,便会将事件写到这些节点下,同时对于外部输入设备(鼠标键盘等)的插拔还会引起这些节点的创建和删除。通Inotify和epoll实现对输入设备和输入事件的监控。上面代码中Inotify检测文件系统IN_DELETE,IN_CREATE,即创建和删除,在这里就监测是输入设备打开或者删除的事件。epoll监测多个文件(有无数据供读出、有无空间供写入)。两个如何关联?通过Inotify,输入设备插拔,将写入mInotifyFd文件。通过epoll,mEpollFd监控mInotifyFd的EPOLLIN,即读事件。所以只需要用epoll_wait查询mEpollFd,只要输入设备插拔都会退出epollWait阻塞,进行具体的工作。

mEpollFd还监控了读管道mWakeReadPipeFD。对应的写管道eventHub.wake()函数写入mWakeReadPipeFD。eventHub在nativeInputManger中作为参数传给inputManager,最终传给inputReader。inputRead会调用eventHub.wake(),触发eventHub的getevents()中的epoll_wait退出阻塞。

inotify详见:inotify_add_watch(2) - Linux manual page  等。

epoll 详见epoll_ctl(2) - Linux manual page 等。

mInotifyFd和mWakeReadPipeFD有数据都会通知mEpollFd。如何区分哪个文件有数据呢?eventHub中通过给epoll_ctl()的第三个参数eventItem的data参数指定不同的值来区分。

目前只是检测了输入设备的插拔,如何获取设备的各种输入呢?

围绕mEpollFd搜索代码,原来在registerDeviceForEpollLocked。顺便说下,最近看代码framwork层的WMS,PMS以及这个input模块,发现有很多函数都以Locked,原来以为是有逻辑上的含义,后面领悟到,原来是提示调用的人需要加锁调用。

registerDeviceForEpollLocked中添加mEpollFd对输入设备的读监听。当设备插入,或使能设备,会触发这个函数。

以设备插入为例,讲如何触发registerDeviceForEpollLocked()的。就涉及到EventHub中一个重要的函数getEvents().后面会讲getEvents()是如何进入的。getEvents()通过epoll_wait监听和处理设备插拔(mInotifyFd),inputreader唤醒(mWakeReadPipeFD),输入设备的输入事件(device->fd)。

一开始是不能监听设备的输入事件的,要先检测到设备的插入。

先看getEvents的最上层逻辑(删除了其他细节),首先是一个无线for循环。通过epoll_wait获得通知,通知放入mPendingEventItems中。

在最上层的for循环里,有一个while循环处理mPendingEventItems。当有设备插入,将mpendingINotify设置为true。

将所有的pendingEvent处理完后,通过readNotifyLocked() 从mINotifyFd中读取数据,循环处理inotify_event,将dev/input/和event->name拼合为devname(准确说是设备文件名,而不是设备名),作为入参调用openDeviceLocked().

openDeviceLocked通过ioctl(devicepath:dev/input/eventX为入参)获取输入设备的各种信息,对devcie变量初始化和赋值。

然后通过registerDeviceForEpolledLocked()监控dev/input/eventX状态,将device添加到mDevices向量里。

监听设备输入文件后,再回来看getEvents函数。函数从设备输入文件读取数据,一个一个读出input_event,转换成RawEvent赋给入参buffer。

从上面可以看出eventHub维护设备列表,读取输入事件。但是eventHub并不处理输入事件,而是通过getEvents返回给入参buffer。

接下来就要看谁调用的getEvents?是InputReader的loopOnce()调用。接着InputReader经过如下路径。

loopOnce()-> processEventLocked()-> processEventsForDeviceLocked()

然后进入InputDevice类的process()处理。

InputDevice如何被添加到mDevices向量中?当eventtype为Device_ADDED时loopOnce()-> processEventLocked()->addDeviceLocked()中的mDevices.add().

InputDevice类的process()函数调用inputMapper类的process函数处理。

inputMapper又是如何来的?loopOnce()-> processEventLocked()->addDeviceLocked()->createDeviceLocked()调用device->addMapper().

当设备是按键时,createDeviceLocked()调用device->addMapper()时会添加一个keyboardInputMapper。

那么现在进入keyboardMapper的process函数。keyboardMapper,InputDevice都在InputReader.cpp文件定义。

最终通过process  -> processKey() ->  getListener()->notifyKey(). 

此Listener是什么呢?回忆一下:

 

所以这个Listener就是InputDispatcher。

终于到了InputDispatcher分发阶段了!

对于power按键,最核心的就是mpolicy->interceptKeyBeforQueueing.  mPolicy是什么?请返回看本篇最开始的NativeInputManager()->InputManager()->InputDispatcher() 这依次的初始化流程,NativeInputManager将自己传递给了InputDispatcher的mPolicy参数。

NativeInputManager其实是C++和java层的桥接。从C++层调用NativeInputManager的函数,其实目的就是为了跳到java层InputManagerManager.java中的函数。它通过jni的CallIntMethod调用NativeInputManager的同名函数。具体参见我的另一篇博客通过android inputManagerService 看JNI 函数注册 - 优快云博客

在inputDispatcher中有三个队列如下,其输入事件处理流程还是比较复杂的。不过power key 触发亮灭屏的操作在interceptKeyBeforQueueing中就处理完了。所以围绕这三个队列的流程就后面有时间再学习。快点结束这篇吧,还有一篇关于PROXIMITY_SCREEN_OFF_WAKE_LOCK的博客,因为半路跳到inputManager而停下来了。这两篇结束后,想学学ims和sip,python也可以啊!

 

回到interceptKeyBeforQueueing:

这个mWindowsManagerCallbacks是什么呢?又要从开天辟地不久之后的startOtherServices说起.inputManager初始化后,通过setWindowManagerCallbacks设置mWindowsManagerCallbacks。入参是InputMonitor.

进入InputMonitor.interceptKeyBoforeQueueing()函数。调用的是mService.mPolicy.interceptKeyBeforeQueueing:

看下面几张图,最终是进入PhoneWindowManager().interceptKeyBeforeQueueing():

 

下面看PhoneWindowManager().interceptKeyBeforeQueueing(),根据Power键时按下还是松开,分别调用interceptPowerKeyDown,interceptPowerKeyUP

result &=~ACTION_PASS_TO_USER.  默认不传到user。

当时按下power键时先进行初步处理,判断是否截屏,来电静音等等。

如果前面都没有处理,即若没有消化掉power键,则继续处理,主要工作是在亮屏时按POWER键发送延时事件,触发长按处理。如果超时前抬起power键,这些事件会被撤销。

超时后,长按事件将得到处理,分别进入powerLongPress(),和powerVeryLongPress():

 

这两个短按和长按关机键的处理函数,默认流程会提示关机。并且将handler设置为true。 

接下来看当power键抬起,maxcount默认为1.所以不会走maxcount判断语句。默认走powerPress(),处理一次power按下。

powerPress中,不支持多次按键,count一定是1.默认走SHORT_PRESS_POWER_GO_TO_SLEEP.

即走goToSlee().

调用PowerManager goToSleep 休眠。具体怎么休眠,灭屏,就要看PowerManager了。

1.亮屏 down 

          发delay事件

           长按 : 弹出关闭重启对话框   delay事件到期触发

           短按 :不做处理,会在up时清除delay事件。

   亮屏 up 

        长按 :不做处理(因为power long press和powerVeryLongPress已经处理了power键,并将handle设置为true了。)

        短按 调用goToSleep休眠系统

2. 灭屏 down

            调用wakeup 唤醒系统:wakeUPFromPowerKey,设置handle为true

     灭屏 up

            不做处理:因为灭屏down时设置handle为true了。

 

<think>我们正在分析Android系统中Power Manager服务(PMS)的屏幕流程。根据引用内容,我们可以构建一个从按键事件到屏幕状态变化的完整流程。以下将结合引用信息逐步分析: ### 1. **按键事件触发** 当用户按下Power键时,Input子系统会捕获该事件并传递给上层。在`PhoneWindowManager`中,会调用`wakeUpFromWakeKey`方法处理请求(引用[5]): ```java private void wakeUpFromWakeKey(KeyEvent event) { // ,原因是WAKE_REASON_WAKE_KEY,details是"android.policy:KEY" if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { // ... 可能启动HOME界面 } } ``` 这里通过`wakeUp`方法向PowerManagerService发起请求。 ### 2. **PowerManagerService处理请求** 在PMS中,会计算屏幕状态、度等参数,并与ActivityManagerService(AMS)、WindowManagerService(WMS)等模块交互(引用[3])。核心流程包括: - 更新电源状态(如从休眠状态切换到状态) - 计算屏幕度曲线(考虑自动度、环境光等) - 通知AMS/WMS准备界面绘制 ### 3. **底层LCD控制** PMS最终会通过写入`/sys/power/state`节点控制屏幕状态(引用[4]): - :调用`autosuspend_disable()`,向节点写入`on` - :写入`mem`(或其他休眠状态字符串) 在写入节点后,会触发LCD的`late_resume`操作(如`sprdfb_dispc_resume`函数),硬件完成屏幕。 ### 4. **流程** 流程类似,但方向相反(引用[1][2]): 1. Power键事件触发`goToSleep`方法 2. PMS计算休眠状态 3. 通知AMS/WMS进入休眠(暂停界面绘制) 4. 写入`/sys/power/state`节点(如`mem`)关闭屏幕 ### 5. **关键节点总结** | **阶段** | **关键操作** | |----------------|-----------------------------------------------------------------------------| | **按键捕获** | `PhoneWindowManager`处理Power键事件,调用`wakeUp`或`goToSleep` | | **PMS状态计算**| 更新全局电源状态,与AMS/WMS交互,计算度/超时等参数 | | **底层控制** | 通过`/sys/power/state`节点控制LCD电源,调用驱动层`resume/suspend`函数 | > **注意**:不同Android版本或厂商定制可能修改流程细节,但核心框架保持一致。 §§相关问题§§ 1. Power键事件如何从内核传递到Android框架层? 2. PMS与WakeLock机制如何协同工作? 3. 自动度调节在流程中如何生效? 4. 时如何保证应用数据安全?</think>### Android Power Manager 屏幕流程分析 Android的电源管理核心是`PowerManagerService`(PMS),屏幕流程涉及**输入事件处理、状态计算、跨模块协作、硬件控制**四个关键阶段。以下是基于Android 7.0+的流程解析(兼容至Android 11): --- #### **1. 输入事件捕获** 当用户按下Power键: - **Input子系统**捕获按键事件,通过`InputReader`/`InputDispatcher`传递到上层。 - **`PhoneWindowManager`** 处理事件: ```java // PhoneWindowManager.java private void wakeUpFromWakeKey(KeyEvent event) { if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { // 若满足条件(如长按HOME键),启动HOME界面 } } ``` 调用`PowerManager.wakeUp()`触发流程[^5]。 --- #### **2. PMS状态计算与决策** **`PowerManagerService`** 执行核心逻辑: - **状态机切换**:从`OFF`状态切换到`ON`或`DREAM`状态。 - **参数计算**: - 屏幕度(结合自动度调节) - 超时时间(根据用户设置、传感器数据) - 电源策略(如是否允许剧院模式唤醒) - **跨模块协作**: - 通知`ActivityManagerService`(AMS)准备应用界面绘制。 - 同步`WindowManagerService`(WMS)更新窗口状态[^3]。 --- #### **3. 底层硬件控制** PMS通过**HAL层**操作显示设备: 1. **禁用自动挂起**: ```c autosuspend_disable(); // 向/sys/power/state写入"on" ``` 2. **唤醒显示控制器**: - 调用`late_resume()`恢复LCD(如`sprdfb_dispc_resume`函数)。 - 硬件初始化时间因设备而异[^4]。 3. **设置度**: - 通过`/sys/class/backlight`节点写入度值。 > 💡 **流程相反**: > 1. PMS计算休眠策略(如`USER_ACTIVITY`超时)。 > 2. 通知AMS/WMS暂停界面。 > 3. 向`/sys/power/state`写入`mem`触发LCD休眠[^1][^2]。 --- #### **4. 关键参与模块** | **模块** | **职责** | |------------------------|---------------------------------------| | `PhoneWindowManager` | 输入事件路由 | | `PowerManagerService` | 电源策略决策、状态管理 | | `DisplayManagerService`| 显示设备控制(度/状态) | | `ActivityManagerService` | 协调应用生命周期 | | 内核驱动 | 操作`/sys/power`节点控制硬件电源状态 | --- ### 流程总结 ```mermaid graph TD A[Power键按下] --> B{Input子系统捕获} B --> C[PhoneWindowManager处理] C --> D[PMS唤醒请求] D --> E[状态计算+跨模块协作] E --> F[写入/sys/power/state] F --> G[内核控制LCD起] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值