現在的位置: 首頁 > 綜合 > 正文

按電源鍵屏幕喚醒和屏幕睡眠流程(從上層到kernel)

2014年11月03日 ⁄ 綜合 ⁄ 共 18209字 ⁄ 字型大小 評論關閉

一. 屏幕的喚醒

      首先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   

                                                                                  //摘錄部分代碼:

[java] view
plain
copy

  1. <span style="font-weight: bold; ">   </span> public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags,  
  2.             int keyCode, int scanCode, int policyFlags, boolean isScreenOn) {  
  3.         final boolean down = action == KeyEvent.ACTION_DOWN;  
  4.         final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0;  
  5.   
  6.         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;  
  7.   
  8.         // If screen is off then we treat the case where the keyguard is open but hidden  
  9.         // the same as if it were open and in front.  
  10.         // This will prevent any keys other than the power button from waking the screen  
  11.         // when the keyguard is hidden by another activity.  
  12.   
  13.         final boolean keyguardActive = (isScreenOn ?  
  14.                                         mKeyguardMediator.isShowingAndNotHidden() :  
  15.                                         mKeyguardMediator.isShowing());  
  16.   
  17.         int result; //result即為返回到wmaction  
  18.         if (isScreenOn || isInjected) {  
  19.             // When the screen is on or if the key is injected pass the key to the application.  
  20.             result = ACTION_PASS_TO_USER;  
  21.         } else {//我們現在走的應該是這個  
  22.             // When the screen is off and the key is not injected, determine whether  
  23.             // to wake the device but don't pass the key to the application.  
  24.             result = 0;  
  25.   
  26.             final boolean isWakeKey = (policyFlags  
  27.                     & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0;  
  28.             if (down && isWakeKey) {  
  29.                 if (keyguardActive) {  
[java] view
plain
copy

  1.                        //也就是說,如果當前屏幕是滅的,且按的鍵是可以喚醒屏幕的,那麼WM會首先將此次按鍵傳遞給keyguard,由keyguard來喚醒屏幕,並作出相應的動作,否則就自己點亮屏幕,通過返回的policy來通知下層。  
  2.   
  3.                     // If the keyguard is showing, let it decide what to do with the wake key.  
  4.                   <span style="color:#ff0000;"> </span> mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);  
  5.                 } else {  
  6.                     // Otherwise, wake the device ourselves.  
  7.   
  8.                     result |= ACTION_POKE_USER_ACTIVITY;  
  9.                 }  
  10.             }  
  11.         }<span style="font-weight: bold; ">  
  12. ....................  
  13. }</span>  

       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的了,貼出代碼看看:

  1. int  
  2. set_screen_state(int on)  
  3. {  
  4.     //QEMU_FALLBACK(set_screen_state(on));  
  5.   
  6.     LOGI("*** set_screen_state %d", on); //神奇的log標誌  
  7.   
  8.     initialize_fds();  
  9.   
  10.     //LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime,  
  11.       //      systemTime(), strerror(g_error));  
  12.   
  13.     if (g_error) return g_error;  
  14.   
  15.     char buf[32];  
  16.     int len;  
  17.   
  18.     if(on)  
  19.         len = sprintf(buf, "%s", on_state);  
  20.     else  
  21.         len = sprintf(buf, "%s", off_state);  
  22.    <span style="color:#cc0000;"> </span>len = write(g_fds[REQUEST_STATE], buf, len);//此處就是寫了kernel的設備文件介面。  
  23.     if(len < 0) {  
  24.         LOGE("Failed setting last user activity: g_error=%d\n", g_error);  
  25.     }  
  26.     return 0;  
  27. }  

