Android R PowerManagerService模块(2) WakeLock机制

4,655 阅读11分钟

WakeLock是Android中为应用层及框架层提供的用来保证CPU处于唤醒状态的一种锁机制。PMS中为应用及框架层其他组件提供了接口,进行WakeLock的申请和释放。应用在申请WakeLock时,需要在清单文件中配置android.Manifest.permission.WAKE_LOCK权限。下面详细看下WakeLock机制的实现。

1.WakeLock分类

根据作用时间,WakeLock可以分为永久锁和超时锁:

  • 永久锁:只要获取了WakeLock锁,必须显式进行释放,否则系统会一直持有该锁;
  • 超时锁:在到达给定时间后,自动释放WakeLock锁,其实现原理为方法内部维护了一个Handler进行。 根据释放原则,WakeLock可以分为计数锁和非计数锁:
  • 计数锁:一次申请必须对应一次释放;
  • 非计数锁:不管申请多少次,只需要一次就可以释放该WakeLock。 默认为计数锁。WakeLock机制从上到下架构如下:

wakelock.jpg

WakeLock有三种表现形式:

  • PowerManger.WakeLock:PMS暴露给应用层和其他组件用来申请WakeLock的接口;
  • PowerManagerService.WakeLock: PowerManager.WakeLock在PMS中的表现形式;
  • SuspendBlocker: PowerManagerService.WakeLock在向底层节点操作时的表现形式。

下面是一个申请WakeLock锁的示例:

// 获取PowerManager对象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
// 创建WakeLock锁实例
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
// 申请WakeLock
wl.acquire(); 
// 释放WakeLock
wl.release();

在PowerManager中,共定义了以下七种WakeLock:

// 保证CPU处于唤醒状态
public static final int PARTIAL_WAKE_LOCK;             // 0x00000001
//Deprecated,保证屏幕一直保持Dim状态,按Power键灭屏后,会忽略该锁
public static final int SCREEN_DIM_WAKE_LOCK;          // 0x00000006
//Deprecated,保证屏幕一直保持常亮状态,按Power键灭屏后,会忽略该锁
public static final int SCREEN_BRIGHT_WAKE_LOCK        // 0x0000000a 
//Deprecated,保证屏幕、键盘灯都保持常亮状态,按Power键灭屏后,会忽略该锁
public static final int FULL_WAKE_LOCK                 // 0x0000001a
// 通过PSensor进行亮灭屏工作,PSensor检测到有物体靠近时关闭屏幕,远离时又亮屏
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK // 0x00000020
// 仅用于PMS唤醒状态为Doze时,进入Doze状态后,DreamMangerService会申请该锁,允许CPU挂起
public static final int DOZE_WAKE_LOCK                 // 0x00000040
// 仅用于PMS唤醒状态为Doze时,保证CPU处于运行状态,以进行Doze状态下屏幕的绘制,如AOD、防烧屏显示
public static final int DRAW_WAKE_LOCK                 // 0x00000080

此外还定义了三个Flag,可以在创建、申请及释放WakeLock时和以上几类搭配使用:

// 在申请WakeLock的同时,会点亮屏幕,如来通知亮屏的实现,不能和PARTIAL_WAKE_LOCK一起使用
public static final int ACQUIRE_CAUSES_WAKEUP = 0x10000000;
// 在释放有该Flag的WakeLock时,会稍微延长自动休眠时间一小会儿,不能和PARTIAL_WAKE_LOCK一起使用
public static final int ON_AFTER_RELEASE = 0x20000000;
// 在释放PROXIMITY_SCREEN_OFF_WAKE_LOCK锁时,不会立即解除PSensor监听,而是在PSensor上报远离后,才会亮屏并解除Psensor监听,仅用于释放PROXIMITY_SCREEN_OFF_WAKE_LOCK锁
public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1 << 0;

2.获取WakeLock对象

PowerManager中提供了接口newWakeLock()来创建WakeLock对象:

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

public WakeLock newWakeLock(int levelAndFlags, String tag) {
    validateWakeLockParameters(levelAndFlags, tag);  // 校验Flag
    // 创建WakeLock对象
    return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
}

首先进行了参数的校验,然后调用WakeLock构造方法创建WakeLock对象:

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

WakeLock(int flags, String tag, String packageName) {
    mFlags = flags;                  //表示wakelock类型
    mTag = tag;                      //一个tag,一般为当前类名
    mPackageName = packageName;      //申请wakelock的应用包名
    mToken = new Binder();           //一个Binder标记
    mTraceName = "WakeLock (" + mTag + ")";
}

除以上几个属性之外,WakeLock中还有如下几个属性:

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

