天天看點

android螢幕喚醒函數,android學習筆記 按電源鍵螢幕喚醒和螢幕睡眠流程(從上層到kernel)...

一. 螢幕的喚醒

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

//摘錄部分代碼:

"font-weight: bold; ">publicintinterceptKeyBeforeQueueing(longwhenNanos,intaction,intflags,

intkeyCode,intscanCode,intpolicyFlags,booleanisScreenOn) {

finalbooleandown = action == KeyEvent.ACTION_DOWN;

finalbooleancanceled = (flags & KeyEvent.FLAG_CANCELED) !=0;

finalbooleanisInjected = (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.

finalbooleankeyguardActive = (isScreenOn ?

mKeyguardMediator.isShowingAndNotHidden() :

mKeyguardMediator.isShowing());

intresult;//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;

finalbooleanisWakeKey = (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.

"color:#ff0000;"> mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);

} else{

// Otherwise, wake the device ourselves.

result |= ACTION_POKE_USER_ACTIVITY;

}

}

}"font-weight: bold; ">

....................

}

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(inton)

{

//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)returng_error;

charbuf[32];

intlen;

if(on)

len = sprintf(buf, "%s", on_state);

else

len = sprintf(buf, "%s", off_state);

"color:#cc0000;">len = write(g_fds[REQUEST_STATE], buf, len);//此處就是寫了kernel的裝置檔案接口。

if(len

LOGE("Failed setting last user activity: g_error=%d\n", g_error);

}

return0;

}

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

}

staticssize_t state_store(structkobject *kobj,structkobj_attribute *attr,

constchar*buf,size_tn)

{

#ifdef CONFIG_SUSPEND

#ifdef CONFIG_EARLYSUSPEND

suspend_state_t state = PM_SUSPEND_ON;

#else

suspend_state_t state = PM_SUSPEND_STANDBY;

#endif

constchar*const*s;

#endif

char*p;

intlen;

interror = -EINVAL;

p = memchr(buf, '\n', n);

len = p ? p - buf : n;

if(len == 4 && !strncmp(buf,"disk", len)) {

error = hibernate();

gotoExit;

}

#ifdef CONFIG_SUSPEND

for(s = &pm_states[state]; state

if(*s && len == strlen(*s) && !strncmp(buf, *s, len))

break;

}

printk("##: enter %s\n", pm_states[state]);

if(state

#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");

returnerror ? error : n;

}

kernel/kernel/power/erlysuspend.c                                                              request_suspend_state

voidrequest_suspend_state(suspend_state_t new_state)

{

unsigned longirqflags;

intold_sleep;

powerkey_wdt_stop();

spin_lock_irqsave(&state_lock, irqflags);

old_sleep = state & SUSPEND_REQUESTED;

if(debug_mask & DEBUG_USER_STATE) {

structtimespec ts;

structrtc_timetm;

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);

}"color: rgb(255, 0, 0); background-color: rgb(255, 255, 255);">"background-color: rgb(255, 255, 255); ">elseif(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

}

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

staticvoidlate_resume(structwork_struct *work)

{

structearly_suspend *pos;

unsigned longirqflags;

intabort = 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);

gotoabort;

}

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

staticvoidearly_suspend(structwork_struct *work)

{

structearly_suspend *pos;

unsigned longirqflags;

intabort = 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);

gotoabort;

}

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);

"background-color: rgb(204, 204, 204);">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

voidwake_unlock(structwake_lock *lock)

{

inttype;

unsigned longirqflags;

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) {

longhas_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

staticvoidsuspend(structwork_struct *work)

{

intret;

intentry_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) {

structtimespec ts;

structrtc_timetm;

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

intpm_suspend(suspend_state_t state)

{

if(state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

returnenter_state(state);//是不是和main.c裡的state_store函數中liunx的suspend一樣?豁然開朗。

return-EINVAL;

}

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

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

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