在此函數中寫了底層的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, \
}

        

                                                                                          

  1. static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,  
  2.                const char *buf, size_t n)  
  3. {  
  4. #ifdef CONFIG_SUSPEND  
  5. #ifdef CONFIG_EARLYSUSPEND  
  6.     suspend_state_t state = PM_SUSPEND_ON;  
  7. #else  
  8.     suspend_state_t state = PM_SUSPEND_STANDBY;  
  9. #endif  
  10.     const char * const *s;  
  11. #endif  
  12.     char *p;  
  13.     int len;  
  14.     int error = -EINVAL;  
  15.   
  16.     p = memchr(buf, '\n', n);  
  17.     len = p ? p - buf : n;  
  18.   
  19.     /* First, check if we are requested to hibernate */  
  20.     if (len == 4 && !strncmp(buf, "disk", len)) {  
  21.         error = hibernate();  
  22.   goto Exit;  
  23.     }  
  24.   
  25. #ifdef CONFIG_SUSPEND  
  26.     for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {  
  27.         if (*s && len == strlen(*s) && !strncmp(buf, *s, len))  
  28.             break;  
  29.   
  30.     }  
  31.     printk("##: enter %s\n", pm_states[state]);  
  32.     if (state < PM_SUSPEND_MAX && *s)  
  33. #ifdef CONFIG_EARLYSUSPEND  // android對linux的睡眠喚醒機製做了一些優化,也就是earlysuspen,laterresume機制,此處宏是有定義的,所以會先走android的那一套  
  34.         if (state == PM_SUSPEND_ON || valid_state(state)) {  
  35.             error = 0;  
  36.             printk("##: entering request_suspend_state()...\n");  
  37.             request_suspend_state(state);  
  38.         }  
  39. #else  
  40.   
  41.         error = enter_state(state);  
  42. #endif  
  43. #endif  
  44.   
  45.  Exit:  
  46.     printk("##: state_store() returns back.\n");  
  47.     return error ? error : n;  
  48. }  


kernel/kernel/power/erlysuspend.c                                                              request_suspend_state

  1. void request_suspend_state(suspend_state_t new_state)  
  2. {  
  3.     unsigned long irqflags;  
  4.     int old_sleep;  
  5.   
  6.     /* when we get here, means userspace service work well, stop reboot watchdog */  
  7.     powerkey_wdt_stop();  
  8.   
  9.     spin_lock_irqsave(&state_lock, irqflags);  
  10.     old_sleep = state & SUSPEND_REQUESTED;  
  11.     if (debug_mask & DEBUG_USER_STATE) {  
  12.         struct timespec ts;  
  13.         struct rtc_time tm;  
  14.         getnstimeofday(&ts);  
  15.         rtc_time_to_tm(ts.tv_sec, &tm);  
  16.         pr_info("request_suspend_state: %s (%d->%d) at %lld "  
  17.             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",  
  18.             new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",  
  19.             requested_suspend_state, new_state,  
  20.             ktime_to_ns(ktime_get()),  
  21.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  
  22.             tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);  
  23.   
  24.     }  
  25.     if (!old_sleep && new_state != PM_SUSPEND_ON) {  
  26.         state |= SUSPEND_REQUESTED;  
  27.         queue_work(suspend_work_queue, &early_suspend_work);  
  28.     }<span style="color: rgb(255, 0, 0); background-color: rgb(255, 255, 255);"> </span><span style="background-color: rgb(255, 255, 255); ">else if (old_sleep && new_state == PM_SUSPEND_ON) {  
  29.         state &= ~SUSPEND_REQUESTED;  
  30.         wake_lock(&main_wake_lock); //acquire    main ——wakelock  
  31.         queue_work(suspend_work_queue, &late_resume_work); //將喚醒的work起來,開始執行之前聲明的late_resume_work  
  32.   
  33.     }</span>  
  34.     requested_suspend_state = new_state;  
  35.     spin_unlock_irqrestore(&state_lock, irqflags);  
  36. }  

而 之前有聲明static DECLARE_WORK(late_resume_work, late_resume); 故實際執行的函數是:late_resume。