//表示内部计数
private int mInternalCount;
//表示外部计数
private int mExternalCount;
//表示是否是计数锁,默认true
private boolean mRefCounted = true;
//表示是否已经持有该锁
private boolean mHeld;
//表示和该wakelock相关联的工作源,这在一些服务获取wakelock时很有用,以便计算工作成本
private WorkSource mWorkSource;
//表示一个历史标签
private String mHistoryTag;

3.WakeLock申请流程

当创建好WakeLock对象以后,就可以申请WakeLock锁了。不管是永久锁还是超时锁,都是通过acquire()方法来申请:

mWakeLock.acquire();             //申请一个永久锁
mWakeLock.acquire(int timeout);  //申请一个超时锁,指定作用时间

PowerManager#acquire()方法如下:

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

// 申请永久锁
public void acquire() {
    synchronized (mToken) {
        acquireLocked();
    }
}
 
// 申请超时锁
public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        //通过Handler设置一个延迟消息自动释放锁
        mHandler.postDelayed(mReleaser, timeout);
    }
}

这两种申请方式完全一样,只不过如果是申请一个超时锁,会通过Handler发送一个延时消息,到达时间后去自动释放锁。继续看acquireLocked()方法:

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

private void acquireLocked() {
    // 计数器+1
    mInternalCount++;
    mExternalCount++;
    //如果是非计数锁或者内部计数值为1,即第一次申请该锁,才会真正去申请
    if (!mRefCounted || mInternalCount == 1) {
        // 移除释放超时锁的Msg
        mHandler.removeCallbacks(mReleaser);
        try {
            // 通过Binder进入PMS中
            mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                    mHistoryTag);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        mHeld = true;  // 表示已持有该锁,申请成功
    }
}

对同一个WakeLock每申请一次,属性值mInternalCount和mExternalCount都会+1,这两个值都用来表示引用计数,前者相对于PowerManager内部,后者则相对于用户操作,之所以有两个引用计数器,主要是为了针对超时锁的释放,如果一个超时锁在已自动释放的情况下,用户手动再释放一次,相当于释放两次。这种情况下由于mExternalCount的存在,就不会导致crash。

mRefCounted用来表示计数锁或非计数锁,默认为true(计数锁),可以通过setReferenceCount()来设置:

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

mHeld表示是否已经持有锁,可以通过调用isHeld()来判断是已申请WakeLock。

以上逻辑都是在APP进程执行的,接下来通过mService进入到system_server,开始执行了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) {
        // 这是PMS中的WakeLock类
        WakeLock wakeLock;
        // 通过IBinder标记确认是否已申请该WakeLock
        int index = findWakeLockIndexLocked(lock);
        boolean notifyAcquire;
        // 说明已申请过该WakeLock,则更新下该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 { // 说明没有申请过该WakeLock
            ......
            // 创建一个WakeLock
            wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid,
                    state);
            // .......
            // 添加到保存系统所有WakeLock的list中
            mWakeLocks.add(wakeLock);
            // 对于PowerManager.PARTIAL_WAKE_LOCK类型锁,省电机制Doze模式会对其进行disable处理
            setWakeLockDisabledStateLocked(wakeLock);
            notifyAcquire = true;
        }
        // 处理PowerManager.ACQUIRE_CAUSES_WAKEUP标记,带有此标记,进行亮屏处理
        applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
        mDirty |= DIRTY_WAKE_LOCKS;  // 设置DIRTY_WAKE_LOCKS标记位
        updatePowerStateLocked();  // 更新状态
        if (notifyAcquire) {
            // 将申请WakeLock动作通知其他组件
            notifyWakeLockAcquiredLocked(wakeLock);
        }
    }
}

首先,通过传入的第一个参数IBinder进行查找WakeLock是否已经存在,若存在,则在原有的WakeLock上更新其属性值;若不存在,则创建一个WakeLock对象,同时将该WakeLock保存到List中,并将相关数据保存到UidState中。

3.1.setWakeLockDisabledStateLocked()

创建WakeLock实例后,接下来调用setWakeLockDisabledStateLocked()方法,这个方法会对PARTIAL_WAKE_LOCK类型的WakeLock进行disable。系统如果持有该类型锁,会导致CPU一直保持唤醒状态而无法休眠,因此在省电策略DeviceIdle模块中,会在某些特定状态下将该类型的锁进行diable:

