Preface
There are many ways to light up the screen, the most commonly used one is to light up the screen with the Power key. This process is relatively simple. This article hopes to analyze this process to clarify the useful process of operating the screen and lay the foundation for the subsequent articles.
Power keys to light up the screen
This article uses the Power key to light up the screen as an example for analysis, and it will callPowerManagerService#wakeUp()
// @Override // Binder call public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) { // ... try { // Can only wake up the display screen under default display group wakeDisplayGroup(Display.DEFAULT_DISPLAY_GROUP, eventTime, reason, details, uid, opPackageName, uid); } finally { (ident); } } private void wakeDisplayGroup(int groupId, long eventTime, @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { synchronized (mLock) { // 1. Update wakefulness to WAKEFULNESS_AWAKE // Including wakefulness that updates PowerManagerService and DisplayGroupPowerStateMapper if (wakeDisplayGroupNoUpdateLocked(groupId, eventTime, reason, details, uid, opPackageName, opUid)) { // 2. Update power status updatePowerStateLocked(); } } }
Notice,PowerManagerService#wakeUp()Only the screens under default grouping can be operated.
Android has added a grouping function to multiple screens since some time ago. Mobile phones usually have only one screen, which belongs to the default grouping.
There are two steps to light up the screen
- Update wakefulness to WAKEFULNESS_AWAKE. Mainly updates the wakefulness of PowerManagerService and DisplayGroupPowerStateMapper.
- Update power status. The process of lighting the screen is handled here.
1. Update wakefulness
private boolean wakeDisplayGroupNoUpdateLocked(int groupId, long eventTime, @WakeReason int reason, String details, int uid, String opPackageName, int opUid) { // ... try { // ... // Set wakefulness to WAKEFULNESS_AWAKE setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid, opPackageName, details); // Update the information of the grouping display (groupId, eventTime); (groupId, true); } return true; } void setWakefulnessLocked(int groupId, int wakefulness, long eventTime, int uid, int reason, int opUid, String opPackageName, String details) { // 1. Update wakefulness of DisplayGroupPowerStateMapper if ((groupId, wakefulness)) { // display group wakefulness has changed mDirty |= DIRTY_DISPLAY_GROUP_WAKEFULNESS; // 2. Update PMS' wakefulness // Note that the first parameter takes the maximum wakefulness of all display groups, and the priority is as follows // PowerManagerInternal#WAKEFULNESS_AWAKE // PowerManagerInternal#WAKEFULNESS_DREAMING // PowerManagerInternal#WAKEFULNESS_DOZING // PowerManagerInternal#WAKEFULNESS_ASLEEP // TODO: Why is the wakefulness of PMS to be set to the largest wakefulness of all display groups? setGlobalWakefulnessLocked((), eventTime, reason, uid, opUid, opPackageName, details); if (wakefulness == WAKEFULNESS_AWAKE) { // Kick user activity to prevent newly awake group from timing out instantly. // 3. Save the time of user behavior userActivityNoUpdateLocked( groupId, eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, uid); } } }
The process of updating wakefulness to WAKEFULNESS_AWAKE is as follows
- Update the wakefulness of DisplayGroupPowerStateMapper to WAKEFULNESS_AWAKE.
- Update the wakefulness of PMS to WAKEFULNESS_AWAKE. Here, other system components will be notified that wakefulness/interaction status has changed. See [1.1 Update PMS wakefulness】
- Save the time for user behavior. This time is used to determine the time to automatically shut down the screen. See [1.2 Save user behavior time】
1.1 Update PMS wakefulness
// private void setGlobalWakefulnessLocked(int wakefulness, long eventTime, int reason, int uid, int opUid, String opPackageName, String details) { if (getWakefulnessLocked() == wakefulness) { return; } // Phase 1: Handle pre-wakefulness change bookkeeping. final String traceMethodName; switch (wakefulness) { // ... case WAKEFULNESS_AWAKE: // Save the time to wake up the device // This time will be used later when updating user behavior mLastWakeTime = eventTime; mLastWakeReason = reason; break; // ... } try { // Phase 2: Handle wakefulness change and bookkeeping. // Under lock, invalidate before set ensures caches won't return stale values. (); // Update wakefulness-related variables of PMS mWakefulnessRaw = wakefulness; mWakefulnessChanging = true; // mDirty set DIRTY_WAKEFULNESS, indicating that the wakefulness of PMS has changed mDirty |= DIRTY_WAKEFULNESS; mDozeStartInProgress &= (getWakefulnessLocked() == WAKEFULNESS_DOZING); // Notify other components, wakefulness changes or interactive state changes if (mNotifier != null) { (wakefulness, reason, eventTime); } (wakefulness); // Phase 3: Handle post-wakefulness change bookkeeping. switch (wakefulness) { case WAKEFULNESS_AWAKE: // Record and detect whether there is permission (reason, details, uid, opPackageName, opUid); if (sQuiescent) { mDirty |= DIRTY_QUIESCENT; } break; // ... } } finally { (Trace.TRACE_TAG_POWER); } }
According to the English comments, the wakefulness process of updating the PMS is divided into three stages. The most important thing is that in the second stage, update wakefulness-related variables, and then Notifier notifies other components and sends a screen-lit notification. The process is as follows, just take a look, this is not the point.
// public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) { // Determine whether the new wakefulness is interactive // WAKEFULNESS_AWAKE is interactive final boolean interactive = (wakefulness); // 1. Notify AMS wakefulness has changed (new Runnable() { @Override public void run() { (wakefulness); } }); // 2. Handle interaction state changes // Handle any early interactive state changes. // Finish pending incomplete ones from a previous cycle. // Handle early interactive state changes if (mInteractive != interactive) { // Finish up late behaviors if needed. // mInteractiveChanging is true, indicating that the last processing process has not been completed yet // Here we will first execute the previous process if (mInteractiveChanging) { handleLateInteractiveChange(); } // 2.1 Update the interactive status of system components // Start input as soon as we start waking up or going to sleep. (interactive); (interactive); // ... // Handle early behaviors. // 2.2 Update variables about interaction state mInteractive = interactive; mInteractiveChangeReason = reason; mInteractiveChangeStartTime = eventTime; // The interaction state is changing mInteractiveChanging = true; // 2.3 Handle early interactive state changes tasks handleEarlyInteractiveChange(); } } private void handleEarlyInteractiveChange() { synchronized (mLock) { if (mInteractive) { // Notify PhoneWindowManager, PhoneWindowManager notify SystemUI keyguard (() -> (mInteractiveChangeReason)); // Send a bright screen broadcast mPendingInteractiveState = INTERACTIVE_STATE_AWAKE; mPendingWakeUpBroadcast = true; updatePendingBroadcastLocked(); } else { // ... } } }
1.2 Save user behavior time
// private boolean userActivityNoUpdateLocked(int groupId, long eventTime, int event, int flags, int uid) { if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) { return false; } (Trace.TRACE_TAG_POWER, "userActivity"); try { if (eventTime > mLastInteractivePowerHintTime) { setPowerBoostInternal(, 0); mLastInteractivePowerHintTime = eventTime; } // 1. Notify the system components that user behavior occurs (event, uid); (eventTime, event); if (mUserInactiveOverrideFromWindowManager) { mUserInactiveOverrideFromWindowManager = false; mOverriddenTimeout = -1; } final int wakefulness = (groupId); if (wakefulness == WAKEFULNESS_ASLEEP || wakefulness == WAKEFULNESS_DOZING || (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) { return false; } maybeUpdateForegroundProfileLastActivityLocked(eventTime); if ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) { // Here is the logic to extend the time of the screen to light up... } else { if (eventTime > ( groupId)) { // 2. Save user activity time (groupId, eventTime); // 3. mDirty sets the DIRTY_USER_ACTIVITY flag bit, // Indicates that the user activity has been updated mDirty |= DIRTY_USER_ACTIVITY; if (event == PowerManager.USER_ACTIVITY_EVENT_BUTTON) { mDirty |= DIRTY_QUIESCENT; } return true; } } } finally { (Trace.TRACE_TAG_POWER); } return false; }
The process of updating user behavior by PMS
- Notify other components of user behavior through Notifier.
- DisplayGroupPowerStateMapper Saves the time for user behavior. This time will be used to determine the time to automatically shut down the screen.
- mDirty Set the DIRTY_USER_ACTIVITY flag to indicate that the user activity has been updated, and the power status will be used to update later.
A brief look at the first step, as follows
private void sendUserActivity(int event) { synchronized (mLock) { if (!mUserActivityPending) { return; } mUserActivityPending = false; } // I don't know what I've done here for the time being TelephonyManager tm = (); (); // PhoneWindowManger will notify SystemUI (); // If FaceDownDetector is performing the flip-down task, there is user behavior at this time, cancel this task (event); }
1.3 Update wakefulness summary
Through the above analysis, we should see an essence, and the process of updating wakefulness is roughly as follows
- Update the wakefulness of DisplayGroupPowerStateMapper, mDirty sets the flag DIRTY_DISPLAY_GROUP_WAKEFULNESS.
- Update the wakefulness of PowerManagerService, mDirty setting flag bit DIRTY_WAKEFULNESS.
- Notifier Notifies other components wakefulness/interaction status has changed and sends a broadcast with the on-screen/off screen.
2. Update power status
// private void updatePowerStateLocked() { if (!mSystemReady || mDirty == 0) { return; } // Pay attention to the technology here, the thread can determine whether a lock has been acquired if (!(mLock)) { (TAG, "Power manager lock was not held when calling updatePowerStateLocked"); } (Trace.TRACE_TAG_POWER, "updatePowerState"); try { // Phase 0: Basic state updates. // Power saving mode function updateIsPoweredLocked(mDirty); // "Charging function" in settings updateStayOnLocked(mDirty); // Brightness increase function updateScreenBrightnessBoostLocked(mDirty); // Phase 1: Update wakefulness. // Loop because the wake lock and user activity computations are influenced // by changes in wakefulness. final long now = (); int dirtyPhase2 = 0; for (;;) { int dirtyPhase1 = mDirty; dirtyPhase2 |= dirtyPhase1; mDirty = 0; // Include all wake locks into mWakeLockSummary updateWakeLockSummaryLocked(dirtyPhase1); // 1. Update user behavior updateUserActivitySummaryLocked(now, dirtyPhase1); updateAttentiveStateLocked(now, dirtyPhase1); // Decide whether to enter sleep/dream/doze state // If you enter a certain state, wakefulness will be updated, so here we need to update the above things through a loop if (!updateWakefulnessLocked(dirtyPhase1)) { break; } } // Phase 2: Lock profiles that became inactive/not kept awake. updateProfilesLocked(now); // Phase 3: Update display power state. // 2. Update the power status of the display final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2); // Phase 4: Update dream state (depends on display ready signal). updateDreamLocked(dirtyPhase2, displayBecameReady); // Phase 5: Send notifications, if needed. finishWakefulnessChangeIfNeededLocked(); // Phase 6: Update suspend blocker. // Because we might release the last suspend blocker here, we need to make sure // we finished everything else first! updateSuspendBlockerLocked(); } finally { (Trace.TRACE_TAG_POWER); } }
All functions of PowerManagerService are concentrated in this function, but there are two main steps related to the bright screen.
- updateUserActivitySummaryLocked()Update user behavior. This user behavior determines the final brightness of the screen. See [2.1 Update user behavior】
- updateDisplayPowerStateLocked()Updates the power status of the display, which initiates a power request for DisplayManagerService, thereby determining the brightness of the screen. See [2.2 Update the power status of the display】
2.1 Update user behavior
// private void updateUserActivitySummaryLocked(long now, int dirty) { // Update the status of the user activity timeout timer. if ((dirty & (DIRTY_DISPLAY_GROUP_WAKEFULNESS | DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) == 0) { return; } (MSG_USER_ACTIVITY_TIMEOUT); // Default is -1 final long attentiveTimeout = getAttentiveTimeoutLocked(); // Hibernation timeout, default is -1 final long sleepTimeout = getSleepTimeoutLocked(attentiveTimeout); // Screen timeout long screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout, attentiveTimeout); // dim duration = 20 % screen off timeout final long screenDimDuration = getScreenDimDurationLocked(screenOffTimeout); screenOffTimeout = getScreenOffTimeoutWithFaceDownLocked(screenOffTimeout, screenDimDuration); final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager; long nextTimeout = -1; boolean hasUserActivitySummary = false; // traversal display group id for (int groupId : ()) { int groupUserActivitySummary = 0; long groupNextTimeout = 0; // Note that the dormant state cannot determine the user's behavior if ((groupId) != WAKEFULNESS_ASLEEP) { final long lastUserActivityTime = (groupId); final long lastUserActivityTimeNoChangeLights = ( groupId); // 1. Get user behavior and timeout // The last user behavior time >= The last time the screen was awakened if (lastUserActivityTime >= mLastWakeTime) { groupNextTimeout = lastUserActivityTime + screenOffTimeout - screenDimDuration; if (now < groupNextTimeout) { // There is no time to dim groupUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT; } else { groupNextTimeout = lastUserActivityTime + screenOffTimeout; if (now < groupNextTimeout) { // In dim time period groupUserActivitySummary = USER_ACTIVITY_SCREEN_DIM; } } } // Timeout, but because a certain lock is released, the screen time needs to be extended if (groupUserActivitySummary == 0 && lastUserActivityTimeNoChangeLights >= mLastWakeTime) { // ... } // General timeout situation, if (groupUserActivitySummary == 0) { // ... } // When PhoneWindowManager handles KeyEvent.KEYCODE_SOFT_SLEEP, userInactiveOverride is true // KeyEvent.KEYCODE_SOFT_SLEEP hibernation button of this software? if (groupUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) { // ... } // The user behavior is to light the screen, and WakeLock does not keep the screen lit up, use AttentionDetector to calculate the screen timeout time again if ((groupUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 && ((groupId) & WAKE_LOCK_STAY_AWAKE) == 0) { // ... } hasUserActivitySummary |= groupUserActivitySummary != 0; if (nextTimeout == -1) { nextTimeout = groupNextTimeout; } else if (groupNextTimeout != -1) { // Here it indicates the case of nextTimeout != -1, which also indicates that there are multiple display groups // From here, we can see that the timeout time of multiple display groups is the same nextTimeout = (nextTimeout, groupNextTimeout); } } // 2. DisplayGroupPowerStateMapper Saves user behavior (groupId, groupUserActivitySummary); } } // traversal display group id ends final long nextProfileTimeout = getNextProfileTimeoutLocked(now); if (nextProfileTimeout > 0) { nextTimeout = (nextTimeout, nextProfileTimeout); } // 3. Regularly update power status // This step decides to automatically shut down the screen if (hasUserActivitySummary && nextTimeout >= 0) { scheduleUserInactivityTimeout(nextTimeout); } }
This function is not only used to update user behavior, but also updates the screen timeout time, and regularly updates the power state to achieve the function of automatically extinguishing the screen.
Update user behavior In step 1, when updating wakefulness, PMS saves the wakefulness time mLastWakeTime, and DisplayGroupPowerStateMapper saves the user behavior time. Therefore, for the process from the screen-off state to the screen-on state, the value of the user behavior is now USER_ACTIVITY_SCREEN_BRIGHT, indicating that the user behavior is the screen-on.
2.2 Update the power status of the display
// private boolean updateDisplayPowerStateLocked(int dirty) { final boolean oldDisplayReady = (); if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | DIRTY_QUIESCENT | DIRTY_DISPLAY_GROUP_WAKEFULNESS)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { // ... } // traversal display group for (final int groupId : ()) { // 1. Get the request to display group final DisplayPowerRequest displayPowerRequest = (groupId); // 2. Update various parameters of the request // Update the requested policy parameters. The so-called policy is to light the screen, or to turn off the screen, or make the screen dim, etc. = getDesiredScreenPolicyLocked(groupId); // ...Omit the process of updating other request parameters... // 3. Make a request to DisplayManagerService // If this request is different from the last request, then the processing of this request is an asynchronous processing process, and false is returned at this time. // Otherwise, no processing is required and returns true directly. final boolean ready = (groupId, displayPowerRequest, mRequestWaitForNegativeProximity); // Update the ready status of DisplayGroupPowerStateMapper final boolean displayReadyStateChanged = (groupId, ready); // If the asynchronous request is processed, DMS will call back to notify PMS, and PMS will update the status and go here. // If the screen is turned on for too long, then use log to record it final boolean poweringOn = (groupId); if (ready && displayReadyStateChanged && poweringOn && ( groupId) == WAKEFULNESS_AWAKE) { (groupId, false); (Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId); final int latencyMs = (int) (() - (groupId)); if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) { (TAG, "Screen on took " + latencyMs + " ms"); } } } mRequestWaitForNegativeProximity = false; } // The return value indicates whether it changes from a non-ready state to a ready state return () && !oldDisplayReady; }
The power status of the screen is very clear, as follows
- First get the request and update the request parameters. Among the request parameters, the main concern is the policy parameters, which determine the status of the screen, that is, whether the screen is on or off.
- Make a request to DisplayManagerService. Note that if the current request is different from the last request, the processing is asynchronous and the returned ready state is false. Otherwise, the processing is synchronous and the returned ready is true.
Let's take a look at how to update the request policy
// int getDesiredScreenPolicyLocked(int groupId) { final int wakefulness = (groupId); final int wakeLockSummary = (groupId); if (wakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } else if (wakefulness == WAKEFULNESS_DOZING) { // ... } if (mIsVrModeEnabled) { return DisplayPowerRequest.POLICY_VR; } // Since the UserActivity at this time is USER_ACTIVITY_SCREEN_BRIGHT, the policy is DisplayPowerRequest.POLICY_BRIGHT if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || !mBootCompleted || ((groupId) & USER_ACTIVITY_SCREEN_BRIGHT) != 0 || mScreenBrightnessBoostInProgress) { return DisplayPowerRequest.POLICY_BRIGHT; } return DisplayPowerRequest.POLICY_DIM; }
The user behavior we just analyzed is USER_ACTIVITY_SCREEN_BRIGHT, so the policy is ultimately DisplayPowerRequest.POLICY_BRIGHT. When a request is initiated to DisplayManagerService, the screen will eventually light up.
2.3 Handling updates to screen status
As we mentioned earlier, the process of processing screen requests may be asynchronous or synchronous. If the process from the screen to the screen opening is bound to the screen opening, the process must be asynchronous. So how does PowerManagerService know that the DisplayManagerService has been processed? In fact, PowerManagerService has registered a callback to DisplayManagerService
// public void systemReady(IAppOpsService appOps) { synchronized (mLock) { ( mDisplayPowerCallbacks, mHandler, sensorManager); } } private final mDisplayPowerCallbacks = new () { @Override public void onStateChanged() { synchronized (mLock) { // Indicates that the screen status has been updated mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED; updatePowerStateLocked(); } } }
When the PowerManagerService learns through the callback that the DisplayManagerService has processed the screen request, it updates the power status again.
at this time,updateDisplayPowerStateLocked()Then initiate a request to DisplayManagerService. Since it is the same as the last request, DisplayManagerService does not process it, and the returned ready status is true.
The remaining process of updating the power state is the ending. Let's take a look at it roughly
// private void finishWakefulnessChangeIfNeededLocked() { // Note that one of the conditions is that all display groups need to be ready if (mWakefulnessChanging && ()) { // ... // The display is ready, and the work of changing PMS wakefulness is considered to be completed mWakefulnessChanging = false; // Notier notifies that wakefulness changes have been completed (); } }
// public void onWakefulnessChangeFinished() { if (mInteractiveChanging) { mInteractiveChanging = false; // Post-tasks that handle interaction state handleLateInteractiveChange(); } } private void handleLateInteractiveChange() { synchronized (mLock) { final int interactiveChangeLatency = (int) (() - mInteractiveChangeStartTime); if (mInteractive) { // Finished waking up... (() -> { // Notify PhoneWindowManager to complete the device wake-up work (mInteractiveChangeReason); }); } else { // ... } } }
2.4 Summary
We analyze the process from extinguishing the screen to extinguishing the screen, but we should see an essential problem. It is actually to initiate a request to the DisplayManagerService to update the screen status (such as turning on the screen, extinguishing the screen).
The requested policy ultimately determines the state of the screen, but there are many factors that affect the requested policy, such as system status (referring to the wakefulness of the PMS), user behavior, wake-up lock, etc. We will see more about deciding on request strategies in a later article.
Summarize
Although this article analyzes the process from extinguishing the screen to turning on the screen, we need to see an essential process, which is actually only two steps.
- Update wakefulness.
- Send a request to DisplayManagerService to update the screen status.
The above is the detailed analysis of the PowerManagerService's screen-brightening process example. For more information about the PowerManagerService screen-brightening process, please pay attention to my other related articles!