kernel/kernel/power/erlysuspend.c                                         late_resume

  1. static void late_resume(struct work_struct *work)  
  2. {  
  3.     struct early_suspend *pos;  
  4.     unsigned long irqflags;  
  5.     int abort = 0;  
  6.   
  7.     mutex_lock(&early_suspend_lock);  
  8.     spin_lock_irqsave(&state_lock, irqflags);  
  9.     if (state == SUSPENDED)  
  10.         state &= ~SUSPENDED;  
  11.     else  
  12.         abort = 1;  
  13.     spin_unlock_irqrestore(&state_lock, irqflags);  
  14.   
  15.     if (abort) {  
  16.         if (debug_mask & DEBUG_SUSPEND)  
  17.             pr_info("late_resume: abort, state %d\n", state);  
  18.         goto abort;  
  19.     }  
  20.   
  21.     if (debug_mask & DEBUG_SUSPEND)  
  22.         pr_info("late_resume: call handlers\n");  
  23.     list_for_each_entry_reverse(pos, &early_suspend_handlers, link)  
  24.         if (pos->resume != NULL) {  
  25.             print_name_offset(NULL, pos->resume);  
  26.             pos->resume(pos);       //此處會調用到之前註冊了laterresume的drv的對應的函數,調用到fb_resume之後,屏幕就喚醒刷屏,屏幕上夜就有了數據,屏幕喚醒的流程就結束了。  
  27.         }  
  28.     if (debug_mask & DEBUG_SUSPEND)  
  29.         pr_info("late_resume: done\n");  
  30. abort:  
  31.     mutex_unlock(&early_suspend_lock);  
  32. }  


總結:  屏幕點亮過程是由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

  1. static void early_suspend(struct work_struct *work)  
  2. {  
  3.     struct early_suspend *pos;  
  4.     unsigned long irqflags;  
  5.     int abort = 0;  
  6.   
  7.     mutex_lock(&early_suspend_lock);  
  8.     spin_lock_irqsave(&state_lock, irqflags);  
  9.     if (state == SUSPEND_REQUESTED)  
  10.         state |= SUSPENDED;  
  11.     else  
  12.         abort = 1;  
  13.     spin_unlock_irqrestore(&state_lock, irqflags);  
  14.   
  15.     if (abort) {  
  16.         if (debug_mask & DEBUG_SUSPEND)  
  17.             pr_info("early_suspend: abort, state %d\n", state);  
  18.         mutex_unlock(&early_suspend_lock);  
  19.         goto abort;  
  20.     }  
  21.   
  22.     if (debug_mask & DEBUG_SUSPEND)  
  23.         pr_info("early_suspend: call handlers\n");  
  24.     list_for_each_entry(pos, &early_suspend_handlers, link) {  
  25.         if (pos->suspend != NULL) {  
  26.             print_name_offset(NULL, pos->suspend);  
  27.             <span style="background-color: rgb(204, 204, 204);">pos->suspend(pos); //調用註冊了earlysuspend的drv的suspend函數,調用到了fb_suspend,屏幕就會進入睡眠,睡眠的過程就結束了</span>  
  28.         }  
  29.   
  30.     }  
  31.     mutex_unlock(&early_suspend_lock);  
  32.   
  33.     if (debug_mask & DEBUG_SUSPEND)  
  34.         pr_info("early_suspend: sync\n");  
  35.   
  36.     //sys_sync();//let screen up faster   
  37. abort:  
  38.     spin_lock_irqsave(&state_lock, irqflags);  
  39.     if (state == SUSPEND_REQUESTED_AND_SUSPENDED)  
  40.         wake_unlock(&main_wake_lock);          //earlysuspend完畢後,檢查當前是否還有wakelock是active狀態,如果沒有,則會進入深睡眠(linux的suspend)  
  41.     spin_unlock_irqrestore(&state_lock, irqflags);  
  42. }  

下面我們繼續跟下代碼,簡單看看earlysuspend到deepsleep的過程,從wake_unlock開始