// 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);
        // 非系统进程
        if (appid >= Process.FIRST_APPLICATION_UID) {
            // Cached inactive processes are never allowed to hold wake locks.
            if (mConstants.NO_CACHED_WAKE_LOCKS) {
                // 强制进入suspend、对应uid进程没有处于active且进程adj大于PROCESS_STATE_RECEIVER
                disabled = mForceSuspendActive  // 强制进入suspend
                        || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState
                                != ActivityManager.PROCESS_STATE_NONEXISTENT &&
                        wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER);
            }
            if (mDeviceIdleMode) {  //处于idle状态时,将非白名单应用wakeLock 禁用
                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;
                }
            }
        }
        // 更新mDisabled属性
        if (wakeLock.mDisabled != disabled) {
            wakeLock.mDisabled = disabled;
            return true;
        }
    }
    return false;
}

主要有三种情况下会禁用Partical WakeLock:

  • 强制进入suspend;
  • WakeLock所属进程不处于active状态,且进程adj大于PROCESS_STATE_RECEIVER;
  • DeviceIdle处于IDLE状态,且所属进程不在doze白名单中;

3.2.applyWakeLockFlagsOnAcquireLocked()处理亮屏标记

接下来调用applyWakeLockFlagsOnAcquireLocked()方法,对ACQUIRE_CAUSES_WAKEUP标记进行处理。如果WakeLock带有标志,并且WakeLock类型为FULL_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK这三种其中之一,则会点亮屏幕:

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

private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
    // 如果持有ACQUIRE_CAUSES_WAKEUP标记,且为亮屏相关三类锁之一
    if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
            && isScreenLock(wakeLock)) {
        ......
        // 亮屏流程
        wakeUpNoUpdateLocked(SystemClock.uptimeMillis(),
                PowerManager.WAKE_REASON_APPLICATION, wakeLock.mTag,
                opUid, opPackageName, opUid);
    }
}

wakeUpNoUpdateLocked()方法是点亮屏幕的主要方法,会在后面部分分析。

3.3.updatePowerStateLocked()更新全局状态

这个方法在PowerManagerService模块(一) 启动流程和核心方法中进行了分析,其中涉及到WakeLock流程的有两个方法:updateWakeLockSummaryLocked()和updateSuspendBlockerLocked()方法,前者已经分析过了,用来将所有的WakeLock统计到mWakeLockSummary全局变量中,这里对后一个方法进行分析。

3.4.updateSuspendBlockerLocked()更新SuspendBlocker

在这个方法中,将会根据系统所有WakeLock的状态,获得一个SuspendBlocker锁:

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

private void updateSuspendBlockerLocked() {
    // 是否因持有WakeLock锁而需要CPU保持唤醒
    final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);
    // 是否因Display状态而需要CPU保持唤醒
    final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
    // 是否开启auto_suspend模式
    final boolean autoSuspend = !needDisplaySuspendBlocker;
    // 是否处于交互状态
    final boolean interactive = mDisplayPowerRequest.isBrightOrDim();
 
    // 如果持有Display SuspendBlocker,则关闭auto-suspend模式
    if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(false);
    }
    // 申请mWakeLockSuspendBlocker锁
    if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.acquire();
        mHoldingWakeLockSuspendBlocker = true;
    }
    // 申请mDisplaySuspendBlocker锁
    if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.acquire();
        mHoldingDisplaySuspendBlocker = true;
    }
    // 设置交互状态
    if (mDecoupleHalInteractiveModeFromDisplayConfig) {
        if (interactive || mDisplayReady) {
            setHalInteractiveModeLocked(interactive);
        }
    }
    // 释放mWakeLockSuspendBlocker
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    // 释放mDisplaySuspendBlocker
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }
    // 开启auto_suspend模式
    if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {
        setHalAutoSuspendModeLocked(true);
    }
}

在创建PMS实例时,创建了两个SuspendBlocker对象:mWakeLockSuspendBlocker和mDisplaySuspendBlocker,在这个方法中则体现出了他们的作用。当系统当前持有WakeLock锁,通过mWakeLockSuspendBlocker使CPU保持唤醒,当屏幕亮屏时,则通过mDisplaySuspendBlocker使CPU保持唤醒。

再看看needDisplaySuspendBlockerLocked()方法,如果该方法返回true,表示需要mDisplaySuspendBlocker锁:

private boolean needDisplaySuspendBlockerLocked() {
    if (!mDisplayReady) {
        return true;
    }
    // 当请求policy为bright或dim时,需要对psensor灭屏场景进行区分
    if (mDisplayPowerRequest.isBrightOrDim()) {
        // 如果在使用psensor且psensor上报靠近事件且psensor灭屏后允许CPU休眠,则表示不需要mDisplaySuspendBlocker
        if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive
                || !mSuspendWhenScreenOffDueToProximityConfig) {
            return true;
        }
    }
    if (mScreenBrightnessBoostInProgress) {
        return true;
    }
    return false;
}

