In the Andorid Framework layer, PowerManagerService(PMS for short) module is responsible for coordinating and managing device CPU resources. CPU resource requests from other components of the application layer and Framework layer are made through PowerManager module. Therefore, some logic affecting system resource scheduling is implemented in THE PMS module. For example, the system is on and off screen, WakeLock management, Dreamland, and sleep time.

The core service in PMS module is PowerManagerService, which inherits from SystemService class and has the commonness of SystemService subclass. There are lifecycle methods, starting with SystemServer, registering with system services, interacting with Binder and other components, etc. During the system startup process, the SystemServer handles all phases.

All SystemService classes execute the following lifecycle methods during startup:

  • Constructor: call by reflection, get instance;
  •  onstart()Method: Enable the corresponding SystemService.
  •  onBootPhase()Method: Specify a service startup phase during the startup of the SystemService service, and specify specific work for each phase;

1.PMS startup process

When system_server is started, PMS will be started by SystemServer via reflection.

First, SystemServer calls its own run() method in the main() method and starts three types of services in the run() method:

  • Bootstrap services: services that are highly dependent on other system services;
  • Core services: some essential services that are not highly dependent;
  • Other services: some messy system services;
// frameworks/base/services/java/com/android/server/SystemServer.java

private void run(a) {
	/ /...
	try {
		startBootstrapServices();// Start the boot service
		startCoreServices();// Start the core service
		startOtherServices();// Start other services
	} catch (Throwable ex) {
	} 
	......
}
Copy the code

PMS is included in the boot service:

// frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices(a) {
	// Start the PMS service using SystemManagerService
	mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
}
Copy the code

As you can see, other services still rely heavily on PMS.

In the SystemServiceManager startService() method, the PMS object is reflected:

    public SystemService startService(String className) {
		final Class<SystemService> serviceClass;
		serviceClass = (Class<SystemService>)Class.forName(className);
		return startService(serviceClass);
	}
        
    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            // Create the service.
            final T service;
            try {
		// Create a PMS object
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (InstantiationException ex) {
            }
            startService(service);
            returnservice; }}public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            / / execution onStart ()
            service.onStart();
        } catch (RuntimeException ex) {
        }
    }
Copy the code

Once the PMS instance is acquired, it is first added to an ArrayList that holds all systemServices, and methods are called back to the elements of the entire List later in the startup process.

Then, the onStart() method of the PMS is called. Let’s take a look at each of these methods.

1.1. Constructor for initialization

