WakeLock is a locking mechanism that Android provides for the application layer and framework layer to keep the CPU awake. The PMS provides interfaces for applications and other framework layer components to apply for and release WakeLock. Application when applying for WakeLock, need the android Manifest file configuration. The Manifest. Permission. WAKE_LOCK permission. Here’s a closer look at the implementation of the WakeLock mechanism.

1. The WakeLock classification

WakeLock can be classified into permanent locks and timeout locks according to the duration of operation:

  • Permanent lock: Once a WakeLock lock is acquired, it must be explicitly released. Otherwise, the system will hold the lock forever.
  • Timeout lock: Automatically releases a WakeLock lock after a specified time has elapsed. This is done by maintaining a Handler inside the method.

According to the release principle, WakeLock can be divided into count and non-count locks:

  • Count locks: one request must correspond to one release;
  • Non-counting lock: No matter how many times you request it, you only need to release the WakeLock once.

The default is count lock. The WakeLock mechanism from top to bottom is as follows:

WakeLock comes in three forms:

  • PowerManger.WakeLock: The interface that the PMS exposes to the application layer and other components to apply for WakeLock;
  • PowerManagerService. WakeLock: PowerManager. WakeLock in PMS forms;
  • SuspendBlocker: PowerManagerService WakeLock to node operation at the bottom of the form.

Here is an example of applying for a WakeLock lock:

// Get the PowerManager object
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// Create a WakeLock instance
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
WakeLock / / application
wl.acquire(); 
/ / release WakeLock
wl.release();
Copy the code

In PowerManager, the following seven WakeLock types are defined:

// Make sure the CPU is awake
public static final int PARTIAL_WAKE_LOCK;             // 0x00000001
//Deprecated to ensure that the screen remains Dim at all times. Press Power to remove the screen and the lock will be ignored
public static final int SCREEN_DIM_WAKE_LOCK;          // 0x00000006
//Deprecated to ensure that the screen is always on and will be Deprecated. Press Power to turn off the screen and the lock will be ignored
public static final int SCREEN_BRIGHT_WAKE_LOCK        // 0x0000000a 
//Deprecated to ensure that the screen and keyboard lights are always on. After pressing Power to turn off the screen, the lock will be ignored
public static final int FULL_WAKE_LOCK                 // 0x0000001a
// Use the PSensor to turn on and off the screen. The PSensor turns off the screen when it detects an object approaching and turns on again when it is far away
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK // 0x00000020
// This lock is only used when the PMS wake state is Doze. After entering the Doze state, DreamMangerService will apply for this lock and allow the CPU to hang
public static final int DOZE_WAKE_LOCK                 // 0x00000040
// Only when the PMS is in Doze state, ensure that the CPU is running to draw the screen in Doze state, such as AOD and anti-burn screen display
public static final int DRAW_WAKE_LOCK                 // 0x00000080
Copy the code

There are also three flags that can be used with the above categories when creating, applying for, and releasing WakeLock:

// While applying for a WakeLock, the screen will light up. The tautagata notifies the implementation that the screen is lit, and cannot be used with PARTIAL_WAKE_LOCK
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
// When releasing a WakeLock with this Flag, the automatic sleep time will be extended for a little while. It cannot be used with PARTIAL_WAKE_LOCK
public static final int ON_AFTER_RELEASE = 0x20000000;
// Upon the release of PROXIMITY_SCREEN_OFF_WAKE_LOCK, the PSensor monitoring will not be removed immediately. Instead, the screen will light up and the PSensor monitoring will be removed only after the PSensor reports that PROXIMITY_SCREEN_OFF_WAKE_LOCK is away. Only used to release PROXIMITY_SCREEN_OFF_WAKE_LOCK
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1 << 0;
Copy the code

2. Obtain the WakeLock object

PowerManager provides the newWakeLock() interface to create WakeLock objects:

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

public WakeLock newWakeLock(int levelAndFlags, String tag) {
    validateWakeLockParameters(levelAndFlags, tag);  / / check the Flag
    // Create a WakeLock object
    return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
}
Copy the code

