android学习笔记 按电源键屏幕唤醒和屏幕睡眠流程(从上层到kernel)

news/2024/11/17 1:33:36/

一. 屏幕的唤醒

      首先inputread在读取到有keyboard事件上报后,会调用到keydispatch的notifykey,去询问wm是否会对这次按键特殊处理,如果WM不处理,则此处会点亮或者熄灭屏幕。

     inputReader.cpp                                 KeyboardInputMapper::processKey

                                                                       getDispatcher()->notifyKey

     inputDispacher.cpp                            InputDispatcher::notifyKey

                                                                      mPolicy->interceptKeyBeforeQueueing

    com_android_server_inputManager.cpp                NativeInputManager::interceptKeyBeforeQueueing

                                                                                             env->CallIntMethod(mCallbacksObj,
                                                                                            gCallbacksClassInfo.interceptKeyBeforeQueueing,
                                                                                             when, action, flags, keyCode, scanCode, policyFlags, isScreenOn);             //此处gCallbacksClassInfo中的各种方法就是InputManager的对应的方法,在JNI初始化的时候就注册了,详情请参看register_android_server_InputManager函数,通过jniRegisterNativeMethods将inputmanager的各种callback注册到gCallbacksClassInfo中。

 返回的wmaction就是后面WM对此次按键事件的policy,通过此返回值,此处会决定下一步的动作。

  InputManager.java                                               interceptKeyBeforeQueueing

                                                                                  mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing

WindowmanagerService.java                             InputMonitor::interceptKeyBeforeQueueing

                                                                                  mPolicy.interceptKeyBeforeQueueing

PhonewindowManager.java                                interceptKeyBeforeQueueing   

                                                                                  //摘录部分代码:

    public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {final boolean down = action == KeyEvent.ACTION_DOWN;final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0;final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;// If screen is off then we treat the case where the keyguard is open but hidden// the same as if it were open and in front.// This will prevent any keys other than the power button from waking the screen// when the keyguard is hidden by another activity.final boolean keyguardActive = (isScreenOn ?mKeyguardMediator.isShowingAndNotHidden() :mKeyguardMediator.isShowing());int result; //result即为返回到wmactionif (isScreenOn || isInjected) {// When the screen is on or if the key is injected pass the key to the application.result = ACTION_PASS_TO_USER;} else {//我们现在走的应该是这个// When the screen is off and the key is not injected, determine whether// to wake the device but don't pass the key to the application.result = 0;final boolean isWakeKey = (policyFlags& (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;if (down && isWakeKey) {if (keyguardActive) {
                       //也就是说,如果当前屏幕是灭的,且按的键是可以唤醒屏幕的,那么WM会首先将此次按键传递给keyguard,由keyguard来唤醒屏幕,并作出相应的动作,否则就自己点亮屏幕,通过返回的policy来通知下层。// If the keyguard is showing, let it decide what to do with the wake key.  mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);} else {// Otherwise, wake the device ourselves.result |= ACTION_POKE_USER_ACTIVITY;}}}
....................
}
       keyguarViewMediator.java                    onWakeKeyWhenKeyguardShowingTq

                                                                              wakeWhenReadyLocked

                                                                                           mHandler.obtainMessage(WAKE_WHEN_READY, keyCode, 0);

                                                                             mHandler.handleMessage

                                                                           handleWakeWhenReady

                                                                           mKeyguardViewManager.wakeWhenReadyTq

        KeyguardViewManager.java                mKeyguardView.wakeWhenReadyTq

        LockpatternKeyguardView.java            wakeWhenReadyTq

                                                                             getCallback().pokeWakelock();

        KeyguardViewMediator.java                  pokeWakelock

                                                                            mWakeLock.acquire();        // mWakeLock即为:mWakeLock = mPM.newWakeLock(
                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
                "keyguard");      具有ACQUIRE_CAUSE_WAKUPQ权限的唤醒锁,上层就是通过此锁来唤醒屏幕,接下来就是powermanager的流程了。

        PowerManager.java                              acquire

                                                                           mService.acquireWakeLock

         PowermanagerService.java                acquireWakeLock

                                                                            acquireWakeLockLocked//此处会检查唤醒锁的标志位,作出对应的处理。

                                                                             setPowerState       //此函数为powermanager的核心函数之一,会对屏幕背光/唤醒,睡眠等作出相应的处理

                                                                           setScreenStateLocked //此函数很关键

                                                                           Power.setScreenState

        power.java                                                setScreenState

       android_os_Power.cpp                          setScreenState

        power.c                                                      set_screen_state//此函数作为上层的最后一个函数,会打印出标志性的log,*** set_screen_state %d,如果打出这个log,至少证明从APP-HAL都是在正常干活的,那么问题只能是kernel的了,贴出代码看看:

