一. 屏幕的喚醒
首先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
//摘錄部分代碼:
- <span style="font-weight: bold; "> </span> 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即為返回到wmaction
- if (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.
- <span style="color:#ff0000;"> </span> mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
- } else {
- // Otherwise, wake the device ourselves.
- result |= ACTION_POKE_USER_ACTIVITY;
- }
- }
- }<span style="font-weight: bold; ">
- ....................
- }</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的了,貼出代碼看看:
- 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);
- else
- len = sprintf(buf, "%s", off_state);
- <span style="color:#cc0000;"> </span>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_EARLYSUSPEND
- suspend_state_t state = PM_SUSPEND_ON;
- #else
- suspend_state_t state = PM_SUSPEND_STANDBY;
- #endif
- const char * const *s;
- #endif
- char *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_SUSPEND
- for (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);
- }
- #else
- error = enter_state(state);
- #endif
- #endif
- Exit:
- 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);
- }<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) {
- state &= ~SUSPEND_REQUESTED;
- wake_lock(&main_wake_lock); //acquire main ——wakelock
- queue_work(suspend_work_queue, &late_resume_work); //將喚醒的work起來,開始執行之前聲明的late_resume_work
- }</span>
- 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;
- else
- abort = 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;
- else
- abort = 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);
- <span style="background-color: rgb(204, 204, 204);">pos->suspend(pos); //調用註冊了earlysuspend的drv的suspend函數,調用到了fb_suspend,屏幕就會進入睡眠,睡眠的過程就結束了</span>
- }
- }
- 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_STAT
- wake_unlock_stat_locked(lock, 0);
- #endif
- if (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_STAT
- update_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的設備文件並不涉及到喚醒和睡眠,顯得比較簡單,有時間也寫出來分享。