The WakeLock constructor is used to create a WakeLock object:

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

WakeLock(int flags, String tag, String packageName) {
    mFlags = flags;                  // Indicates the wakelock type
    mTag = tag;                      // a tag, usually the name of the current class
    mPackageName = packageName;      // Apply for the application package name of Wakelock
    mToken = new Binder();           // A Binder tag
    mTraceName = "WakeLock (" + mTag + ")";
}
Copy the code

In addition to the above properties, WakeLock also has the following properties:

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

// Indicates an internal count
private int mInternalCount;
// indicates an external count
private int mExternalCount;
// Indicates whether it is a count lock. The default is true
private boolean mRefCounted = true;
// Indicates whether the lock is already held
private boolean mHeld;
// Represents the work source associated with the wakelock. This is useful when some services acquire wakelock to calculate the work cost
private WorkSource mWorkSource;
// Represents a history label
private String mHistoryTag;
Copy the code

3.WakeLock application process

Once the WakeLock object is created, you can apply for a WakeLock lock. Acquire () for both permanent and timeout locks:

mWakeLock.acquire();             // Request a permanent lock
mWakeLock.acquire(int timeout);  // Apply for a timeout lock
Copy the code

PowerManager#acquire()

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

// Request a permanent lock
public void acquire(a) {
    synchronized(mToken) { acquireLocked(); }}// Request timeout lock
public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        // Set a delay message with Handler to automatically release the lockmHandler.postDelayed(mReleaser, timeout); }}Copy the code

The two methods are the same, except that if a timeout lock is requested, a delay message is sent to the Handler to release the lock automatically when the time is reached. Continue with the acquireLocked() method:

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

private void acquireLocked(a) {
    // count +1
    mInternalCount++;
    mExternalCount++;
    // If it is not a count lock or the internal count value is 1, it is the first time to apply for the lock
    if(! mRefCounted || mInternalCount ==1) {
        // remove the Msg that releases the timeout lock
        mHandler.removeCallbacks(mReleaser);
        try {
            // Enter PMS with Binder
            mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                    mHistoryTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        mHeld = true;  // Indicates that the lock is held and the application is successful}}Copy the code

Each time a WakeLock is applied, the values mInternalCount and mExternalCount are added to 1. Both values are used to count references, the former relative to PowerManager internals and the latter relative to user operations. If a timeout lock is automatically released, the user manually releases it again, which means that the timeout lock is released twice. In this case, the crash will not occur because of mExternalCount.

MRefCounted is used to count or non-count locks. The default is true and can be set by setReferenceCount() :

public void setReferenceCounted(boolean value) {
    synchronized(mToken) { mRefCounted = value; }}Copy the code

MHeld indicates whether a lock is already held, which can be determined by calling isHeld() to claim a WakeLock.

The above logic is executed in the APP process, then through the mService into the system_server, began to execute the process in the PMS. PMS#acquireWakeLockInternal()

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

private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
        WorkSource ws, String historyTag, int uid, int pid) {
    synchronized (mLock) {
        // This is the WakeLock class in PMS
        WakeLock wakeLock;
        // Verify that the WakeLock has been requested by the IBinder flag
        int index = findWakeLockIndexLocked(lock);
        boolean notifyAcquire;
        // The WakeLock has been applied for. Update the WakeLock
        if (index >= 0) {
            wakeLock = mWakeLocks.get(index);
            if(! wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) { notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName, uid, pid, ws, historyTag); wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid); } notifyAcquire =false;
        } else { // Indicates that the WakeLock has not been applied for.// Create a WakeLock
            wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid,
                    state);
            / /...
            // Add to the list of all WakeLock saved in the system
            mWakeLocks.add(wakeLock);
            // For powerManager.partial_wake_lock type locks, Doze mode disables them
            setWakeLockDisabledStateLocked(wakeLock);
            notifyAcquire = true;
        }
        ACQUIRE_CAUSES_WAKEUP ¶
        applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
        mDirty |= DIRTY_WAKE_LOCKS;  // Set the DIRTY_WAKE_LOCKS flag bit
        updatePowerStateLocked();  // Update the status
        if (notifyAcquire) {
            // Notify other components of the WakeLock request actionnotifyWakeLockAcquiredLocked(wakeLock); }}}Copy the code

First, the first parameter IBinder is passed to find if the WakeLock already exists. If so, the original WakeLock is updated with its property value. If not, create a WakeLock object, save the WakeLock to the List, and save the related data to the UidState.

3.1. SetWakeLockDisabledStateLocked ()

Create WakeLock instance, after the next call setWakeLockDisabledStateLocked () method, this method would be to disable the WakeLock PARTIAL_WAKE_LOCK type. If the system holds this type of lock, the CPU will remain in the wake state and cannot sleep. Therefore, in the DeviceIdle module of power saving policy, this type of lock will be diable in certain states:

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

private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
    if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
            == PowerManager.PARTIAL_WAKE_LOCK) {
        boolean disabled = false;
        final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
        // Non-system process
        if (appid >= Process.FIRST_APPLICATION_UID) {
            // Cached inactive processes are never allowed to hold wake locks.
            if (mConstants.NO_CACHED_WAKE_LOCKS) {
                Suspend is forced. The uid process is not active and adj is greater than PROCESS_STATE_RECEIVER
                disabled = mForceSuspendActive  // Force to enter suspend| | (! wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState ! = ActivityManager.PROCESS_STATE_NONEXISTENT && wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER); }if (mDeviceIdleMode) {  // Disable the wakeLock application when it is in idle state
                final UidState state = wakeLock.mUidState;
                if (Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0 &&
                        Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) < 0&& state.mProcState ! = ActivityManager.PROCESS_STATE_NONEXISTENT && state.mProcState > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { disabled =true; }}}// Update the mDisabled attribute
        if(wakeLock.mDisabled ! = disabled) { wakeLock.mDisabled = disabled;return true; }}return false;
}
Copy the code