int
set_screen_state(int on)
{//QEMU_FALLBACK(set_screen_state(on));LOGI("*** set_screen_state %d", on); //神奇的log标志initialize_fds();//LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime,//      systemTime(), strerror(g_error));if (g_error) return g_error;char buf[32];int len;if(on)len = sprintf(buf, "%s", on_state);elselen = sprintf(buf, "%s", off_state); len = write(g_fds[REQUEST_STATE], buf, len);//此处就是写了kernel的设备文件接口。if(len < 0) {LOGE("Failed setting last user activity: g_error=%d\n", g_error);}return 0;
}
在此函数中写了底层的power控制的设备文件接口,对应的设备文件为:/sys/power/state

接下来的流程就是到了内核空间。

kernel/kernel/power/main.c                                    state_store        

  //此函数被宏power_attr(state)声明为设备文件接口 sys/power/state,宏power_attr的定义为(power.h):

                                                                                                                       #define power_attr(_name) \
static struct kobj_attribute _name##_attr = {\
.attr = {\
.name = __stringify(_name),\
.mode = 0644, \
}, \
.show = _name##_show,\
.store = _name##_store, \
}

        

                                                                                          

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPENDsuspend_state_t state = PM_SUSPEND_ON;
#elsesuspend_state_t state = PM_SUSPEND_STANDBY;
#endifconst char * const *s;
#endifchar *p;int len;int error = -EINVAL;p = memchr(buf, '\n', n);len = p ? p - buf : n;/* First, check if we are requested to hibernate */if (len == 4 && !strncmp(buf, "disk", len)) {error = hibernate();goto Exit;}#ifdef CONFIG_SUSPENDfor (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {if (*s && len == strlen(*s) && !strncmp(buf, *s, len))break;}printk("##: enter %s\n", pm_states[state]);if (state < PM_SUSPEND_MAX && *s)
#ifdef CONFIG_EARLYSUSPEND  // android对linux的睡眠唤醒机制做了一些优化,也就是earlysuspen,laterresume机制,此处宏是有定义的,所以会先走android的那一套if (state == PM_SUSPEND_ON || valid_state(state)) {error = 0;printk("##: entering request_suspend_state()...\n");request_suspend_state(state);}
#elseerror = enter_state(state);
#endif
#endifExit:printk("##: state_store() returns back.\n");return error ? error : n;
}

kernel/kernel/power/erlysuspend.c                                                              request_suspend_state