PMS is constructed as follows:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    PowerManagerService(Context context, Injector injector) {
        super(context);

        mContext = context;
        mBinderService = new BinderService();   // Used for IPC interactions
        mLocalService = new LocalService();     // Users interact across threads
        // Create a NativeWrapper object to manage all native interactions
        mNativeWrapper = injector.createNativeWrapper();
	// Internal class Injector object, which manages creating other objects
        mInjector = injector;  
        // Create the PMS main thread
        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
        mHandlerThread.start();
        // Create PMS main thread Handler
        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
	// Manage constants under settings. Global
        mConstants = new Constants(mHandler);
        / / create AmbientDisplayConfiguration object, some display configuration management, such as AOD
        mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
        // Create an AttentionDetector object to check if the user activity needs to be updated
        mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
	// Create the BatterySavingStats object to record the battery consumption rate
        mBatterySavingStats = new BatterySavingStats(mLock);
        // Create a BatterySaverPolicy object to manage some power-saving policies
        mBatterySaverPolicy =
                mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
	// Create the BatterySaverController object to manage switching power saving policies
        mBatterySaverController = new BatterySaverController(mLock, mContext,
                BackgroundThread.get().getLooper(), mBatterySaverPolicy,
                mBatterySavingStats);
	// Create the BatterySaverStateMachine object to enable or disable the power-saving policy
        mBatterySaverStateMachine = new BatterySaverStateMachine(
                mLock, mContext, mBatterySaverController);

        synchronized (mLock) {
            / / create SuspendBlockerImpl object, WakeLock ultimately reflects, "PowerManagerService. WakeLocks" type object is responsible for the keep alive the CPU
            mWakeLockSuspendBlocker =
                    mInjector.createSuspendBlocker(this."PowerManagerService.WakeLocks");
	    / / create SuspendBlockerImpl object, "PowerManagerService. Display" type object responsible for maintaining normally on screen
            mDisplaySuspendBlocker =
                    mInjector.createSuspendBlocker(this."PowerManagerService.Display");
            if(mDisplaySuspendBlocker ! =null) {
		// Apply mDisplaySuspendBlocker to keep the screen on
                mDisplaySuspendBlocker.acquire();
                mHoldingDisplaySuspendBlocker = true;
            }
	    // Whether auto-suspend mode is available
            mHalAutoSuspendModeEnabled = false;
	    // Whether the state is interactive
            mHalInteractiveModeEnabled = true;
            // Set the device state to wake up
            mWakefulness = WAKEFULNESS_AWAKE;
 	    // Silent mode, will control the backlight lighting, use few scenes
            sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
	    // The mNativeWrapper object performs native layer initialization
            mNativeWrapper.nativeInit(this);
            mNativeWrapper.nativeSetAutoSuspend(false); // Set the auto suspend state
            mNativeWrapper.nativeSetInteractive(true); // Set the interactive state
            mNativeWrapper.nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);// Set double click to wake up Feature state}}Copy the code

In the above methods, a lot of initialization work is done, mainly as follows:

    1. Create BinderService objects for IPC and LocalService objects for cross-thread interaction within the system_server process;
    1. Create the main thread of the PMS and instantiate PowerManagerHandler with Looper of the HandlerThread to handle the Message as the main thread Handler of the PMS.
    1. Read related configuration parameters, such as system configuration of various brightness parameters;
    1. SuspendBlocker = SuspendBlocker = SuspendBlocker; SuspendBlocker = SuspendBlocker; SuspendBlocker = SuspendBlocker; SuspendBlocker = SuspendBlocker;
    1. State interaction with the underlying layer is through the mNativeWrapper object.

1.2.onStart() method to register the service

After the constructor completes, execute the onStart() method:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override
public void onStart(a) {
    // Publish to system services
    publishBinderService(Context.POWER_SERVICE, mBinderService);
    // Publish to the local service
    publishLocalService(PowerManagerInternal.class, mLocalService);
    // Set Watchdog listening
    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
}
Copy the code

In this method, Binder services and Local services of PMS are registered. After BinderService is registered, Binder objects can be obtained from ServiceManager in other modules for IPC communication. Once LocalService is registered, cross-thread interactions can be performed within the system_server process by retrieving the LocalService object.

BinderService in PMS is an internal BinderService class that inherits from IPowerManager.Stub, which inherits from Binder and implements IPowerManager. The cross-process interaction between PMS and other modules is actually implemented through PMS.BinderService.

Binder registration is performed in the ServiceManager:

// frameworks/base/core/java/android/os/ServiceManager.java

@UnsupportedAppUsage
public static void addService(String name, IBinder service, boolean allowIsolated,
        int dumpPriority) {
    try {
        getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
    } catch (RemoteException e) {
        Log.e(TAG, "error in addService", e); }}Copy the code

After the ServiceManager is registered, the iPowerManager. Stub object can be obtained from other services based on context. POWER_SERVICE for cross-process interaction.

LocalService is used for Internal system_server interactions, and the registered object is another Internal class — LocalService that inherits from PowerManagerInternal (classes with an Internal are generally used within the System process). Local services are registered in Local services. The registration method is as follows:

// frameworks/base/core/java/com/android/server/LocalServices.java

public static <T> void addService(Class<T> type, T service) {
    synchronized (sLocalServiceObjects) {
        if (sLocalServiceObjects.containsKey(type)) {
            throw new IllegalStateException("Overriding service registration");
        }
        // Add to a MapsLocalServiceObjects.put(type, service); }}Copy the code

As you can see, the Local Service is registered by adding it directly to a Map.

1.3.onBootPhase() carries out the processing of each startup phase

Back in SytemServer, the instantiation and registration of the PMS in the startBootstrapServices() method is complete, and then the SystemService lifecycle starts onBootPhase(), This method sets up the startup phase for all instantiated and registered services so that different work can be done at different startup phases as follows:

// frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

public void startBootPhase(final int phase) {
    mCurrentPhase = phase;
    try {
        final int serviceLen = mServices.size();
        for (int i = 0; i < serviceLen; i++) {
	    // Call SystemService onBootPhase()
            final SystemService service = mServices.get(i);
            try {
                service.onBootPhase(mCurrentPhase);
            } catch (Exception ex) {
                
            }
        }
    } finally{}}Copy the code

In SystemServiceManager#startBootPhase(), iterate through the SystemServiceManager#mServices list by passing different parameters in SystemServiceManager, Call each SystemService#onBootPhase(int) method to do different work in the method implementation depending on the parameters. SystemService defines seven parameters that represent the startup phase:

  • PHASE_WAIT_FOR_DEFAULT_DISPLAY: The first startup phase, used to ensure that the default logical screen already exists before starting PKMS and used only by DisplayManagerService;
  • PHASE_LOCK_SETTINGS_READY: The second startup phase. The execution of this phase means that the Lock Pattern/Password related services are ready and only DisplayManagerService uses this phase.
  • PHASE_SYSTEM_SERVICES_READY: The third startup phase whose execution means that core system services can be safely used by other services;
  • PHASE_DEVICE_SPECIFIC_SERVICES_READY: Start the fourth phase, the phase of execution, means that the other services can be used safely devices specified some system services, these services in config_deviceSpecificSystemServices configuration;
  • PHASE_ACTIVITY_MANAGER_READY: The fifth startup phase, the execution of which means that the other AMS components have been started and are ready to broadcast;
  • PHASE_THIRD_PARTY_APPS_CAN_START: The sixth startup phase, which means the system can start the APP and bind/start the service.
  • PHASE_BOOT_COMPLETED: The sixth startup phase. The execution of this phase means that the startup is complete and the Home application is started and can interact with the device.

The PMS#onBootPhase() method only handles the above two phases:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override
public void onBootPhase(int phase) {
    synchronized (mLock) {
        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
	    Adb shell Settings get global boot_count adb shell Settings get global boot_count
            incrementBootCount();

        } else if (phase == PHASE_BOOT_COMPLETED) {
            final long now = SystemClock.uptimeMillis();
            mBootCompleted = true; // Indicates that the startup is complete
	    // mDirty is a bit flag
            mDirty |= DIRTY_BOOT_COMPLETED;
            mBatterySaverStateMachine.onBootCompleted();
	    // Update user activity time
            userActivityNoUpdateLocked(
                    now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
	    // Update global status informationupdatePowerStateLocked(); }}}Copy the code

MDirty is a binary flag bit, which part used to show the power status is changed, through carries on the setting (|), reset (~) operation, binary number in each bit value (0 or 1), different processing, this variable is very important. Finally, call the updatePowerStateLocked() method, which is the most important method in the entire PMS and is examined in detail below.

At this point, all the SystemService lifecycle methods during startup have been executed.

1.4. SytemReady () method

For PMS start, after when you perform the life cycle method, the SystemServer. StartOtherServices () also conducted step:


private void startOtherServices(a) {
    / /...
    mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
    / /...
}
Copy the code

This method mainly does the following operations, the related method code will not paste:

  • Obtain various local and remote services, such as Dreamland Service, PhoneWindowManager, BatteryService, etc.
  • Register broadcasts used to interact with other Sytemservices.
  • Call the updateSettingsLocked() method to update the changes in Settings;
  • Call the readConfigurationLocked() method to read the default values in the configuration file;
  • Register SettingsObserver listener;

At this point, the PMS startup process is complete.

The sequence diagram of the above process is as follows:

UpdatePowerStateLocked ()

The updatePowerStateLocked() method is the core method of the entire PMS module and the most important method in the entire PSM. It updates the entire Power state. When the Power status changes, such as on/off screen, battery status change, dark screen, and WakeLock lock application/release…… Call this method, and call other sibling methods to update each state:

// frameworks/base/services/core/java/com/android/server/SystemService.java

    private void updatePowerStateLocked(a) {
        if(! mSystemReady || mDirty ==0) {  // When mDirty is 0, no updates are made
            return;
        }
        try {
            // Phase 0: Base state update
            updateIsPoweredLocked(mDirty);    // Update the charging status
            updateStayOnLocked(mDirty);       // Update whether the current screen is steady on, controlled by mStayOn
            updateScreenBrightnessBoostLocked(mDirty);    // Update whether the brightness variable needs to be enhanced

            // Phase 1: Updates the wake state
            // by changes in wakefulness.
            final long now = mClock.uptimeMillis();
            int dirtyPhase2 = 0;
            // Loop through the update process until updateWakefulnessLocked() returns false
            for (;;) {
                int dirtyPhase1 = mDirty;
                dirtyPhase2 |= dirtyPhase1;
                mDirty = 0;    // Clear the tag
                // Update the mWakeLockSummary attribute used to count wakelock
                updateWakeLockSummaryLocked(dirtyPhase1);
                // Update the mUserActivitySummary attribute, the tag value used to count user activity status
                updateUserActivitySummaryLocked(now, dirtyPhase1);
                // Update subtle mode state
                updateAttentiveStateLocked(now, dirtyPhase1);
                // Update the wake up state, return true if the state changes
                if(! updateWakefulnessLocked(dirtyPhase1)) {break; }}// Phase 2: 
            updateProfilesLocked(now);

            // Phase 3: Updates the display status
            final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

            // Phase 4: Updates the Dreamland status
            updateDreamLocked(dirtyPhase2, displayBecameReady);

            // Phase 5: If wakefulness changes, make the final closing
            finishWakefulnessChangeIfNeededLocked();

            // Phase 6: Updates the SuspendBlocker lock statusupdateSuspendBlockerLocked(); }}Copy the code

All of the above methods are analyzed one by one.

2.1. UpdateIsPoweredLocked () Updates the charging status

This method is used to update the mIsPowered property, which represents the current charging state. The logic of plugging and unplugging USB to light up the screen function is in this method. The method is as follows:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateIsPoweredLocked(int dirty) {
    if((dirty & DIRTY_BATTERY_STATE) ! =0) {  // This method is executed only if there is a marker bit DIRTY_BATTERY_STATE
        // Record the old value
        final boolean wasPowered = mIsPowered;   // Whether to charge
        final int oldPlugType = mPlugType;   // Charging type
        final boolean oldLevelLow = mBatteryLevelLow;  // Check whether it is lower than the low power threshold
        // Get a new value
        mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
        mPlugType = mBatteryManagerInternal.getPlugType();
        mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();  // Current power
        mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
 
        if(wasPowered ! = mIsPowered || oldPlugType ! = mPlugType) {// Indicates that the charging status has changed
            mDirty |= DIRTY_IS_POWERED;  // Set the flag bit DIRTY_IS_POWERED
 
            // Update the wireless charging status
            final boolean dockedOnWirelessCharger = mWirelessChargerDetector.update(
                    mIsPowered, mPlugType);
 
            final long now = SystemClock.uptimeMillis();
            // Check whether the USB screen needs to be lit
            if (shouldWakeUpWhenPluggedOrUnpluggedLocked(wasPowered, oldPlugType,
                    dockedOnWirelessCharger)) {
                // Screen light process
                wakeUpNoUpdateLocked(now, PowerManager.WAKE_REASON_PLUGGED_IN,
                        "android.server.power:PLUGGED:" + mIsPowered, Process.SYSTEM_UID,
                        mContext.getOpPackageName(), Process.SYSTEM_UID);
            }
            // Update user activity time
            userActivityNoUpdateLocked(
                    now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
            // Play charging prompt sound and animation
            if (mBootCompleted) {  // It will play only after the startup is complete
                if(mIsPowered && ! BatteryManager.isPlugWired(oldPlugType) && BatteryManager.isPlugWired(mPlugType)) { mNotifier.onWiredChargingStarted(mForegroundProfile); }else if(dockedOnWirelessCharger) { mNotifier.onWirelessChargingStarted(mBatteryLevel, mForegroundProfile); }}}// Update the charging status to BatterySaverStateMachinemBatterySaverStateMachine.setBatteryStatus(mIsPowered, mBatteryLevel, mBatteryLevelLow); }}Copy the code

When will DIRTY_BATTERY_STATE be set only if the DIRTY_BATTERY_STATE flag is set for mDirty? ACTION_BATTERY_CHANGED When battery information changes, the HEALTHD module reports it to BatteryService. BatteryService sends an Intent.ACTION_BATTERY_CHANGED to notify other components.

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void handleBatteryStateChangedLocked(a) {
    mDirty |= DIRTY_BATTERY_STATE;
    updatePowerStateLocked();
}
Copy the code

Therefore, this method is executed whenever the battery state changes. In this method:

  1. First, update some global variables like mIsPowered.
  2. And then, throughshouldWakeUpWhenPluggedOrUnpluggedLocked()Method to determine whether the screen needs to light up, which is the logic of the function of inserting and removing USB to light up the screen.
  3. Next, executeuserActivityNoUpdateLocked()Method To update the user’s active time, which determines when the screen will automatically go off. Each time the user operates the phone (touch, button, or Other), the time will be updated to the current time. The user’s last active time + setting automatic sleep time = the time when the screen will automatically go off.
  4. Next, the Notifier object, a class in the PMS module that broadcasts and asynchronously notifies other components, is called to play plug-plug USB sounds or animations.
  5. Finally, pass charging status to mBatterySaverStateMachine, to adjust energy saving strategy.

2.2. UpdateStayOnLocked () Updates the screen to be steady on

This method is used to update the value of the global variable mStayOn. “Developer options-do not lock the screen”, when enabled, will stay on while charging and will not sleep automatically:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateStayOnLocked(int dirty) {
    if((dirty & (DIRTY_BATTERY_STATE | DIRTY_SETTINGS)) ! =0) { 
        final boolean wasStayOn = mStayOn;
        if(mStayOnWhilePluggedInSetting ! =0
                && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
            // Return true if charging in any way (AC/USB/wireless)
            mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
        } else {
            mStayOn = false;
        }
         // Set the DIRTY_STAY_ON flag to mDirty when the status changes
        if(mStayOn ! = wasStayOn) { mDirty |= DIRTY_STAY_ON; }}}Copy the code

This method is executed only after the DIRTY_BATTERY_STATE or DIRTY_SETTINGS flag bit is set for mDirty. DIRTY_BATTERY_STATE after the battery status change Settings, DIRTY_SETTINGS is value after change Settings in the Settings mStayOnWhilePluggedInSetting is from the value of the “no lock screen” in the Settings, If it changes and is in a charging state, the value of the mStayOn variable is updated. In the automatic screen off process, once the value of mStayOn is true, the screen is never turned off, thus realizing the function of “not locking the screen”.

2.3. UpdateScreenBrightnessBoostLocked update whether enhance brightness ()

This method will enhance the brightness of the global variable mScreenBrightnessBoostInProgress update said, PMS, BinderService provides boostScreenBrightness () method, which allows other components through the interface to a brightness control to the largest, And hold for 5 seconds after recovery:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateScreenBrightnessBoostLocked(int dirty) {
    if((dirty & DIRTY_SCREEN_BRIGHTNESS_BOOST) ! =0) {
        if (mScreenBrightnessBoostInProgress) {
            final long now = SystemClock.uptimeMillis();
            mHandler.removeMessages(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
            if (mLastScreenBrightnessBoostTime > mLastSleepTime) {
                final long boostTimeout = mLastScreenBrightnessBoostTime +
                        SCREEN_BRIGHTNESS_BOOST_TIMEOUT;
                // It will be updated again after 5s
                if (boostTimeout > now) {
                    Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, boostTimeout);
                    return; }}// Indicates the end of brightness enhancement
            mScreenBrightnessBoostInProgress = false;
            mNotifier.onScreenBrightnessBoostChanged();
            // Update user activity time
            userActivityNoUpdateLocked(now,
                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); }}}Copy the code

This method is executed only if mDirty sets the DIRTY_SCREEN_BRIGHTNESS_BOOST flag. This is set to boostScreenBrightness(). This feature has one use scenario in Google native logic:

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

mDoublePressOnPowerBehavior = mContext.getResources().getInteger(
        com.android.internal.R.integer.config_doublePressOnPowerBehavior);
Copy the code

After config_doublePressOnPowerBehavior configured for 2, double-click the power button will through the above method will increase brightness to the largest.

2.4. UpdateWakeLockSummaryLocked update WakeLock statistical values ()

From the start of this method to the end of updateWakefulnessLocked(), a for loop is executed.

This method is used to update the mWakeLockSummary property, which is used to record the status value of all WakeLock lock states. It represents all WakeLock locks and is used as a judgment condition to determine the specific request status in the request Display. The WakeLock lock is affected by the sleep status of the system. For example, powerManager.screen_bright is ignored after the sleep status of the system. Doze locks (powermanager.doze_wake_lock, etc.) will be ignored when the system wakes up:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateWakeLockSummaryLocked(int dirty) {
    if((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) ! =0) {
        mWakeLockSummary = 0;  / / set to 0
        // Reset the mWakeLockSummary for each ProfilePowerState as well
        final int numProfiles = mProfilePowerState.size();
        for (int i = 0; i < numProfiles; i++) {
            mProfilePowerState.valueAt(i).mWakeLockSummary = 0;
        }
        // Iterate over the list of mWakeLocks
        final int numWakeLocks = mWakeLocks.size();
        for (int i = 0; i < numWakeLocks; i++) {
            final WakeLock wakeLock = mWakeLocks.get(i);
            // Get the Flag corresponding to each WakeLock
            final int wakeLockFlags = getWakeLockSummaryFlags(wakeLock);
            // Mark on mWakeLockSummary
            mWakeLockSummary |= wakeLockFlags;
            // mark each ProfilePowerState#mWakeLockSummary as well
            for (int j = 0; j < numProfiles; j++) {
                final ProfilePowerState profile = mProfilePowerState.valueAt(j);
                if(wakeLockAffectsUser(wakeLock, profile.mUserId)) { profile.mWakeLockSummary |= wakeLockFlags; }}}// Adjust mWakeLockSummary based on system status
        mWakeLockSummary = adjustWakeLockSummaryLocked(mWakeLockSummary);
        // adjust each ProfilePowerState#mWakeLockSummary as well
        for (int i = 0; i < numProfiles; i++) {
            finalProfilePowerState profile = mProfilePowerState.valueAt(i); profile.mWakeLockSummary = adjustWakeLockSummaryLocked(profile.mWakeLockSummary); }}}Copy the code

This method is executed only if mDirty sets the DIRTY_WAKE_LOCKS and DIRTY_WAKEFULNESS flag bits. DIRTY_WAKE_LOCKS is set when a WakeLock lock is claimed, and DIRTY_WAKEFULNESS is set when the system wake state changes.

After entering this method, it first traverses the List of saved WakeLock and sets markers for mWakeLockSummary according to the WakeLock type. These markers are as follows:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private static final int WAKE_LOCK_CPU = 1 << 0;                   // Indicates that the CPU needs to stay awake
private static final int WAKE_LOCK_SCREEN_BRIGHT = 1 << 1;         // Hold the FULL_WAKE_LOCK lock and keep the screen on
private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;            // SCREEN_DIM_WAKE_LOCK is held, and dim must be kept on screen
private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;         // It holds the FULL_WAKE_LOCK lock and requires the key light to be on
private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;  // A Psensor WakeLock lock is held
private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5;            // Keep the screen steady on
private static final int WAKE_LOCK_DOZE = 1 << 6;                  // hold the DOZE_WAKE_LOCK lock
private static final int WAKE_LOCK_DRAW = 1 << 7;                  // hold DRAW_WAKE_LOCK
Copy the code

Then it will adjust according to the system state:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private int adjustWakeLockSummaryLocked(int wakeLockSummary) {
    DOZE_WAKE_LOCK and powerManager. DRAW_WAKE_LOCK are ignored when the wakeup state is not Doze
    if(mWakefulness ! = WAKEFULNESS_DOZING) { wakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW); }// When the wakeup state is Asleep or Doze, the on-screen lock and PSensor lock are ignored
    if(mWakefulness == WAKEFULNESS_ASLEEP || (wakeLockSummary & WAKE_LOCK_DOZE) ! =0) {
        wakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                | WAKE_LOCK_BUTTON_BRIGHT);
        if(mWakefulness == WAKEFULNESS_ASLEEP) { wakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF; }}if((wakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) ! =0) {
        WAKE_LOCK_STAY_AWAKE is used only when Awake
        if (mWakefulness == WAKEFULNESS_AWAKE) { 
            wakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
        } else if (mWakefulness == WAKEFULNESS_DREAMING) {  // The wake state is in Dreamland and requires the CPU to remain activewakeLockSummary |= WAKE_LOCK_CPU; }}// A DRAW_WAKE_LOCK is required to keep the CPU awake
    if((wakeLockSummary & WAKE_LOCK_DRAW) ! =0) {
        wakeLockSummary |= WAKE_LOCK_CPU;
    }
 
    return wakeLockSummary;
}
Copy the code

This value can be used to determine the type of lock held by the current system when analyzing and handling bugs related to the on-off screen.

The final result is mWakeLockSummary, which will play an important role in the automatic extinction process. If the WAKE_LOCK_STAY_AWAKE flag bit is set for mWakeLockSummary during automatic extinction, the screen will not be extinguished.

The ProfilePowerState object is used to set different parameters for different users. In multi-user mode, different states can be supported. This part is omitted.

2.5. UpdateUserActivitySummaryLocked update user activity state ()

This method is used to update the global variable mUserActivitySummary, which represents user activity status and has three values:

private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;  // Indicates the interaction in the light state
private static final int USER_ACTIVITY_SCREEN_DIM = 1 << 1;     // Indicates an interaction in Dim state
private static final int USER_ACTIVITY_SCREEN_DREAM = 1 << 2;   // Represents the interaction in Dreamland state
Copy the code

When to start the automatic screen off, is implemented in this method. When the device interacts with the user, the next spin-down time is calculated based on the current time, auto Off time, Dim duration, and current wake up status. The duration from Dim to screen, from Dim to screen off, and from screen on to screen saver are calculated here. For detailed analysis of this method, see Android R PowerManagerService module (4) Screen Off process.

2.6. UpdateAttentiveStateLocked update subtle mode ()

This method is used to update the subtle mode state, which is a new feature added to Android R. The purpose of this method is to solve the situation where the user has not operated for a long time but has been holding the bright screen lock, so the system does not die. It is a power saving optimization.

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

    private void updateAttentiveStateLocked(long now, int dirty) {
        // Time threshold for triggering subtle mode
        long attentiveTimeout = getAttentiveTimeoutLocked();
        // Automatic sleep time
        long goToSleepTime = mLastUserActivityTime + attentiveTimeout;
        // Subtle mode prompts Dialog to pop up time
        long showWarningTime = goToSleepTime - mAttentiveWarningDurationConfig;
        // Check whether the promotion dialog box is displayed
        boolean warningDismissed = maybeHideInattentiveSleepWarningLocked(now, showWarningTime);
        
        if (attentiveTimeout >= 0&& (warningDismissed || (dirty & (DIRTY_ATTENTIVE | DIRTY_STAY_ON | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_PROXIMITY_POSITIVE | DIRTY_WAKEFULNESS | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS)) ! =0)) {...// Whether a pop-up warning is required
            if (isBeingKeptFromShowingInattentiveSleepWarningLocked()) {
                return;
            }

            long nextTimeout = -1;

            if (now < showWarningTime) {
                nextTimeout = showWarningTime;
            } else if (now < goToSleepTime) {
                // Pop-up warning to user
                mInattentiveSleepWarningOverlayController.show();
                nextTimeout = goToSleepTime;
            } else{}// The screen will go off the next time you enter
            if (nextTimeout >= 0) { scheduleAttentiveTimeout(nextTimeout); }}}Copy the code

Two configuration values are provided:

  • MAttentiveWarningDurationConfig said trigger subtle pattern before, the length of the pop-up warning, arrived at the time, will pop up a dialog prompting the user whether brighter screen;
  • MAttentiveTimeoutConfig indicates the time threshold for triggering the subtle mode. After the time threshold is reached, the screen is automatically off.

In this case, the screen is automatically turned off even if the auto sleep time is not reached.

2.7. UpdateWakefulnessLocked () whether need to update the awakened state

This method is also related to the automatic screen off process. If the screen is off automatically, the system wake up status is updated.

These three methods are placed in for(;;). It is executed in a cycle because they jointly determine the wakeup state of the device. The first two methods are the summary state, while the second method determines whether to change the current wakeup state of the device according to the value summarized by the first two methods. The summary state will be affected by mWakefulness, so it will be processed in a cycle.

At the same time, the for loop is executed twice only when the screen goes off and goes to sleep or screensaver. Otherwise, the for loop is executed once. See Android R PowerManagerService module (4) for a detailed analysis of this method.

2.8. UpdateProfilesLocked ()

The above methods update the global state. This method updates whether different users are locked based on the state stored in ProfilePowerState.

2.9. UpdateDisplayPowerStateLocked ()

This method is used to request and update Display state, and in this method, it identifies multiple properties that affect Display state, encapsulates these values in a DisplayPowerRequest object, makes a request to the DisplayMangerService, Finally, DMS completes the update of Display brightness and status:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private boolean updateDisplayPowerStateLocked(int dirty) {
    final boolean oldDisplayReady = mDisplayReady;
    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)) ! =0) {
        // Get the requested 'policy' based on the system wakeup state: off, doze, dim or bright.
        mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
 
        final boolean autoBrightness;  // Whether auto brightness is enabled
        final int screenBrightnessOverride; // Whether there is coverage brightness.// WindowManager overrides brightness values, such as adjusting brightness while playing video
        mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
        // Whether to use automatic brightness
        mDisplayPowerRequest.useAutoBrightness = autoBrightness;
        // Check whether a PSensor Wakelock exists
        mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
        // Whether the brightness is enhanced
        mDisplayPowerRequest.boostScreenBrightness = shouldBoostScreenBrightness();
        // Set some battery information to be used when setting brightness
        updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest);
        // When the wake state is Doze, determine the state of display
        if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
            mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
            if((mWakeLockSummary & WAKE_LOCK_DRAW) ! =0
                    && !mDrawWakeLockOverrideFromSidekick) {
                if (mDisplayPowerRequest.dozeScreenState == Display.STATE_DOZE_SUSPEND) {
                    mDisplayPowerRequest.dozeScreenState = Display.STATE_DOZE;
                }
                if(mDisplayPowerRequest.dozeScreenState == Display.STATE_ON_SUSPEND) { mDisplayPowerRequest.dozeScreenState = Display.STATE_ON; }}// Screen brightness when Doze
            mDisplayPowerRequest.dozeScreenBrightness =
                    mDozeScreenBrightnessOverrideFromDreamManager;
        } else {
            mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
            mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
        }
        // Initiates the request and returns a value indicating whether the request has been completed in the DisplayPowerController
        mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                mRequestWaitForNegativeProximity);
        // A flag to release PSensor WakeLock lock
        mRequestWaitForNegativeProximity = false;
 
        if((dirty & DIRTY_QUIESCENT) ! =0) {
            sQuiescent = false;  // This value mainly controls the backlight alone. The default is false}}returnmDisplayReady && ! oldDisplayReady; }Copy the code

When a request is made to DisplayManagerService, all information is encapsulated in a DisplayPowerRequest object, where the policy property value has five types:

  • POLICY_OFF: The request screen turns off.
  • POLICY_DOZE: Request the screen to enter the Doze state;
  • POLICY_DIM: Request screen enters Dim state,
  • POLICY_BRIGHT: the request screen is normally on.
  • POLICY_VR: VR mode dependent;

In the process of the request, through getDesiredScreenPolicyLocked () method, according to the current state and WakeLock awakened state to decide to Display state of the request:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

int getDesiredScreenPolicyLocked(a) {
    // If the Asleep state is Asleep, the Display state is set to OFF
    if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) {
        return DisplayPowerRequest.POLICY_OFF;
    }
    // If the current wakeup state is Doze, the Display state is set to the state specified by Doze
    if (mWakefulness == WAKEFULNESS_DOZING) {
        if((mWakeLockSummary & WAKE_LOCK_DOZE) ! =0) {
            return DisplayPowerRequest.POLICY_DOZE;
        }
        if (mDozeAfterScreenOff) {
            // Skip Doze and set it to OFF
            returnDisplayPowerRequest.POLICY_OFF; }}...// If there is a screen lock, the user activity status is ON, and the brightness is enhanced, the Display status will be set to ON
    if((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) ! =0|| (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) ! =0| |! mBootCompleted || mScreenBrightnessBoostInProgress) {return DisplayPowerRequest.POLICY_BRIGHT;
    }
    // If the above conditions are not met, DIM is set by default
    return DisplayPowerRequest.POLICY_DIM;
}
Copy the code

When the DisplayPowerController completes the request, it returns true to mDisplayReady, indicating that the request is complete. The following flow is summarized in the bright-screen flow.

2.10. UpdateDreamLocked ()

This method is used to update the device Dreamland status, such as whether to enter Dream, Doze, or hibernate:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void updateDreamLocked(int dirty, boolean displayBecameReady) { 
    if((dirty & (DIRTY_WAKEFULNESS | DIRTY_USER_ACTIVITY | DIRTY_WAKE_LOCKS | DIRTY_BOOT_COMPLETED | DIRTY_SETTINGS | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE | DIRTY_BATTERY_STATE)) ! =0 || displayBecameReady) {
        if (mDisplayReady) {  //mDisplayReady is true before further execution
            // Send a message asynchronously through HandlerscheduleSandmanLocked(); }}}Copy the code

As you can see, updates to dreamland-related states rely on the mDisplayReady variable, which indicates whether Display is ready, so the method body will be called further only if it is ready. The handleSandman() method is called from the PMS main thread to perform Dreamland operations:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void handleSandman(a) {...synchronized (mLock) {
        // If the sleep elf is summoned and the Display state is ready
        if (mSandmanSummoned && mDisplayReady) {
            // Check whether you can enter Dreamland
            startDreaming = canDreamLocked() || canDozeLocked();
            mSandmanSummoned = false;
        } else {
            startDreaming = false; }}final boolean isDreaming;
    if(mDreamManager ! =null) {
        if (startDreaming) {
            mDreamManager.stopDream(false /*immediate*/);
            // Start entering Dreamland
            mDreamManager.startDream(wakefulness == WAKEFULNESS_DOZING);
        }
        isDreaming = mDreamManager.isDreaming();
    } else {
        isDreaming = false;
    }
 
    synchronized (mLock) {
        ......
        // Determine whether the dream should continue.
        if (wakefulness == WAKEFULNESS_DREAMING) {
            if (isDreaming && canDreamLocked()) {
                if (mDreamsBatteryLevelDrainCutoffConfig >= 0&& mBatteryLevel < mBatteryLevelWhenDreamStarted - mDreamsBatteryLevelDrainCutoffConfig && ! isBeingKeptAwakeLocked()) {/ / from the Dreamland
                } else {
                    return; // continue dreaming}}// Exit Dreamland and go to sleep
            if (isItBedTimeYetLocked()) {
                goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
                updatePowerStateLocked();
            } else {
                // Wake up the device
                wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
                        PowerManager.WAKE_REASON_UNKNOWN,
                        "android.server.power:DREAM_FINISHED", Process.SYSTEM_UID, mContext.getOpPackageName(), Process.SYSTEM_UID); updatePowerStateLocked(); }}else if (wakefulness == WAKEFULNESS_DOZING) {
            if (isDreaming) {
                return; // continue dozing
            }
 
            // The screen is removed
            reallyGoToSleepNoUpdateLocked(SystemClock.uptimeMillis(), Process.SYSTEM_UID);
            // Update the PMS statusupdatePowerStateLocked(); }}// Stop dream.
    if (isDreaming) {
        mDreamManager.stopDream(false /*immediate*/); }}Copy the code

In the above method, DreamManager will be called to handle the specific Dreamland logic, which will be analyzed in detail in the DreamManagerService module analysis.

2.11. FinishWakefulnessChangeIfNeededLocked ()

This method mainly updates the last part of the wake up state after it changes:

private void finishWakefulnessChangeIfNeededLocked(a) {
    if (mWakefulnessChanging && mDisplayReady) {
        // If the wake state is in Doze state, no processing is done
        if (mWakefulness == WAKEFULNESS_DOZING
                && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
            return; // wait until dream has enabled dozing
        }
        if (mWakefulness == WAKEFULNESS_AWAKE) {
            // if the screen lighting process exceeds 200ms, output the screen lighting time
            if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
                Slog.w(TAG, "Screen on took " + latencyMs + " ms"); }}// The status update is complete
        mWakefulnessChanging = false;
        // Handle the change of the wake up state through NotifiermNotifier.onWakefulnessChangeFinished(); }}Copy the code

This method is executed only when the screen state changes. After entering the method, through Notifier# onWakefulnessChangeFinished destroy () method to send bright screen, screen broadcast, etc.

The logScreenOn() method in this method will print out how long it takes for the entire screen to light up, which is useful in everyday troubleshooting.

2.12. UpdateSuspendBlockerLocked ()

This method is used to update SuspendBlocker lock status. The Suspend lock mechanism is a lock in the Android framework that represents all WakeLock levels above the framework layer. A Wakelock lock is performed by an APP or other component in the PMS module. A Wakelock lock is performed by an APP or other component in the SYSTEM_server module. Writing nodes to Hal through the Suspend lock, the Kernal layer reads the nodes to wake or hibernate the CPU.

A detailed analysis of this method is summarized in the WakeLock mechanism of the Android R PowerManagerService module (2).

So far, all the core methods in PMS have been briefly analyzed. Some methods only explain their effects and will be analyzed in detail later in the specific business.