There are three main situations in which Partical WakeLock is disabled:

  • Force to enter suspend;
  • The owning process of WakeLock is not in the active state and adj is greater than PROCESS_STATE_RECEIVER.
  • DeviceIdle is in IDLE state and the owning process is not in the Doze whitelist.

3.2. ApplyWakeLockFlagsOnAcquireLocked () with bright screen tag

The next call applyWakeLockFlagsOnAcquireLocked () method, to deal with ACQUIRE_CAUSES_WAKEUP markers. If WakeLock is tagged and the WakeLock type is FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK, or SCREEN_DIM_WAKE_LOCK, the screen lights up:

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

private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
    ACQUIRE_CAUSES_WAKEUP is held and is one of the three types of locks associated with a bright screen
    if((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) ! =0
            && isScreenLock(wakeLock)) {
        ......
        // Screen light processwakeUpNoUpdateLocked(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag, opUid, opPackageName, opUid); }}Copy the code

The wakeUpNoUpdateLocked() method is the main method for lighting up the screen, as discussed later.

3.3. UpdatePowerStateLocked () to update the global state

This method is analyzed in the PowerManagerService module (1) startup process and core methods. There are two methods involved in the WakeLock process: UpdateWakeLockSummaryLocked () and updateSuspendBlockerLocked () method, the former has been analyzed and used to make all the WakeLock counted mWakeLockSummary global variable, The latter method is analyzed here.

3.4. UpdateSuspendBlockerLocked update SuspendBlocker ()

In this method, a SuspendBlocker lock is obtained based on the status of all WakeLock in the system:

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

private void updateSuspendBlockerLocked(a) {
    // Whether the CPU needs to stay awake because it holds a WakeLock lock
    final booleanneedWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) ! =0);
    // Whether the CPU needs to stay awake because of the Display state
    final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
    // Whether to enable the auto_suspend mode
    final booleanautoSuspend = ! needDisplaySuspendBlocker;// Whether it is in an interactive state
    final boolean interactive = mDisplayPowerRequest.isBrightOrDim();
 
    // If you hold Display SuspendBlocker, turn off auto-suspend mode
    if(! autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) { setHalAutoSuspendModeLocked(false);
    }
    // Apply for mWakeLockSuspendBlocker lock
    if(needWakeLockSuspendBlocker && ! mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.acquire(); mHoldingWakeLockSuspendBlocker =true;
    }
    // Apply the mDisplaySuspendBlocker lock
    if(needDisplaySuspendBlocker && ! mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.acquire(); mHoldingDisplaySuspendBlocker =true;
    }
    // Set the interaction state
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        if(interactive || mDisplayReady) { setHalInteractiveModeLocked(interactive); }}/ / release mWakeLockSuspendBlocker
    if(! needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.release(); mHoldingWakeLockSuspendBlocker =false;
    }
    / / release mDisplaySuspendBlocker
    if(! needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.release(); mHoldingDisplaySuspendBlocker =false;
    }
    // Start auto_suspend mode
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true); }}Copy the code