kernel/kernel/power/wakelock.c                                      wake_unlock

  1. void wake_unlock(struct wake_lock *lock)  
  2. {  
  3.     int type;  
  4.     unsigned long irqflags;  
  5.     spin_lock_irqsave(&list_lock, irqflags);  
  6.     type = lock->flags & WAKE_LOCK_TYPE_MASK;  
  7. #ifdef CONFIG_WAKELOCK_STAT  
  8.     wake_unlock_stat_locked(lock, 0);  
  9. #endif  
  10.     if (debug_mask & DEBUG_WAKE_LOCK)  
  11.         pr_info("wake_unlock: %s\n", lock->name);  
  12.     lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);  
  13.     list_del(&lock->link);  
  14.     list_add(&lock->link, &inactive_locks);  
  15.     if (type == WAKE_LOCK_SUSPEND) {  
  16.         long has_lock = has_wake_lock_locked(type); //判斷當前是否還有wake_lock是active的  
  17.         if (has_lock > 0) {  
  18.             if (debug_mask & DEBUG_EXPIRE)  
  19.   
  20.                 pr_info("wake_unlock: %s, start expire timer, "  
  21.                     "%ld\n", lock->name, has_lock);  
  22.             mod_timer(&expire_timer, jiffies + has_lock);  
  23.         } else {  
  24.             if (del_timer(&expire_timer))  
  25.                 if (debug_mask & DEBUG_EXPIRE)  
  26.                     pr_info("wake_unlock: %s, stop expire "  
  27.                         "timer\n", lock->name);  
  28.             if (has_lock == 0) {  
  29.                 if (sprd_suspend_enable) {    
  30.                     queue_work(suspend_work_queue, &suspend_work); //起suspend_work,根據聲明,此處的work對應的函數即是suspend  
  31.                 }  
  32.   
  33.             }  
  34.         }  
  35.         if (lock == &main_wake_lock) {  
  36.             if (debug_mask & DEBUG_SUSPEND)  
  37.                 print_active_locks(WAKE_LOCK_SUSPEND);  
  38. #ifdef CONFIG_WAKELOCK_STAT  
  39.             update_sleep_wait_stats_locked(0);  
  40. #endif  
  41.         }  
  42.     }  
  43.     spin_unlock_irqrestore(&list_lock, irqflags);  
  44. }  


kernel/kernel/power/wakelock.c                                        suspend

  1. static void suspend(struct work_struct *work)  
  2. {  
  3.     int ret;  
  4.     int entry_event_num;  
  5.   
  6.     add_pm_message(get_sys_cnt(), "suspend--enter: ", 0, 0, 0);  
  7.   
  8.     if (has_wake_lock(WAKE_LOCK_SUSPEND)) {  
  9.         if (debug_mask & DEBUG_SUSPEND)  
  10.             pr_info("suspend: abort suspend\n");  
  11.         return;  
  12.     }  
  13.   
  14.     entry_event_num = current_event_num;  
  15.     sys_sync();  
  16.     if (debug_mask & DEBUG_SUSPEND)  
  17.         pr_info("suspend: enter suspend\n");  
  18.     ret = pm_suspend(requested_suspend_state);  
  19.     if (debug_mask & DEBUG_EXIT_SUSPEND) {  
  20.   
  21.         struct timespec ts;  
  22.         struct rtc_time tm;  
  23.         getnstimeofday(&ts);  
  24.         rtc_time_to_tm(ts.tv_sec, &tm);  
  25.         pr_info("suspend: exit suspend, ret = %d "  
  26.             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,  
  27.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  
  28.             tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);  
  29.     }  
  30.   
  31.     if (current_event_num == entry_event_num) {  
  32.         if (debug_mask & DEBUG_SUSPEND)  
  33.             pr_info("suspend: pm_suspend returned with no event\n");  
  34.         wake_lock_timeout(&unknown_wakeup, HZ / 2);  
  35.     }  
  36.     add_pm_message(get_sys_cnt(), "suspend--leave: ", 0, 0, 0);  
  37. }  


kernel/kernel/power/suspend.c                                   pm_suspend

  1. int pm_suspend(suspend_state_t state)  
  2. {  
  3.     if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)  
  4.         return enter_state(state); //是不是和main.c里的state_store函數中liunx的suspend一樣?豁然開朗。  
  5.     return -EINVAL;  
  6. }  



接下來就是linux的suspend了,沒有再仔細看過,慚愧慚愧。

總的來說,屏幕的睡眠是和上層的keyguard沒有關係,是在WM和PMS以及相關的JNI的配合下對kernel的操作完成的。

屏幕喚醒和睡眠就寫到這裡,而背光的點亮過程,大部分處理是在PMS中,是在HAL層操作了lights的設備文件並不涉及到喚醒和睡眠,顯得比較簡單,有時間也寫出來分享。

抱歉!評論已關閉.