3.5.SuspendBlocker#acquire()申请SuspendBlocker

接下来将目光聚焦到SuspendBlocker上,它是一个接口,并且只有acquire()release()两个方法,用来申请和释放SuspendBlocker。PMS.SuspendBlockerImpl实现了该接口,acquire()方法如下:

public void acquire() {
    synchronized (this) {
        // 引用计数+1
        mReferenceCount += 1;
        if (mReferenceCount == 1) {
            // 进入native层
            mNativeWrapper.nativeAcquireSuspendBlocker(mName);
        }
    }
}

SuspendBlocker对象的申请操作也使用了引用计数方式,每申请一次,其引用计数+1,如果mReferenceCount > 1,不会进行锁的申请,只有当mReferenceCount为0时,才会真正执行申请锁操作,然后通过JNI进入了native层:

// 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());
}

最终,在SystemSuspend中,将会在/sys/power/wake_lock节点中,写入SuspendBlocker的name,完成申请过程:

// 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)) { 
        }
    }
}

这个name就是在创建SuspendBlocker对象时传入的String类型参数,可以通过adb shell看看这个节点内容:

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

整个申请流程可以看出,WakeLock的申请和释放锁,对应着SuspendBlocker的申请和释放锁。也可以说SuspendBlocker类是WakeLock锁功能的具体实现者。只要持有SuspendBlocker锁,都会使得CPU处于唤醒状态。

WakeLock申请时序图如下:

acquire-wakelock.jpg

4.WakeLock的释放

当申请了WakeLock锁(更准确地说是PARTIAL_WAKELOCK类型的WakeLock锁)执行完相应的任务后,要及时对其进行释放,否则会导致CPU无法进入休眠状态,导致电量消耗过快。

在释放WakeLock时,也有两种释放形式:

  • release():正常释放WakeLock;
  • release(int flag):传入一个标志值,以达到修改释放行为的目的。目前只支持一个Flag——RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY,这个值前面已经说过,主要和PSensor灭屏后的动作有关。release()方法如下:
// frameworks/base/core/java/android/os/PowerManager.java

public void release(int flags) {
    synchronized (mToken) {
        if (mInternalCount > 0) {
            // 计数-1
            mInternalCount--;
        }
        // 如果非超时锁,外部计数-1
        if ((flags & RELEASE_FLAG_TIMEOUT) == 0) {
            mExternalCount--;
        }
        if (!mRefCounted || mInternalCount == 0) {
            mHandler.removeCallbacks(mReleaser);
            if (mHeld) {
                try {
                    // 进入PMS中
                    mService.releaseWakeLock(mToken, flags);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
                mHeld = false;
            }
        }
        // 在没有申请锁时进行释放操作,直接抛出异常
        if (mRefCounted && mExternalCount < 0) {
            throw new RuntimeException("WakeLock under-locked " + mTag);
        }
    }
}

对于计数锁的释放,每次都会对内部计数值mInternalCount减一,且当它减为0时,才会去执行PMS中的释放流程;对于非计数锁的释放,每次都会调用到PMS释放流程中。

进入PMS流程后,PMS#releaseWakeLock()在进行权限验证后,执行PMS#releaseWakeLockInternal()方法:

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

private void releaseWakeLockInternal(IBinder lock, int flags) {
    synchronized (mLock) {
        //查找WakeLock是否存在,不存在直接返回
        int index = findWakeLockIndexLocked(lock);
        if (index < 0) {
            return;
        }
        WakeLock wakeLock = mWakeLocks.get(index);
        // 如果带有该Flag,它会在距离传感器上报远离事件后才会取消PSensor监听
        if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) {
            //表示在点亮屏幕前需要等待PSensor返回远离值
            mRequestWaitForNegativeProximity = true;
        }
         
        //释放锁
        removeWakeLockLocked(wakeLock, index);
    }
}

这里对RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY标记进行了处理。这个标记和PSensor的行为有关。

当应用申请了PROXIMITY_SCREEN_OFF_WAKE_LOCK类型WakeLock锁,PMS模块会请求DMS模块注册一个PSensor监听,根据PSensor的事件状态来进行亮灭屏,通话时贴耳灭屏就是这样实现的。如果在PSensor灭屏的场景下,带这个flag去释放WakeLock,那么即使该WakeLock已经释放,依然会保持PSensor的监听状态,直到PSensor上报远离事件后,才会解除PSensor监听。

然后将执行removeWakeLockLocked()方法进行进一步操作:

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