When the PMS instance is created, two SuspendBlocker objects are created: mWakeLockSuspendBlocker and mDisplaySuspendBlocker, and their roles are reflected in this method. MWakeLockSuspendBlocker is used to keep the CPU awake when the system currently holds a WakeLock lock, or mDisplaySuspendBlocker is used to keep the CPU awake when the screen is on.

Look at needDisplaySuspendBlockerLocked () method, if this method returns true, said need mDisplaySuspendBlocker lock:

private boolean needDisplaySuspendBlockerLocked(a) {
    if(! mDisplayReady) {return true;
    }
    // If the requested policy is Bright or DIM, the pSensor screen is off
    if (mDisplayPowerRequest.isBrightOrDim()) {
        MDisplaySuspendBlocker mDisplaySuspendBlocker mDisplaySuspendBlocker mDisplaySuspendBlocker mDisplaySuspendBlocker mDisplaySuspendBlocker mDisplaySuspendBlocker
        if(! mDisplayPowerRequest.useProximitySensor || ! mProximityPositive || ! mSuspendWhenScreenOffDueToProximityConfig) {return true; }}if (mScreenBrightnessBoostInProgress) {
        return true;
    }
    return false;
}
Copy the code

3.5. SuspendBlocker# acquire SuspendBlocker () application

Next, let’s focus on SuspendBlocker, which is an interface with only acquire() and Release () methods for applying and releasing SuspendBlocker. PMS.SuspendBlockerImpl implements this interface, acquire() as follows:

public void acquire(a) {
    synchronized (this) {
        // Reference count +1
        mReferenceCount += 1;
        if (mReferenceCount == 1) {
            // Enter the native layermNativeWrapper.nativeAcquireSuspendBlocker(mName); }}}Copy the code

If the mReferenceCount is > 1, the lock will not be applied. If the mReferenceCount is 0, the lock will be applied. Then enter the native layer via JNI:

// frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}
Copy the code

Finally, from SystemSuspend, SuspendBlocker’s name is written to the /sys/power/wake_lock node to complete the application process:

/ / system/hardware/interfaces/suspend / 1.0 / default/SystemSuspend CPP

void SystemSuspend::incSuspendCounter(const string& name) {
    auto l = std::lock_guard(mCounterLock);
    if (mUseSuspendCounter) {
        mSuspendCounter++;
    } else {
        if (!WriteStringToFd(name, mWakeLockFd)) { 
        }
    }
}
Copy the code

The name is the String argument passed when creating the SuspendBlocker object. You can use the ADB shell to view the node’s contents:

$ adb shell cat /sys/power/wake_lock
PowerManager.SuspendLockout PowerManagerService.Display
Copy the code

As you can see, WakeLock applies for and releases locks, corresponding to SuspendBlocker applies for and releases locks. You can also say that SuspendBlocker class is the concrete implementer of the WakeLock lock function. Holding SuspendBlocker locks causes the CPU to wake up.

WakeLock application sequence diagram is as follows:

4. Release WakeLock

After applying for a WakeLock lock (or PARTIAL_WAKELOCK type WakeLock lock), release the lock in a timely manner. Otherwise, the CPU cannot sleep and consumes power too fast.

When releasing WakeLock, there are also two forms of release:

  • Release () : Release WakeLock normally;
  • Release (int Flag) : Passes in a flag value to modify the release behavior. Currently, only one Flag — is supportedRELEASE_FLAG_WAIT_FOR_NO_PROXIMITY, this value is mainly related to the action of PSensor after the screen is off. The release() method looks like this:
// frameworks/base/core/java/android/os/PowerManager.java

public void release(int flags) {
    synchronized (mToken) {
        if (mInternalCount > 0) {
            / / count - 1
            mInternalCount--;
        }
        // If the lock is not timeout, the external count is -1
        if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
            mExternalCount--;
        }
        if(! mRefCounted || mInternalCount ==0) {
            mHandler.removeCallbacks(mReleaser);
            if (mHeld) {
                try {
                    // Enter PMS
                    mService.releaseWakeLock(mToken, flags);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                mHeld = false; }}// Throw an exception if the lock is not unlocked
        if (mRefCounted && mExternalCount < 0) {
            throw new RuntimeException("WakeLock under-locked "+ mTag); }}}Copy the code

For count lock release, the internal count of mInternalCount is reduced by one each time, and when it is reduced to 0, the release process in PMS is performed. For the release of a non-counting lock, the PMS release process is invoked each time.

After entering the PMS process, PMS#releaseWakeLock() executes the PMS#releaseWakeLockInternal() method after permission verification:

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

private void releaseWakeLockInternal(IBinder lock, int flags) {
    synchronized (mLock) {
        // Check if WakeLock exists
        int index = findWakeLockIndexLocked(lock);
        if (index < 0) {
            return;
        }
        WakeLock wakeLock = mWakeLocks.get(index);
        // If the Flag is set, the PSensor will cancel the monitoring only after the distance sensor reports the distance event
        if((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) ! =0) {
            // Indicates that you need to wait for PSensor to return a distance value before lighting up the screen
            mRequestWaitForNegativeProximity = true;
        }
         
        / / releases the lockremoveWakeLockLocked(wakeLock, index); }}Copy the code

The RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY flag is processed here. This flag is related to PSensor behavior.

When an application applies for a WakeLock lock of the type PROXIMITY_SCREEN_OFF_WAKE_LOCK, the PMS module will request the DMS module to register a PSensor monitor to turn on and off the screen according to the event status of the PSensor. This is how the screen is turned on and off during a call. If you release a WakeLock with this flag when the PSensor screen is off, the PSensor monitors the WakeLock even after the WakeLock is released. The PSensor monitors the WakeLock until the PSensor reports a remote event.

The removeWakeLockLocked() method is then called to do further:

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

private void removeWakeLockLocked(WakeLock wakeLock, int index) {
    mWakeLocks.remove(index);  // Remove the WakeLock from the save system WakeLock list
    // Update relevant WakeLock data in the UidState that WakeLock belongs to.// Handle wakelock with ON_AFTER_RELEASE flag
    applyWakeLockFlagsOnReleaseLocked(wakeLock);
    mDirty |= DIRTY_WAKE_LOCKS;  // Set the flag bit
    // Update the PMS global status
    updatePowerStateLocked();
}
Copy the code

First, remove the WakeLock from the mWakeLocks list.

Next, execute applyWakeLockFlagsOnReleaseLocked () method, to deal with a WakeLock with ON_AFTER_RELEASE logo. When a WakeLock with this flag is released, the user activity status is updated so that the automatic extinction time is delayed when there is no user action:

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

private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
    if((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) ! =0
            && isScreenLock(wakeLock)) {
        // Updates the user activity time to prolong the screen off timeuserActivityNoUpdateLocked(SystemClock.uptimeMillis(), PowerManager.USER_ACTIVITY_EVENT_OTHER, PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS, wakeLock.mOwnerUid); }}Copy the code

UserActivityNoUpdateLocked () method will update user activity time, detailed analysis of the method to see Android R PowerManagerService module (4) screen process.

Finally, execute the updatePowerStateLocked() method, which has the WakeLock related method mentioned in the application process. Here’s just the release code:

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

private void updateSuspendBlockerLocked(a) {.../ / release mWakeLockSuspendBlocker
    if(! needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) { mWakeLockSuspendBlocker.release(); mHoldingWakeLockSuspendBlocker =false;
    }
    / / release mDisplaySuspendBlocker
    if(! needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) { mDisplaySuspendBlocker.release(); mHoldingDisplaySuspendBlocker =false; }}Copy the code

Release mWakeLockSuspendBlocker and mDisplaySuspendBlocker as in the application process:

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

public void release(a) {
    synchronized (this) {
        mReferenceCount -= 1;  // Reference count -1
        if (mReferenceCount == 0) {
            // Enter the native layer
            mNativeWrapper.nativeReleaseSuspendBlocker(mName);
        } else if (mReferenceCount < 0) {
            / /...}}}Copy the code

When releasing, the reference count is -1, and when mReferenceCount is 0, it will enter the native layer for the final release:

// hardware/libhardware_legacy/power.cpp

static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
    release_wake_lock(name.c_str());
}
Copy the code

Finally, SuspendBlocker’s name is written to the /sys/power/ wake_UNLOCK node in SystemSuspend as well:

/ / system/hardware/interfaces/suspend / 1.0 / default/SystemSuspend CPP

void SystemSuspend::decSuspendCounter(const string& name) {
    auto l = std::lock_guard(mCounterLock);
    if (mUseSuspendCounter) {
        if (--mSuspendCounter == 0) {
            mCounterCondVar.notify_one();
        }
    } else {
        if (!WriteStringToFd(name, mWakeUnlockFd)) {
            PLOG(ERROR) << "error writing " << name << " to "<< kSysPowerWakeUnlock; }}}Copy the code

At this point, the WakeLock release process is complete.

5. To summarize

SuspendBlocker operates /sys/power/wake_lock and /sys/power/ wake_UNLOCK to wake and sleep a WakeLock. SuspendBlocker holds a partial wakeup lock. This interface is used internally to avoid introducing internal dependencies on high-level wakeup locks.

In PMS, there are three SuspendBlocker locks created, two of which have already been seen:

    1. MWakeLockSuspendBlocker lock: Indicates that the CPU is kept awake by WakeLockmWakeLockSummary & WAKE_LOCK_CPU) ! = 0;

MWakeLockSummary summarizes all WakeLock locks. The WAKE_LOCK_CPU flag is set when PARTIAL_WAKE_LOCK and DRAW_WAKE_LOCK are applied, or when the screen is awake or screensave. Apply mWakeLockSuspendBlocker lock and write “powerManager. WakeLocks” to the /sys/power/wake_lock node to keep the CPU awake.

    1. MDisplaySuspendBlocker lock: Indicates that the CPU is kept awake because the screen is lit. As long as the screen is in a state of bright screen, can apply for mDisplaySuspendBlocker locks, to the/sys/power/wake_lock writes “PowerManagerService. Display”;
    1. PowerManagerService. Broadcasts lock: said due to the need to ensure the success of the Notifier broadcast to send, and keep the CPU awaken state;

This lock is not yet encountered in the analysis process. It is passed to the Notifier class in the form of constructors. Some interactions between the PMS and other services are carried out through the Notifier, and the on-off screen broadcasts are sent by the PMS to the Notifier. If the CPU in the process of radio sent into dormancy, the radio can’t send completed, therefore, need a lock to ensure that the Notifier broadcast success to send, this is PowerManagerService Broadcasts the role of the lock. When the broadcast is sent, the lock is released immediately.