void request_suspend_state(suspend_state_t new_state)
{unsigned long irqflags;int old_sleep;/* when we get here, means userspace service work well, stop reboot watchdog */powerkey_wdt_stop();spin_lock_irqsave(&state_lock, irqflags);old_sleep = state & SUSPEND_REQUESTED;if (debug_mask & DEBUG_USER_STATE) {struct timespec ts;struct rtc_time tm;getnstimeofday(&ts);rtc_time_to_tm(ts.tv_sec, &tm);pr_info("request_suspend_state: %s (%d->%d) at %lld ""(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",requested_suspend_state, new_state,ktime_to_ns(ktime_get()),tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);}if (!old_sleep && new_state != PM_SUSPEND_ON) {state |= SUSPEND_REQUESTED;queue_work(suspend_work_queue, &early_suspend_work);} else if (old_sleep && new_state == PM_SUSPEND_ON) {state &= ~SUSPEND_REQUESTED;wake_lock(&main_wake_lock); //acquire    main ——wakelockqueue_work(suspend_work_queue, &late_resume_work); //将唤醒的work起来,开始执行之前声明的late_resume_work}requested_suspend_state = new_state;spin_unlock_irqrestore(&state_lock, irqflags);
}
而 之前有声明static DECLARE_WORK(late_resume_work, late_resume); 故实际执行的函数是:late_resume。


kernel/kernel/power/erlysuspend.c                                         late_resume

static void late_resume(struct work_struct *work)
{struct early_suspend *pos;unsigned long irqflags;int abort = 0;mutex_lock(&early_suspend_lock);spin_lock_irqsave(&state_lock, irqflags);if (state == SUSPENDED)state &= ~SUSPENDED;elseabort = 1;spin_unlock_irqrestore(&state_lock, irqflags);if (abort) {if (debug_mask & DEBUG_SUSPEND)pr_info("late_resume: abort, state %d\n", state);goto abort;}if (debug_mask & DEBUG_SUSPEND)pr_info("late_resume: call handlers\n");list_for_each_entry_reverse(pos, &early_suspend_handlers, link)if (pos->resume != NULL) {print_name_offset(NULL, pos->resume);pos->resume(pos);       //此处会调用到之前注册了laterresume的drv的对应的函数,调用到fb_resume之后,屏幕就唤醒刷屏,屏幕上夜就有了数据,屏幕唤醒的流程就结束了。}if (debug_mask & DEBUG_SUSPEND)pr_info("late_resume: done\n");
abort:mutex_unlock(&early_suspend_lock);
}

总结:  屏幕点亮过程是由inputread捕获后交由WM处理,由keyguard去申请唤醒锁,powermanagerservice去调用kernel的唤醒的过程,其中弯弯绕还是比较多的,涉及的东西也很多,wakelock机制我还没有搞的很清楚。


二、 屏幕睡眠

和屏幕唤醒的过程很类似,如下:

 inputReader.cpp                                 KeyboardInputMapper::processKey

                                                                       getDispatcher()->notifyKey

     inputDispacher.cpp                            InputDispatcher::notifyKey

                                                                   mPolicy->interceptKeyBeforeQueueing

    com_android_server_inputManager.cpp                NativeInputManager::interceptKeyBeforeQueueing

  InputManager.java                                               interceptKeyBeforeQueueing

                                                                                  mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing

WindowmanagerService.java                             InputMonitor::interceptKeyBeforeQueueing

                                                                                  mPolicy.interceptKeyBeforeQueueing

PhonewindowManager.java                                interceptKeyBeforeQueueing   

                                                                                     //同上面的分析,此处返回的action是被或上了ACTION_GO_TO_SLEEP的(见1975行对KeyEvent.KEYCODE_POWER的处理).......一级一级的返回后.....

 com_android_server_inputManager.cpp                               NativeInputManager::interceptKeyBeforeQueueing      //返回值中含有gotosleep的flag,故走到gotosleep分支

                                                                                                          android_server_PowerManagerService_goToSleep

com_android_server_PowerManagerService.cpp                android_server_PowerManagerService_goToSleep          //同上面的inputmanager,此处也会调用到PowerManagerService的gotosleep,也是用register_android_server_PowerManagerService方法来对应起来。

                                                                                                          env->CallVoidMethod(gPowerManagerServiceObj, gPowerManagerServiceClassInfo.goToSleep,
                nanoseconds_to_milliseconds(eventTime));

PowermanagerService.java                                                     goToSleep

                                                                                                        goToSleepWithReason

goToSleepLocked

                                             setPowerState(SCREEN_OFF, false, reason);

                                                                                                        setPowerState

                                                                                                        setScreenStateLocked

                                                                                                          Power.setScreenState(false)

power.java                                                                                      setScreenState

android_os_power.java                                                               setScreenState

power.c                                                                                           set_screen_state 

kernel/kernel/power/main.c                                                          state_store 

kernel/kernel/power/earlysuspend.c                                           request_suspend_state             //此处流程和唤醒大同小异,不在赘述

                                                                                                              early_suspend

static void early_suspend(struct work_struct *work)
{struct early_suspend *pos;unsigned long irqflags;int abort = 0;mutex_lock(&early_suspend_lock);spin_lock_irqsave(&state_lock, irqflags);if (state == SUSPEND_REQUESTED)state |= SUSPENDED;elseabort = 1;spin_unlock_irqrestore(&state_lock, irqflags);if (abort) {if (debug_mask & DEBUG_SUSPEND)pr_info("early_suspend: abort, state %d\n", state);mutex_unlock(&early_suspend_lock);goto abort;}if (debug_mask & DEBUG_SUSPEND)pr_info("early_suspend: call handlers\n");list_for_each_entry(pos, &early_suspend_handlers, link) {if (pos->suspend != NULL) {print_name_offset(NULL, pos->suspend);pos->suspend(pos); //调用注册了earlysuspend的drv的suspend函数,调用到了fb_suspend,屏幕就会进入睡眠,睡眠的过程就结束了}}mutex_unlock(&early_suspend_lock);if (debug_mask & DEBUG_SUSPEND)pr_info("early_suspend: sync\n");//sys_sync();//let screen up faster 
abort:spin_lock_irqsave(&state_lock, irqflags);if (state == SUSPEND_REQUESTED_AND_SUSPENDED)wake_unlock(&main_wake_lock);          //earlysuspend完毕后,检查当前是否还有wakelock是active状态,如果没有,则会进入深睡眠(linux的suspend)spin_unlock_irqrestore(&state_lock, irqflags);
}


下面我们继续跟下代码,简单看看earlysuspend到deepsleep的过程,从wake_unlock开始

kernel/kernel/power/wakelock.c                                      wake_unlock

void wake_unlock(struct wake_lock *lock)
{int type;unsigned long irqflags;spin_lock_irqsave(&list_lock, irqflags);type = lock->flags & WAKE_LOCK_TYPE_MASK;
#ifdef CONFIG_WAKELOCK_STATwake_unlock_stat_locked(lock, 0);
#endifif (debug_mask & DEBUG_WAKE_LOCK)pr_info("wake_unlock: %s\n", lock->name);lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);list_del(&lock->link);list_add(&lock->link, &inactive_locks);if (type == WAKE_LOCK_SUSPEND) {long has_lock = has_wake_lock_locked(type); //判断当前是否还有wake_lock是active的if (has_lock > 0) {if (debug_mask & DEBUG_EXPIRE)pr_info("wake_unlock: %s, start expire timer, ""%ld\n", lock->name, has_lock);mod_timer(&expire_timer, jiffies + has_lock);} else {if (del_timer(&expire_timer))if (debug_mask & DEBUG_EXPIRE)pr_info("wake_unlock: %s, stop expire ""timer\n", lock->name);if (has_lock == 0) {if (sprd_suspend_enable) {	queue_work(suspend_work_queue, &suspend_work); //起suspend_work,根据声明,此处的work对应的函数即是suspend}}}if (lock == &main_wake_lock) {if (debug_mask & DEBUG_SUSPEND)print_active_locks(WAKE_LOCK_SUSPEND);
#ifdef CONFIG_WAKELOCK_STATupdate_sleep_wait_stats_locked(0);
#endif}}spin_unlock_irqrestore(&list_lock, irqflags);
}

kernel/kernel/power/wakelock.c                                        suspend
static void suspend(struct work_struct *work)
{int ret;int entry_event_num;add_pm_message(get_sys_cnt(), "suspend--enter: ", 0, 0, 0);if (has_wake_lock(WAKE_LOCK_SUSPEND)) {if (debug_mask & DEBUG_SUSPEND)pr_info("suspend: abort suspend\n");return;}entry_event_num = current_event_num;sys_sync();if (debug_mask & DEBUG_SUSPEND)pr_info("suspend: enter suspend\n");ret = pm_suspend(requested_suspend_state);if (debug_mask & DEBUG_EXIT_SUSPEND) {struct timespec ts;struct rtc_time tm;getnstimeofday(&ts);rtc_time_to_tm(ts.tv_sec, &tm);pr_info("suspend: exit suspend, ret = %d ""(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);}if (current_event_num == entry_event_num) {if (debug_mask & DEBUG_SUSPEND)pr_info("suspend: pm_suspend returned with no event\n");wake_lock_timeout(&unknown_wakeup, HZ / 2);}add_pm_message(get_sys_cnt(), "suspend--leave: ", 0, 0, 0);
}

kernel/kernel/power/suspend.c                                   pm_suspend

int pm_suspend(suspend_state_t state)
{if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)return enter_state(state); //是不是和main.c里的state_store函数中liunx的suspend一样?豁然开朗。return -EINVAL;
}


接下来就是linux的suspend了,没有再仔细看过,惭愧惭愧。

总的来说,屏幕的睡眠是和上层的keyguard没有关系,是在WM和PMS以及相关的JNI的配合下对kernel的操作完成的。



屏幕唤醒和睡眠就写到这里,而背光的点亮过程,大部分处理是在PMS中,是在HAL层操作了lights的设备文件并不涉及到唤醒和睡眠,显得比较简单,有时间也写出来分享。



                                                                                                                                                    


      

  





                                                                                   



http://www.ppmy.cn/news/692920.html

相关文章

杰理-watch-手表-AC701-双击屏幕唤醒

杰理-watch-手表-AC701-双击屏幕唤醒 TP不在休眠状态&#xff0c;功耗较大&#xff0c;测试熄屏功耗2-3mA &#xff08;1&#xff09;TP抬起的时候rouse_two_flag置1 判断是都开启双击唤醒&#xff0c;关闭TP则进入睡眠&#xff0c;功耗降低 开机设定初始状态 static bo…

电脑休眠唤醒后会出现屏幕闪烁问题怎么彻底解决?

电脑休眠唤醒后会出现屏幕闪烁问题怎么彻底解决&#xff1f;有的用户在电脑待机休眠之后&#xff0c;重新去唤醒电脑使用&#xff0c;这个时候电脑屏幕就会出现验证的屏幕闪烁&#xff0c;导致无法进行正常的使用。这个情况是电脑系统不兼容导致的。如果想要彻底解决问题&#…

Android唤醒屏幕

Android唤醒屏幕 大家在开发中遇到要唤醒屏幕的时候&#xff0c;一般都想到用WalkLock。WalkLock真的能点亮屏幕吗&#xff1f;答案是肯定的。 可是有时候为什么不点亮屏幕&#xff0c;这个就是参数设置的问题了。 PowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK | Pow…

tp 双击唤醒 最终实现方案 参考

折腾许久&#xff0c;终于调试出一个比较满意的版本&#xff0c;采用的许多方法跟上一篇不一样&#xff0c;在这里重新整理。注册和上报键值这里就不在累赘了&#xff0c;本篇博客主要要解决以下bug&#xff1a;1.系统进入睡眠状态后&#xff0c;如何通过tp唤醒系统。2.如何解决…

android app 唤醒屏幕

1&#xff0c;添加权限&#xff1a; <uses-permission android:name"android.permission.WAKE_LOCK" /> 2&#xff0c;代码如下&#xff1a; // 唤醒屏幕private static PowerManager mPowerManager;private static PowerManager.WakeLock mWakeLock;Suppres…

android开发双击唤醒屏幕,安卓手机双击唤醒原理是什么 安卓双击唤醒原理介绍...

很多安卓智能手机屏幕的唤醒方式各种各样&#xff0c;最常规的方式就是按一下电源键点亮屏 幕。 手机电源键位置 由于手机 机身大小的限制和手感问题越来越不好触碰到&#xff0c;因此双击亮屏功能就产生了。市场上 一些小软件在安装之后就可以让你的 安卓智能手机 支持双击唤醒…

android 唤醒和解锁屏幕

/*** 唤醒手机屏幕并解锁*/public void wakeUpAndUnlock() {// 获取电源管理器对象PowerManager pm (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);boolean screenOn pm.isScreenOn();Log.d("WakeScreen0","screenOn: &qu…

android 唤醒屏幕并解锁

public static void wakeUpAndUnlock(Context context){KeyguardManager km (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);KeyguardManager.KeyguardLock kl km.newKeyguardLock("unLock");//解锁kl.disableKeyguard();//获取电源管理器对…