private void removeWakeLockLocked(WakeLock wakeLock, int index) {
    mWakeLocks.remove(index);  // 从保存系统WakeLock列表中移除该WakeLock
    // 更新WakeLock所属的UidState中的相关wakeLock数据
    ......
    // 对带有ON_AFTER_RELEASE标志的wakelock进行处理
    applyWakeLockFlagsOnReleaseLocked(wakeLock);
    mDirty |= DIRTY_WAKE_LOCKS;  // 设置标记位
    // 更新PMS全局状态
    updatePowerStateLocked();
}

首先,将该WakeLock从mWakeLocks列表中移除;

接下来,执行applyWakeLockFlagsOnReleaseLocked()方法,对带有ON_AFTER_RELEASE标志的WakeLock进行处理。在释放带有该标记的WakeLock时,会更新用户活动状态,从而当没有用户操作情况下,会延迟自动灭屏时间:

// 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)) {
        // 更新用户活动时间,用于延长灭屏时间
        userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
                PowerManager.USER_ACTIVITY_EVENT_OTHER,
                PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
                wakeLock.mOwnerUid);
    }
}

userActivityNoUpdateLocked()方法会更新用户活动时间,该方法的详细分析见Android R PowerManagerService模块(4) 灭屏流程

最后,执行updatePowerStateLocked()方法,该方法中和WakeLock相关的方法已经在申请流程中说过,这里只看看释放相关代码:

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

private void updateSuspendBlockerLocked() {
    ......
    // 释放mWakeLockSuspendBlocker
    if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
        mWakeLockSuspendBlocker.release();
        mHoldingWakeLockSuspendBlocker = false;
    }
    // 释放mDisplaySuspendBlocker
    if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
        mDisplaySuspendBlocker.release();
        mHoldingDisplaySuspendBlocker = false;
    }
}

和申请流程一样,还是对mWakeLockSuspendBlocker和mDisplaySuspendBlocker进行释放:

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

public void release() {
    synchronized (this) {
        mReferenceCount -= 1;  // 引用计数-1
        if (mReferenceCount == 0) {
            // 进入native层
            mNativeWrapper.nativeReleaseSuspendBlocker(mName);
        } else if (mReferenceCount < 0) {
            // ......
        }
    }
}

在释放时对引用计数-1,当mReferenceCount为0时,会进入native层进行最后的释放:

// hardware/libhardware_legacy/power.cpp

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

最终,也会在SystemSuspend中,在/sys/power/wake_unlock节点中写入SuspendBlocker的name,完成释放:

// 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;
        }
    }
}

至此,WakeLock释放流程执行完毕。

5.总结

WakeLock锁的申请和释放流程,实际上就是通过SuspendBlocker操作/sys/power/wake_lock和/sys/power/wake_unlock节点,来控制设备的唤醒和休眠。 SuspendBlocker官方的解释是:SuspendBlocker相当于持有部分唤醒锁,该接口在内部使用,以避免在高级别唤醒锁机制上引入内部依赖关系。

在PMS中,共创建了三个SuspendBlocker锁,其中两个已经见过了:

    1. mWakeLockSuspendBlocker锁:表示由WakeLock而使CPU保持唤醒状态,该锁的申请条件是mWakeLockSummary & WAKE_LOCK_CPU) != 0; mWakeLockSummary汇总了所有WakeLock锁,当申请了PARTIAL_WAKE_LOCK、DRAW_WAKE_LOCK锁,或者屏幕处于唤醒、屏保时,会给该属性设置WAKE_LOCK_CPU标记位,从而申请mWakeLockSuspendBlocker锁,向/sys/power/wake_lock节点中写入"PowerManager.WakeLocks",使CPU保持唤醒状态。
    1. mDisplaySuspendBlocker锁:表示由于屏幕亮屏而使CPU保持唤醒状态。只要屏幕处于亮屏状态时,就会申请mDisplaySuspendBlocker锁,向/sys/power/wake_lock中写入“PowerManagerService.Display”;
    1. PowerManagerService.Broadcasts锁:表示由于需要确保Notifier中广播的成功发送,而使CPU保持唤醒状态;

该锁在分析流程中还暂时没有碰到,它以构造方法的形式传给了Notifier类,PMS中和其他服务的部分交互通过Notifier进行,还有亮灭屏广播都是由PMS交给Notifier来发送。如果CPU在广播发送过程中进入休眠,则广播无法发送完成,因此,需要一个锁来保证Notifier中广播的成功发送,这就是PowerManagerService.Broadcasts锁的作用。当广播发送完毕后,该锁就立即释放。