Android7.0 PowerManagerService 之亮滅屏(一)
本篇從按下power按鍵後,按鍵事件從InputManagerService 傳到PhoneWindowManager.java開始分析power 按鍵做屏幕亮滅過程的分析,關於power 按鍵的其他行爲參考另一篇博文(Android 7.0 Power 按鍵處理流程)
(注:博客園顯示的圖片很模糊,上傳的爲大圖,可以圖片另存爲查看)
言歸正傳,本篇涉及的幾個模塊(文件)如下,先做個簡單的介紹有個直觀大概的瞭解,方便後面流程細節的理解。
Ø PowerManagerService.Java:(/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java)
PMS,是Android系統中的電源處理服務,主要負責電源相關的計算和決策,如是否應該滅屏 或者讓屏幕變暗,是否應該讓系統休眠等等。
Ø DisplayPowerController.java:(/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java)
DPC,管理顯示設備(這裏指的顯示設備是屏幕)狀態,主要處理距離傳感器(如打電話時候靠近則滅屏,離開時候屏幕亮起)以及亮滅屏動畫(包括根據光感傳感器計算屏幕目標亮度值)。在DisplayManagerService.java(DMS)中實例化一個對象,以DMS爲橋樑與PMS進行交互通過異步回調機制來通知PMS那些發生了改變。同時也與WMS進行交互。
Ø DisplayPowerState.java:(/frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java)
DPS,管理顯示設備的狀態僅在DPC中實例化一個對象,是DPC的一部分。
Ø Notifier.java:( /frameworks/base/services/core/java/com/android/server/power/Notifier.java)
將電源狀態的重要變化,通過廣播通知出去。
Ø ColorFade.java:(/frameworks/base/services/core/java/com/android/server/display/ColorFade.java)
是負責屏幕由關到開,由開到關的一些GL動畫,由DPC進行控制。
Ø AutomaticBrightnessController.java:(/frameworks/base/services/core/java/com/android/server/display/AutomaticBrightnessController.java)
主要處理光傳感器,將底層上傳的參數進行處理計算,將計算的新的亮度值傳給DPC來設定屏幕的亮度值(即根據環境的光線強度來計算屏幕的亮暗程度)。
Ø RampAnimator.java:(/frameworks/base/services/core/java/com/android/server/display/RampAnimator.java)
僅僅是屏幕亮度漸變動畫。
一、Power按鍵的上報與處理
詳細見【Android 7.0 Power 按鍵處理流程】此處僅略微的複習一下,方便了後面的理解。
1)Power按鍵的上報
在InputManagerService收到power按鍵事件經過一系列的處理和轉換最終將會傳遞到PhoneWindowManager(PWM)的interceptKeyBeforeQueueing()函數來做具體的業務邏輯.下圖爲上報的流程圖。
2)power 按鍵關於量滅屏處理
關於量滅屏的處理主要在interceptKeyBeforeQueueing()函數中。判斷是否是isWakeKey,如果是則在此函數的最後通過調用wakeUp()函數來具體處理,這就拉開了本文的序幕。
注:此函數很長本文刪除無關的部分,僅僅保留部分和量滅屏相關的說明具體處理流程即可。詳細參考【Android 7.0 Power 按鍵處理流程】
A: 按鍵處理判斷是否要量滅屏
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 || event.isWakeKey(); if (interactive || (isInjected && !isWakeKey)) { // When the device is interactive or the key is injected pass the // key to the application. result = ACTION_PASS_TO_USER; isWakeKey = false; if (interactive) { // If the screen is awake, but the button pressed was the one that woke the device // then don't pass it to the application if (keyCode == mPendingWakeKey && !down) { result = 0; } // Reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } } else if (!interactive && shouldDispatchInputWhenNonInteractive(event)) { // If we're currently dozing with the screen on and the keyguard showing, pass the key // to the application but preserve its wake key status to make sure we still move // from dozing to fully interactive if we would normally go from off to fully // interactive. result = ACTION_PASS_TO_USER; // Since we're dispatching the input, reset the pending key mPendingWakeKey = PENDING_KEY_NULL; } 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; if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) { isWakeKey = false; } // Cache the wake key on down event so we can also avoid sending the up event to the app if (isWakeKey && down) { mPendingWakeKey = keyCode; } } // If the key would be handled globally, just return the result, don't worry about special // key processing. if (isValidGlobalKey(keyCode) && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) { if (isWakeKey) { wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); } return result; } .................... case KeyEvent.KEYCODE_ENDCALL: { result &= ~ACTION_PASS_TO_USER; if (down) { TelecomManager telecomManager = getTelecommService(); boolean hungUp = false; if (telecomManager != null) { hungUp = telecomManager.endCall(); } if (interactive && !hungUp) { mEndCallKeyHandled = false; mHandler.postDelayed(mEndCallLongPress, ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout()); } else { mEndCallKeyHandled = true; } } else { if (!mEndCallKeyHandled) { mHandler.removeCallbacks(mEndCallLongPress); if (!canceled) { if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { if (goHome()) { break; } } if ((mEndcallBehavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { mPowerManager.goToSleep(event.getEventTime(), PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); isWakeKey = false; } } } } break; } .................... if (useHapticFeedback) { performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); } if (isWakeKey) { wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY"); } return result; }
B:wakeUp處理量滅屏
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) { final boolean theaterModeEnabled = isTheaterModeEnabled(); if (!wakeInTheaterMode && theaterModeEnabled) { return false; } if (theaterModeEnabled) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); } mPowerManager.wakeUp(wakeTime, reason); //調用PowerManagerService亮屏操作 return true; } }
二、PowerManagerService處理量滅屏過程
從上面的分析可知,在PWM中處理按鍵事件如果需要喚醒屏幕則會調用PWM的wakeUp()函數,此函數會調用PMS 的wakeUp()函數來具體處理。
從上面的分析可知,在PWM中處理按鍵事件如果需要喚醒屏幕則會調用PWM的wakeUp()函數,此函數會調用PMS 的wakeUp()函數來具體處理。
注:限於篇幅本文僅列出重要的函數和調用過程
首先來個概覽,瞭解一下主要完成了如下三件事
1) 由Notifier根據系統的具體狀態來發出廣播
2) PMS 通過updatePowerStateLocked()計算和更新電源的全局狀態
3) PMS將最新的電源狀態等傳入DPC中,根據距離傳感器和光感傳感器,計算具體的屏幕亮度和量滅屏動畫等
4) DPC通知PWM繪製keyguard與windows(此時會block等待繪製完成,滅屏時候無需繪製),繪製完成後通知DPC繼續
5) DPC具體調用顯示設備開啓或關閉屏幕(執行量滅屏的動畫效果)
1)PowerManagerService--wakeUpNoUpdateLocked()
PMS首先講wakefullness狀態. 之後發送亮滅屏廣播通知其他應用手機處於亮屏還是滅屏狀態。
private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid, String opPackageName, int opUid) { if (DEBUG_SPEW) { Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); } if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE || !mBootCompleted || !mSystemReady) { return false; //判斷是否要去亮屏 } Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp"); try { switch (mWakefulness) { case WAKEFULNESS_ASLEEP: Slog.i(TAG, "Waking up from sleep due to"+opPackageName+" "+reason+" (uid " + reasonUid +")..."); break; case WAKEFULNESS_DREAMING: Slog.i(TAG, "Waking up from dream due to"+opPackageName+" "+reason+" (uid " + reasonUid +")..."); break; case WAKEFULNESS_DOZING: Slog.i(TAG, "Waking up from dozing due to"+opPackageName+" "+reason+" (uid " + reasonUid +")..."); break; } mLastWakeTime = eventTime; //設置最後一次喚醒的時間 setWakefulnessLocked(WAKEFULNESS_AWAKE, 0); //Notifier調用onWakefulnessChangeStarted發送亮屏廣播 mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid); //調用Notifier通知battery處理 userActivityNoUpdateLocked( //更新最後一次用戶事件時間 eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } return true; }
1)Notifier 發送量滅屏廣播
Notifier發送廣播前會與與AMS,WMS,IMS進行交互,通知各模塊電源狀態的改變,各模塊會自行處理電源狀態改變通知。
A:onWakefulnessChangeStarted()
public void onWakefulnessChangeStarted(final int wakefulness, int reason) { final boolean interactive = PowerManagerInternal.isInteractive(wakefulness); //亮屏true, 滅屏false if (DEBUG) { Slog.d(TAG, "onWakefulnessChangeStarted: wakefulness=" + wakefulness + ", reason=" + reason + ", interactive=" + interactive); } // Tell the activity manager about changes in wakefulness, not just interactivity. // It needs more granularity than other components. mHandler.post(new Runnable() { @Override public void run() { mActivityManagerInternal.onWakefulnessChanged(wakefulness); //與AMS交互處理 } }); // Handle any early interactive state changes. // Finish pending incomplete ones from a previous cycle. if (mInteractive != interactive) { // Finish up late behaviors if needed. if (mInteractiveChanging) { handleLateInteractiveChange(); } // Start input as soon as we start waking up or going to sleep. mInputManagerInternal.setInteractive(interactive); //在IMS中記錄現在的屏幕狀態 mInputMethodManagerInternal.setInteractive(interactive); // Notify battery stats. try { mBatteryStats.noteInteractive(interactive); //喚醒battery狀態 } catch (RemoteException ex) { } // Handle early behaviors. mInteractive = interactive; mInteractiveChangeReason = reason; mInteractiveChanging = true; handleEarlyInteractiveChange(); //初期處理交互模式改變 } }
B: handleEarlyInteractiveChange()
當屏幕在wakingup時需要通知window進行更新手勢監聽,更新方向監聽,更新鎖屏超時時間
private void handleEarlyInteractiveChange() { synchronized (mLock) { if (mInteractive) { // Waking up... //亮屏 mHandler.post(new Runnable() { @Override public void run() { EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0); mPolicy.startedWakingUp(); } }); // Send interactive broadcast. mPendingInteractiveState = INTERACTIVE_STATE_AWAKE; mPendingWakeUpBroadcast = true; updatePendingBroadcastLocked(); //更新亮屏廣播 } else { // Going to sleep... //滅屏 // Tell the policy that we started going to sleep. final int why = translateOffReason(mInteractiveChangeReason); mHandler.post(new Runnable() { @Override public void run() { mPolicy.startedGoingToSleep(why); } }); } }
(1):updatePendingBroadcastLocked()
private void updatePendingBroadcastLocked() { if (!mBroadcastInProgress && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast || mPendingInteractiveState != mBroadcastedInteractiveState)) { mBroadcastInProgress = true; mSuspendBlocker.acquire(); Message msg = mHandler.obtainMessage(MSG_BROADCAST); msg.setAsynchronous(true); mHandler.sendMessage(msg); } }
(2)sendNextBroadcast()
private void sendNextBroadcast() { final int powerState; synchronized (mLock) { if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) { // Broadcasted power state is unknown. Send wake up. mPendingWakeUpBroadcast = false; mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE; } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) { // Broadcasted power state is awake. Send asleep if needed. if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) { mPendingGoToSleepBroadcast = false; mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP; } else { finishPendingBroadcastLocked(); return; } } else { // Broadcasted power state is asleep. Send awake if needed. 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); if (powerState == INTERACTIVE_STATE_AWAKE) { sendWakeUpBroadcast();//這裏發送亮屏廣播 } else { sendGoToSleepBroadcast(); } }
(3)sendWakeUpBroadcast()
注意:Notifier 自身也會接收亮屏廣播,其受到後會調用finishPendingBroadcastLocked()函數來釋放wakeLock
private void sendWakeUpBroadcast() { if (DEBUG) { Slog.d(TAG, "Sending wake up broadcast."); } if (ActivityManagerNative.isSystemReady()) { mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null, mWakeUpBroadcastDone, mHandler, 0, null, null); } else { EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1); sendNextBroadcast(); } }