Android 8.0 手机亮灭屏
本文主要跟蹤分析通過按松power鍵來喚醒,熄滅屏幕的邏輯。下面是一些相關類的介紹
PowerManagerService.java:簡稱PMS,負責Andorid系統中電源管理方面的工作。作為系統核心服務之一,主要處理系統中與power相關的計算,然后決策系統該如何反應。同時PowerManagerService與其他服務及HAL層等都有交互關系,協調power如何與系統其他模塊的交互,比如沒有用戶活動時屏幕變暗等。
DisplayPowerController.java:簡稱DPC管理Display設備的電源狀態。僅在PowerManagerService中實例化了一個對象,它算是PowerManagerService的一部分,只不過是獨立出來了而已。主要處理和距離傳感器,
燈光傳感器,以及包括關屏在內的一些動畫,通過異步回調的方式來通知PowerManagerService某些事情發生了變化。
DisplayPowerState.java:簡稱DPS,在本質上類似于View,只不過用來描述一個display的屬性,當這些屬性發生變化時,可以通過一個序列化的命令,讓這些和display電源狀態的屬性一起產生變化。
這個類的對象只能被DispalyPowerController的Looper持有。而這個Looper應該就是PowerManagerService中新建的一個HandlerThread中的Looper。和PowerManager相關的,
包括DisplayPowerState和DisplayPowerController相關的消息處理應該都可以通過這個HandlerThread進行運轉的
Notifier.java:將power狀態的重要變化,通過廣播發送出去,并且參與了與AMS,WMS,IMP的交互。
ColorFade.java:負責屏幕由關到開,由開到關的一些GL動畫,由DPC進行控制。
AutomaticBrightnessController.java:主要處理光傳感器,將底層上傳的參數進行處理計算,將計算的新的亮度值傳給DPC。
RampAnimator.java:處理屏幕亮度漸變動畫。
正常我們按下power鍵后,出觸發Kernel關于該事件的中斷。然后inputReader通過EventHub獲取該事件,并做出事件處理,之后由inputdispatch進行事件分發。
因為power事件是key事件,會調用interceptKeyBeforeQueueing進行處理,通過如果流程
InputDispatcher.cpp
? ->com_android_server_input_InputManagerService.cpp
????????? ->InputManagerService.java
?????????????????? ->InputMonitor.java
?????????????????????????? ->WindowManagerPolicy.java
??????????????????????????????????? ->PhoneWindowManager.java
最終由PhoneWindowManager.java的interceptKeyBeforeQueueing方法對該次事件進行處理,對power鍵以及屏幕狀態進行判斷,來決定亮屏還是滅屏等操作。當需要亮屏時,會調用PowerMangerService中的wakeup函數進行處理。
主要代碼如下:
首先定義以下幾個布爾值,主要是按鍵屬性和屏幕狀態
//是否亮屏狀態,代碼是否可以與用戶交互
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
//按鍵事件是否為down事件
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
//事件是否被取消
final boolean canceled = event.isCanceled();
//按鍵的keycode,不同按鍵 keycode不同,一般power 116
final int keyCode = event.getKeyCode();
//是否是輸入事件
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
//鎖屏界面狀態
final boolean keyguardActive = (mKeyguardDelegate == null ? false :(interactive ?isKeyguardShowingAndNotOccluded() :mKeyguardDelegate.isShowing()));
//flags有wake標記,或者按鍵(isWakeKey函數)為KEYCODE_BACK, KEYCODE_MENU, KEYCODE_WAKEUP, KEYCODE_PAIRING, KEYCODE_STEM_1, KEYCODE_STEM_2, KEYCODE_STEM_3設置isWakeKey為true
boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 || event.isWakeKey();
?
?
if (isValidGlobalKey(keyCode)//有效的全局按鍵 (KEYCODE_POWER:KEYCODE_WAKEUP:KeyEvent.KEYCODE_SLEEP:),
????????? && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {//在com.android.internal.R.xml.global_keys 有定義
? if (isWakeKey) {
????????? wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");喚醒屏幕
? }
? return result;
}
?
接下來是對特殊按鍵的處理,這里只關注power鍵
?switch (keyCode) {
? ?case KeyEvent.KEYCODE_POWER: {
????????? result &= ~ACTION_PASS_TO_USER;
????????? isWakeKey = false; // wake-up will be handled separately
????????? if (down) {
?????????????????? interceptPowerKeyDown(event, interactive);//處理power按下
????????? } else {
?????????????????? interceptPowerKeyUp(event, interactive, canceled);//處理power松開
????????? }
????????? break;
? }
?
power down 鍵的處理
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
? // 獲取wakelock,報錯CPU喚醒狀態
? if (!mPowerKeyWakeLock.isHeld()) {
????????? mPowerKeyWakeLock.acquire();
? }
?
? // 取消多次按下超時監測
? if (mPowerKeyPressCounter != 0) {
????????? mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);
? }
?
? // Detect user pressing the power button in panic when an application has
? // taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
?????????????????? SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags),
?????????????????? isNavBarEmpty(mLastSystemUiFlags));
? if (panic) {
????????? mHandler.post(mHiddenNavPanic);
? }
?
? // Latch power key state to detect screenshot chord.
? if (interactive && !mScreenshotChordPowerKeyTriggered
?????????????????? && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
????????? mScreenshotChordPowerKeyTriggered = true;
????????? mScreenshotChordPowerKeyTime = event.getDownTime();
????????? interceptScreenshotChord();//屏幕截屏
????????? interceptDisableTouchModeChord();? //添加口袋模式
? }
?
? // 當power鍵按下,停止電話響鈴或者結束通話
? TelecomManager telecomManager = getTelecommService();
? boolean hungUp = false;
? if (telecomManager != null) {
????????? if (telecomManager.isRinging()) {
?????????????????? 停止響鈴
?????????????????? telecomManager.silenceRinger();
????????? } else if ((mIncallPowerBehavior
?????????????????????????? & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
?????????????????????????? && telecomManager.isInCall() && interactive) {
?????????????????? Setting數據庫讀取設置,如果開了掛斷電話
?????????????????? hungUp = telecomManager.endCall();
????????? }
? }
?
? SprdGestureLauncherService gestureService = LocalServices.getService(
?????????????????? SprdGestureLauncherService.class);
? boolean gesturedServiceIntercepted = false;
? if (gestureService != null) {
????????? gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
?????????????????????????? mTmpBoolean);
????????? if (mTmpBoolean.value && mGoingToSleep) {
?????????????????? mCameraGestureTriggeredDuringGoingToSleep = true;
????????? }
? }
?
? // If the power key has still not yet been handled, then detect short
? // 如果power鍵還沒有被處理,判斷是短按,多按,長按等場景并做出對應處理
? mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered
?????????????????? || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
? if (!mPowerKeyHandled) {
????????? if (interactive) {
?????????????????? //如果屏幕是亮的,長按動作處理對應的長按動作
?????????????????? if (hasLongPressOnPowerBehavior()) {
?????????????????????????? Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
?????????????????????????? msg.setAsynchronous(true);
?????????????????????????? mHandler.sendMessageDelayed(msg,
??????????????????????????????????? ??????? ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
?????????????????? }
? ??????? } else {//屏幕休眠的狀態,喚醒屏幕
?????????????????? wakeUpFromPowerKey(event.getDownTime());
?
?????????????????? if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
?????????????????????????? Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
?????????????????????????? msg.setAsynchronous(true);
?????????????????????????? mHandler.sendMessageDelayed(msg,//如果長按動作繼續執行長按操作
??????????????????????????????????? ??????? ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
?????????????????????????? mBeganFromNonInteractive = true;
?????????????????? } else {
?????????????????????????? final int maxCount = getMaxMultiPressPowerCount();
?
?????????????????????????? if (maxCount <= 1) {
??????????????????????????????????? mPowerKeyHandled = true;
?????????????????????????? } else {
??????????????????????????????????? mBeganFromNonInteractive = true;
......
power up 鍵的處理
private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {
? final boolean handled = canceled || mPowerKeyHandled; //power鍵是否被處理了
? mScreenshotChordPowerKeyTriggered = false;
? cancelPendingScreenshotChordAction();
? cancelPendingPowerKeyAction();
?
? if (!handled) {
????????? // power press 加1
????????? mPowerKeyPressCounter += 1;
????????
????????? final int maxCount = getMaxMultiPressPowerCount();//多按的次數
????????? final long eventTime = event.getDownTime();//press power 時間
????????? if (mPowerKeyPressCounter < maxCount) {
?????????????????? // 這種情況可能是一個多次按鍵事件,等待一會做確認處理
?????????????????? Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,
??????????????????????????????????? interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);
?????????????????? msg.setAsynchronous(true);
?????????????????? mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());
?????????????????? return;
????????? }
?
????????? // 沒有其他action,繼續處理power按下事件
????????? powerPress(eventTime, interactive, mPowerKeyPressCounter);
? }
?
? // Done.? Reset our state.
? finishPowerKeyPress();//power按鍵處理結束
}
private void finishPowerKeyPress() {
? mBeganFromNonInteractive = false;
? mPowerKeyPressCounter = 0;
? if (mPowerKeyWakeLock.isHeld()) {
????????? mPowerKeyWakeLock.release();//釋放wakelock
? }
}
下面繼續跟蹤powerPress函數
private void powerPress(long eventTime, boolean interactive, int count) {
? if (mScreenOnEarly && !mScreenOnFully) {
????????? Slog.i(TAG, "Suppressed redundant power key press while "
?????????????????????????? + "already in the process of turning the screen on.");
????????? return;
? }
?
? if (count == 2) {//按兩次power事件
????????? powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
? } else if (count == 3) {//按三次power事件
????????? powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
? } else if (interactive && !mBeganFromNonInteractive) {//屏幕如果亮著而且mBeganFromNonInteractive為false會執行下面操作
????????? switch (mShortPressOnPowerBehavior) {//根據配置om.android.internal.R.integer.config_shortPressOnPowerBehavior 決定對應值 此處為1
?????????????????? case SHORT_PRESS_POWER_NOTHING:
?????????????????????????? break;
?????????????????? case SHORT_PRESS_POWER_GO_TO_SLEEP:
?????????????????????????? mPowerManager.goToSleep(eventTime,
??????????????????????????????????? ??????? PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);//執行滅屏操作
?????????????????????????? break;
?????????????????? ......
}
?
我們繼續跟蹤亮屏操作,從power down事件處理可知,調用wakeUpFromPowerKey點亮屏幕
private void wakeUpFromPowerKey(long eventTime) {
? Slog.d(TAG, "wake Up From Power Key"); // 此處會打印這句log,可作為分析問題是注意點
? wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");//調用wakeup繼續執行亮屏操作,注意調用mPowerManager.wakeUp操作
}
下面我們看下PowerManager.wakeUp的相關處理
public void wakeUp(long time) {
? try {
????????? mService.wakeUp(time, "wakeUp", mContext.getOpPackageName());//調用到PWM.wakeUpInternal->PWM.wakeUpNoUpdateLocked
? } catch (RemoteException e) {
????????? throw e.rethrowFromSystemServer();
? }
}
?
當power接收到亮滅屏調用后,會先進行設置手機wakefullness狀態. 之后發送亮滅屏廣播通知其他應用手機處于亮屏還是滅屏狀態。
private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
????????? String opPackageName, int opUid) {
? //mWakefulness標識系統當前狀態共有四種定義:
? WAKEFULNESS_ASLEEP:表示系統當前處于休眠狀態,只能被wakeUp()調用喚醒。
? WAKEFULNESS_AWAKE:表示系統目前處于正常運行狀態。
? WAKEFULNESS_DREAMING:表示系統當前正處于互動屏保的狀態。
? WAKEFULNESS_DOZING:表示系統正處于“doze”狀態
? if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
?????????????????? || !mBootCompleted || !mSystemReady) {//按鍵時間在睡下去之前,屏幕是正常狀態,沒有完成開機流程,系統沒有準備完成,這四種情況不繼續執行喚醒流程
????????? return false;
? }
?
? try {//系統不是點亮狀態,打出相關log
????????? switch (mWakefulness) {
?????????????????? case WAKEFULNESS_ASLEEP:
?????????????????? case WAKEFULNESS_DREAMING:
?????????????????? case WAKEFULNESS_DOZING:
?????????????????????????? Slog.i(TAG, "Waking up from dozing due to"+opPackageName+" "+reason+" (uid " + reasonUid +")...");
?????????????????????????? break;
????????? }
?
????????? mLastWakeTime = eventTime;//更新wake時間
????????? setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);//更新mWakefulness,并調用Notifier.onWakefulnessChangeStarted發送亮屏廣播?
????????? mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);//調用Notifier通知battery處理?
????????? userActivityNoUpdateLocked(eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);//更新最后一次用戶事件的時間
? } finally {
? }
? return true;
}
?
然后主要就是在Notifier.java中與AMS,window,input進行交互,通知各模塊手機狀態發生了改變,根據屏幕狀態各自進行處理, 最后發送亮滅屏廣播, 通知相關的模塊.
private void setWakefulnessLocked(int wakefulness, int reason) {
? if (mWakefulness != wakefulness) {
????????? mWakefulness = wakefulness;//更新mWakefulness變量
????????? mWakefulnessChanging = true;
????????? mDirty |= DIRTY_WAKEFULNESS;
????????? mNotifier.onWakefulnessChangeStarted(wakefulness, reason);//調用Notify的函數onWakefulnessChangeStarted 發廣播通知
? }
}
public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
??? //獲取交互模式變量,亮屏為true, 滅屏為false
? final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);
?
? 通知AMS wakefulness狀態的變化
? mHandler.post(new Runnable() {
????????? @Override
????????? public void run() {
?????????????????? mActivityManagerInternal.onWakefulnessChanged(wakefulness);
????????? }
? });
?
? //interactive 變化時才會執行里面的邏輯
? if (mInteractive != interactive) {
????????? // Finish up late behaviors if needed.
????????? if (mInteractiveChanging) {
?????????????????? handleLateInteractiveChange();
????????? }
?
????????? //在input中記錄現在的屏幕狀態
????????? mInputManagerInternal.setInteractive(interactive);
????????? mInputMethodManagerInternal.setInteractive(interactive);
?
????????? //喚醒battery狀態
????????? try {
?????????????????? mBatteryStats.noteInteractive(interactive);
????????? } catch (RemoteException ex) { }
?
????????? // Handle early behaviors.
????????? mInteractive = interactive;
????????? mInteractiveChangeReason = reason;
????????? mInteractiveChanging = true;
????????? handleEarlyInteractiveChange();//處理交互模式改變
? }
}
?
private void handleEarlyInteractiveChange() {
? synchronized (mLock) {
????????? if (mInteractive) {//mInteractive 為true代表要執行亮屏邏輯
?????????????????? // Waking up...
?????????????????? mHandler.post(new Runnable() {
?????????????????????????? @Override
?????????????????????????? public void run() {
??????????????????????????????????? // Note a SCREEN tron event is logged in PowerManagerService.
??????????????????????????????????? mPolicy.startedWakingUp();//調用PhoneWindowManager 方法開始亮屏流程
?????????????????????????? }
?????????????????? });
?
?????????????????? // 更新亮屏廣播
?????????????????? mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;
?????????????????? mPendingWakeUpBroadcast = true;
?????????????????? updatePendingBroadcastLocked();//mSuspendBlocker.acquire();持有partial 鎖
????????? } else {//mInteractive 為false代表要執行滅屏邏輯
?????????????????? final int why = translateOffReason(mInteractiveChangeReason);
?????????????????? mHandler.post(new Runnable() {
?????????????????????????? @Override
?????????????????????????? public void run() {
??????????????????????????????????? mPolicy.startedGoingToSleep(why);//調用PhoneWindowManager 方法開始滅屏流程
?????????????????????????? }
?????????????????? });
????????? }
? }
}
?
調用updatePendingBroadcastLocked函數發出handle msg 消息
Message msg = mHandler.obtainMessage(MSG_BROADCAST);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
然后handle收到后執行sendNextBroadcast函數
case MSG_BROADCAST:
? sendNextBroadcast();
? break;
private void sendNextBroadcast() {
? final int powerState;
? //mBroadcastedInteractiveState 這個變量記錄當前的系統亮滅屏情況
? synchronized (mLock) {
????????? if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
?????????????????? // 當前狀態位置更新為喚醒狀態
?????????????????? mPendingWakeUpBroadcast = false;
?????????????????? mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
????????? } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
?????????????????? // 當前是喚醒狀態,更新為滅屏狀態
?????????????????? if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
??????????????????????????????????? || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {
?????????????????????????? mPendingGoToSleepBroadcast = false;
?????????????????????????? mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
?????????????????? } else {
?????????????????????????? finishPendingBroadcastLocked();//釋放partial 鎖
?????????????????????????? return;
?????????????????? }
????????? } else {
?????????????????? // 當前是滅屏狀態,更新為喚醒狀態
?????????????????? if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
?????????????????? ???????????????? || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {
?????????????????????????? mPendingWakeUpBroadcast = false;
?????????????????????????? mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
?????????????????? } else {
?????????????????????????? finishPendingBroadcastLocked();
?????????????????????????? return;
?????????????????? }
????????? }
????????? //記錄下當前系統時間為廣播開始時間
????????? mBroadcastStartTime = SystemClock.uptimeMillis();
????????? powerState = mBroadcastedInteractiveState;
? }
?
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_SEND, 1);//打出event log
?
? if (powerState == INTERACTIVE_STATE_AWAKE) {//根據powerState的值決定發亮屏還是滅屏廣播
????????? sendWakeUpBroadcast();
? } else {
????????? sendGoToSleepBroadcast();
? }
}
廣播都是前臺廣播,然后其他模塊收到廣播后作出對應處理
mScreenOnIntent.addFlags(
? Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
? | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
mScreenOffIntent.addFlags(
? Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
? | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
Notify 收到這個廣播后會,繼續調用sendNextBroadcast,因為mPendingWakeUpBroadcast值變化,會調用finishPendingBroadcastLocked();釋放partial 鎖
private final BroadcastReceiver mWakeUpBroadcastDone = new BroadcastReceiver() {
? @Override
? public void onReceive(Context context, Intent intent) {
? ?? EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 1,
?????????????????????????? SystemClock.uptimeMillis() - mBroadcastStartTime, 1);
????????? sendNextBroadcast();
? }
};
總結:當power接收到亮滅屏調用后,會先進行設置手機wakefullness狀態,之后發送亮滅屏廣播通知其他模塊手機處于亮屏還是滅屏狀態。并且在發送廣播過程中power也與AMS,window,input進行交互,通知各模塊手機狀態發生了改變,根據屏幕狀態各自進行處理。其中發送亮滅屏廣播的主要實現在Notifier.java中。
當wakefulness狀態發生改變,AMS收到通知。如果亮屏操作,AMS就會通過函數comeOutOfSleepIfNeedLocked調用到ActivityStackSupervisor中,將sleep超時消息移除,最后將在棧頂的activity顯示出來。
power是通過WindowManagerPolicy與PhoneWindowManager進行交互,當屏幕在喚醒時需要通知window進行更新手勢監聽,更新方向監聽,更新鎖屏超時時間。最后通知keyguard屏幕點亮了,進行刷新鎖屏界面時間,之后通過回調接口,回調回PhoneWindowManager的onShown函數,通知window keyguard準備完成。
當亮屏時通過InputManagerService將當前屏幕狀態傳入JNI中進行記錄,當再次發生power鍵事件可以方便確認該事件是需要亮屏還是滅屏。
?
當廣播處理完畢后就會調用PMS的內部函數updatePowerStateLocked來更新全局電源狀態。其實在PMS中很多函數都只是對一些必須的屬性進行賦值,大部分最終都會調用到updatePowerStateLocked函數中進行功能執行,其中更新電源狀態主要依據就是變量mDirty。mDirty就是用來記錄power state的變化的標記位,這樣的狀態變化在系統中一共定義了12個,每一個狀態對應一個固定的數字,都是2的倍數。這樣,若有多個狀態一塊變化,進行按位取或這樣結果既是唯一的,又能準確標記出各個狀態的變化。在updatePowerStateLocked中主要做了如下事情:
- 首先判斷手機是否處于充電狀態,如果標記位DIRTY_BATTERY_STATE發生改變時就證明電池狀態發生了改變,然后通過對比判斷(通過電池前后變化與充電狀態的變化),確認手機是否處于充電狀態。如果手機處于充電狀態會將表示充電的標記位記入mDirty中。當有USB插拔時我們也可以通過配置信息來決定是否需要點亮屏幕。并且是否在充電或者充電方式發生改變,系統都會認為發生了一次用戶事件進行更新最后用戶操作時間,以此來重新計算屏幕超時時間。
private void updateIsPoweredLocked(int dirty) {
? if ((dirty & DIRTY_BATTERY_STATE) != 0) {
????????? final boolean wasPowered = mIsPowered;//是否在充電?
????????? final int oldPlugType = mPlugType;//充電類型?
????????? final boolean oldLevelLow = mBatteryLevelLow;//低電模式
????????? mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
????????? mPlugType = mBatteryManagerInternal.getPlugType();
????????? mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
????????? mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
????????? if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
?????????????????? mDirty |= DIRTY_IS_POWERED;//如果充電中設置mDirty
?????????????????? /* < Bug#696466 optimization for sceenoff */
?????????????????? if (wasPowered != mIsPowered)
?????????????????????????? SystemProperties.set("sys.sprd.power.ispowered", (mIsPowered? "1": "0"));//設置屬性sys.sprd.power.ispowered
?????????????????? /* Bug#696466 optimization for sceenoff > */
?????????????????? //無線充電
?????????????????? final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
??????????????????????????????????? mIsPowered, mPlugType, mBatteryLevel);
?????????????????? final long now = SystemClock.uptimeMillis();
?????????????????? if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
??????????????????????????????????? dockedOnWirelessCharger)) {//插拔USB是否要點亮屏幕
?????????????????????????? wakeUpNoUpdateLocked(now, "android.server.power:POWER", Process.SYSTEM_UID,
??????????????????????????????????????????? mContext.getOpPackageName(), Process.SYSTEM_UID);
?????????????????? }
?????????????????? userActivityNoUpdateLocked(//插拔USB算一次用戶事件,重新設置最后一次用戶事件的時間點
??????????????????????????????????? now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
?????????????????? // Tell the notifier whether wireless charging has started so that
?????????????????? // it can provide feedback to the user.
?????????????????? if (dockedOnWirelessCharger) {
?????????????????????????? mNotifier.onWirelessChargingStarted();
?????????????????? }
????????? }
????????? if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {//低電模式
?????????????????? if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
?????????????????????????? mAutoLowPowerModeSnoozing = false;
?????????????????? }
?????????????????? updateLowPowerModeLocked();//更新低電模式
????????? }
? }
}
- 更新wakefulness
for (;;) {
? int dirtyPhase1 = mDirty;
? dirtyPhase2 |= dirtyPhase1;
? mDirty = 0;
? updateWakeLockSummaryLocked(dirtyPhase1);
? updateUserActivitySummaryLocked(now, dirtyPhase1);
? if (!updateWakefulnessLocked(dirtyPhase1)) {
????????? break;
? }
}
updateWakeLockSummaryLocked函數將wakeLock的類型用mWakeLockSummary進行記錄,最后與Wakefulness狀態結合重新算出新的mWakeLockSummary值,再判斷是否需要睡眠時會使用。
之后updateUserActivitySummaryLocked就會更新屏幕超時時間,根據最后一次的用戶事件與屏幕超時時間與dim持續時間來計算屏幕超時的時間,然后與現在的時間進行對比,來決定屏幕要繼續高亮,還是要變為dim狀態。
PowerManagerHandler接收屏幕超時的消息, 并且調用handleUserActivityTimeout進行處理, 該函數之后就在Handler線程中運行
?
private final class PowerManagerHandler extends Handler {?
??? public PowerManagerHandler(Looper looper) {?
??????? super(looper, null, true /*async*/);?
??? }?
?
??? @Override?
??? public void handleMessage(Message msg) {?
??????? switch (msg.what) {?
??????????? case MSG_USER_ACTIVITY_TIMEOUT:?
??????????????? handleUserActivityTimeout();??? //處理用戶超時事件?
??????????????? break;?
??????????? case MSG_SANDMAN:?
??????????????? handleSandman();?
??????????????? break;?
??????????? case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:?
????? ??????????handleScreenBrightnessBoostTimeout();?
??????????????? break;?
??????? }?
??? }?
}?
?
private void handleUserActivityTimeout() { // runs on handler thread?
??? synchronized (mLock) {?
??????? if (DEBUG_SPEW) {?
??????????? Slog.d(TAG, "handleUserActivityTimeout");?
??????? }?
?
??????? mDirty |= DIRTY_USER_ACTIVITY;? //設置有用戶活動的mDirty值?
??????? updatePowerStateLocked();?? //更新電源狀態, 最后去判斷是否要睡眠?
??? }?
}?
?
根據前面流程圖可以看出更新wakefulness過程是通過一個死循環來執行的,只有調用函數updateWakefulnessLocked返回false時才會跳出循環。在循環中對wakeLockSummary進行更新,并且更新自動滅屏時間后,進行判斷系統是否該睡眠了,是否可以跳出循環。
在updateWakefulnessLocked中主要根據是否存在wakeLock,用戶活動進行判斷設備是否需要進入睡眠狀態。從函數isBeingKeptAwakeLocked可以看出當device拿著一個wake lock,有用戶事件,有距離傳感器等都不會滅屏進行睡眠狀態。如果需要睡眠就會往下面調用,最后跳出循環。
private boolean updateWakefulnessLocked(int dirty) {?
??? boolean changed = false;?
??? if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED?
??????????? | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE?
??????????? | DIRTY_DOCK_STATE)) != 0) {?
??????? if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) {?? //mWakefulness為AWAKE, 并且到了睡覺時間, 就去睡覺?
??????????? if (DEBUG_SPEW) {?
??????????????? Slog.d(TAG, "updateWakefulnessLocked: Bed time...");????
??????????? }?
??????????? final long time = SystemClock.uptimeMillis();?
??????????? if (shouldNapAtBedTimeLocked()) {??
??????????????? changed = napNoUpdateLocked(time, Process.SYSTEM_UID);? //睡覺前先小憩一會?
??????????? } else {?
??????????????? changed = goToSleepNoUpdateLocked(time,?
??????????????????????? PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);? //直接睡覺?
??????????? }?
??????? }?
??? }?
??? return changed;?
}?
[java] view plain copy
private boolean isItBedTimeYetLocked() {// 對所有該喚醒的情況取反, 就是該休眠了?
??? return mBootCompleted && !isBeingKeptAwakeLocked();???
}?
private boolean isBeingKeptAwakeLocked() {?
??? return mStayOn? //設置了stay on?
??????????? || mProximityPositive? //距離傳感器返回一個positive結果,保持喚醒?
??????????? || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0? //當有wake lock時保持喚醒?
??????????? || (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT?
??????????????????? | USER_ACTIVITY_SCREEN_DIM)) != 0?? //有user activity時保持喚醒?
??????????? || mScreenBrightnessBoostInProgress;?
}?
[java] view plain copy
???? mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver,?
??????????? Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,?
??????????? mDreamsActivatedOnSleepByDefaultConfig ? 1 : 0,?
??????????? UserHandle.USER_CURRENT) != 0);???? //從settings數據庫獲取對應值?
??? mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver,?
??????????? Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,?
??????????? mDreamsActivatedOnDockByDefaultConfig ? 1 : 0,?
??????????? UserHandle.USER_CURRENT) != 0);?
?
?private boolean shouldNapAtBedTimeLocked() {? //當返回true, 設備自動nap?
??? return mDreamsActivateOnSleepSetting?
??????????? || (mDreamsActivateOnDockSetting?
??????????????????? && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED);?
}?
[java] view plain copy
private boolean napNoUpdateLocked(long eventTime, int uid) {?
??? if (DEBUG_SPEW) {?
??????? Slog.d(TAG, "napNoUpdateLocked: eventTime=" + eventTime + ", uid=" + uid);?
??? }?
?
??? if (eventTime < mLastWakeTime || mWakefulness != WAKEFULNESS_AWAKE?
??????????? || !mBootCompleted || !mSystemReady) {?
??????? return false;?
??? }?
?
??? Trace.traceBegin(Trace.TRACE_TAG_POWER, "nap");?
??? try {?
??????? Slog.i(TAG, "Nap time (uid " + uid +")...");?
?
??????? mSandmanSummoned = true;?
??????? setWakefulnessLocked(WAKEFULNESS_DREAMING, 0);? //設置WAKEFULNESS_DREAMING?
??? } finally {?
??????? Trace.traceEnd(Trace.TRACE_TAG_POWER);?
??? }?
??? return true;?
}?
調用goToSleepNoUpdateLocked進行睡眠, 當按power鍵滅屏是也會調用該函數.
?
[java] view plain copy
private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {?
??? if (DEBUG_SPEW) {?
??????? Slog.d(TAG, "goToSleepNoUpdateLocked: eventTime=" + eventTime?
??????????????? + ", reason=" + reason + ", flags=" + flags + ", uid=" + uid);?
??? }?
?
??? if (eventTime < mLastWakeTime?
??????????? || mWakefulness == WAKEFULNESS_ASLEEP?
??????????? || mWakefulness == WAKEFULNESS_DOZING?
??????????? || !mBootCompleted || !mSystemReady) {?
??????? return false;?? //判斷設備是否應該睡眠?
??? }?
?
??? Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");?
??? try {?
??????? switch (reason) {?? //輸出滅屏的原因?
??????????? case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN:?
??????????????? Slog.i(TAG, "Going to sleep due to device administration policy "?
??????????????????????? + "(uid " + uid +")...");?
??????????????? break;?
??????????? case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT:?
??????????????? Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")...");?
?????? ?????????break;?
??????????? case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH:?
??????????????? Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")...");?
??????????????? break;?
??????????? case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON: ?
??????????????? Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")...");?
??????????????? break;?
??????????? case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON:?
??????????????? Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")...");?
??????????????? break;?
??????????? case PowerManager.GO_TO_SLEEP_REASON_HDMI:?
??????????????? Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")...");?
??????????????? break;?
??????????? default:?
??????????????? Slog.i(TAG, "Going to sleep by application request (uid " + uid +")...");?
??????????????? reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION;?
??????????????? break;?
??????? }?
?
??????? mLastSleepTime = eventTime;?
??????? mSandmanSummoned = true;?
? ??????setWakefulnessLocked(WAKEFULNESS_DOZING, reason);?
?
??????? // Report the number of wake locks that will be cleared by going to sleep.?
??????? int numWakeLocksCleared = 0;?
??????? final int numWakeLocks = mWakeLocks.size();???
??????? for (int i = 0; i < numWakeLocks; i++) {? //遍歷所有的wakeLocks, 將FULL, BRIGHT, DIM Locks,計入numWakeLocksCleared中?
??????????? final WakeLock wakeLock = mWakeLocks.get(i);?
??????????? switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {?
??????????????? case PowerManager.FULL_WAKE_LOCK:?
??????????????? case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:?
??????????????? case PowerManager.SCREEN_DIM_WAKE_LOCK:?
??????????????????? numWakeLocksCleared += 1;?
??????????????????? break;?
??????????? }?
??????? }?
??????? EventLog.writeEvent(EventLogTags.POWER_SLEEP_REQUESTED, numWakeLocksCleared);?
?
??????? // Skip dozing if requested.?
??????? if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {?
??????????? reallyGoToSleepNoUpdateLocked(eventTime, uid); //如果沒有doze流程,直接設置WAKEFULNESS_ASLEEP?
??????? }?
??? } finally {?
??????? Trace.traceEnd(Trace.TRACE_TAG_POWER);?
??? }?
??? return true;?? //返回true?
}?
當第一個for循環中將所有的狀態都設置好了, 并且此時也沒有重要的mDirty發生變化, 在下一次循環中mDirty的值為0, updateWakefulnessLocked返回false,就會跳出循環.
當跳出循環之后在函數updateDisplayPowerStateLocked中進行獲取需要請求的設備電源狀態是亮還是滅或者dim,判斷是否開啟了自動調節亮度開關,是否使用了距離傳感器,并經過一系列的計算獲取亮度值等,最終都記錄到DisplayPowerRequest中,經過DMS傳入DPC中,進行處理。
在PowerManagerService中對各種狀態進行判斷后,將其數值封裝進DisplayPowerRequest中傳入DisplayPowerController中進一步處理。在亮屏過程中DisplayPowerController會根據傳過來的數值來設置新的電源狀態為亮屏,然后調用DisplayPowerState來對狀態進行設置。由于此時ColorFade level(就是在手機屏幕的一層surface,當level為0是為一層黑幀,level為1.0時為透明)的值為0,表示屏幕還沒有繪制好,所以此時需要block screen直到window界面繪制完成。當需要亮屏時調用PhoneWindowManager的screenTurningOn函數,通知window屏幕就要點亮了。然后調用WMS中函數waitForAllWindowsDrawn函數等待將所有需要繪制的window繪制完成后回調回來,超時時間為1000ms。在WMS中獲取需要繪制的window將其加入mWaitingForDrawn中等待繪制,通過檢查mWaitingForDrawn是否為空來判斷,window是否繪制完成。此時screenTurningOn函數就執行完了,剩下的就是等待windows繪制完成。
總結
以上是生活随笔為你收集整理的Android 8.0 手机亮灭屏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C语言】用回调函数实现冒泡排序
- 下一篇: 62道开发人员面试经典题