吾生也有涯,而知也无涯。这篇文章主要讲述Android7.0 PowerManagerService WakeLock的使用及流程相关的知识,希望能为你提供帮助。
作为移动终端,
电量是一种稀缺资源,
需要尽可能的节省。于是,
android系统在空闲时,
会主动进入到休眠状态。
我们知道整个Android系统中运行着很多个进程,
因此必须有一种机制能够知道每个进程是否正在进行重要的工作,
只有这样Android系统才能对整个终端当前的状态做出判断。
显然我们不能启动一个进程,
去主动监管其它所有进程的工作状态,
这样CPU开销太大,
反而加剧了电量的消耗。为此Android引入了基于WakeLock的电量管理机制,
而PMS就是专门负责管理WakeLock的进程。
个人觉得WakeLock机制的思想,
有点类似于早期通信领域局域网中的令牌环机制。当局域网中有设备需要发送数据时,
需要申请令牌(Token),
申请到令牌才能发送数据;
设备发送完数据后,
再释放掉令牌。
与此相似,
Android设备中运行的进程需要使用电量资源时,
也需要向PMS申请一个WakeLock;
当工作完成后,
就释放掉申请的WakeLock。PMS通过判断当前是否还有进程持有WakeLock,
就能得出系统是否空闲的结论。
从这里也可以看出,
当我们写一个永不停止工作的线程,
但不申请WakeLock时,
系统仍然可以休眠。在休眠时,
CPU不会再耗费资源去调度该线程,
于是“永不停止工作”被打上了引号。
接下来,
我们就来看看PMS中的WakeLock。
一、创建WakeLock
我们以RIL.java为例,
看看一般情况下,
PMS以外的其它进程如何使用WakeLock。
在RIL.java的构造函数中:
public RIL(Context context, int preferredNetworkType,
int cdmaSubscription, Integer instanceId) {
.............
PowerManager pm =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
//获取WakeLock,
第一个参数决定了WakeLock的等级和flag
mWakeLock =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);
//默认WakeLocked会ReferenceCounted,
即一次申请对应一次释放
//设为false后,
一次释放就可以对应所有的申请
mWakeLock.setReferenceCounted(false);
...........
//RIL.java中自己维护了WakeLockCount
mWakeLockCount =
0;
...........
}
从上面的代码, 可以看出调用PowerManager的newWakeLock函数, 可以创建出WakeLock。
我们看看newWakeLock函数定义:
public WakeLock newWakeLock(int levelAndFlags, String tag) {
//检查参数有效性,
即levelAndFlags必须对应于PowerManager中定义的WakeLock级别和flag,
tag不能为空
validateWakeLockParameters(levelAndFlags, tag);
//此WakeLock为PowerManager定义的内部类
return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
}
1、WakeLock Level
newWakeLock中的第一个参数对应于WakeLock的级别和标志位构成的位图。
目前, 在PowerManger中一共为WakeLock定义了7种level。
/**
* Wake lock level: Ensures that the CPU is running;
the screen and keyboard
* backlight will be allowed to go off.
*
* If the user presses the power button, then the screen will be turned off
* but the CPU will be kept on until all partial wake locks have been released.
* /
public static final int PARTIAL_WAKE_LOCK =
0x00000001;
/**
* Wake lock level: Ensures that the screen is on (but may be dimmed);
* the keyboard backlight will be allowed to go off.
*
* If the user presses the power button, then the SCREEN_DIM_WAKE_LOCK will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
*/
@
Deprecated
public static final int SCREEN_DIM_WAKE_LOCK =
0x00000006;
/**
* Wake lock level: Ensures that the screen is on at full brightness;
* the keyboard backlight will be allowed to go off.
*
*If the user presses the power button, then the SCREEN_BRIGHT_WAKE_LOCK will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
*/
@
Deprecated
public static final int SCREEN_BRIGHT_WAKE_LOCK =
0x0000000a;
/**
* Wake lock level: Ensures that the screen and keyboard backlight are on at
* full brightness.
*
*If the user presses the power button, then the FULL_WAKE_LOCK will be
* implicitly released by the system, causing both the screen and the CPU to be turned off.
*/
@
Deprecated
public static final int FULL_WAKE_LOCK =
0x0000001a;
/**
* Wake lock level: Turns the screen off when the proximity sensor activates.
* If the proximity sensor detects that an object is nearby, the screen turns off
* immediately.Shortly after the object moves away, the screen turns on again.
*
* A proximity wake lock does not prevent the device from falling asleep
* unlike link FULL_WAKE_LOCK, SCREEN_BRIGHT_WAKE_LOCK and SCREEN_DIM_WAKE_LOCK.
* If there is no user activity and no other wake locks are held, then the device will fall asleep (and lock) as usual.
* However, the device will not fall asleep while the screen has been turned off
* by the proximity sensor because it effectively counts as ongoing user activity.
*
* Cannot be used with ACQUIRE_CAUSES_WAKEUP (WakeLock的flag).
*/
//例如拨号,
打通后接听电话,
屏幕变黑
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK =
0x00000020;
/**
* Wake lock level: Put the screen in a low power state and allow the CPU to suspend
* if no other wake locks are held.
*
* This is used by the dream manager to implement doze mode.It currently
* has no effect unless the power manager is in the dozing state.
* /
public static final int DOZE_WAKE_LOCK =
0x00000040;
/**
* Wake lock level: Keep the device awake enough to allow drawing to occur.
*
* This is used by the window manager to allow applications to draw while the
* system is dozing.It currently has no effect unless the power manager is in
* the dozing state.
* /
public static final int DRAW_WAKE_LOCK =
0x00000080;
从上面的代码注释可以看出, WakeLock主要用于控制CPU、屏幕和键盘三大部分( 当然, 现在的Anroid中基本没有键盘了) 。
对于PARTIAL_WAKE_LOCK、SCREEN_DIM_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和FULL_WAKE_LOCK而言, 不考虑Power键的话, 随着等级的提高, 权限也相应增大, 即持有高等级的锁, 能够激活的部分越多; 如果考虑Power键的话, PARTIAL_WAKE_LOCK可以保证CPU不休眠, 反而是权限最大的。
PROXIMITY_SCREEN_OFF_WAKE_LOCK、DOZE_WAKE_LOCK和DRAW_WAKE_LOCK都是和具体场景相关的锁。
2、WakeLock Flag
PowerManager定义的WakeLock Flag很多, 无法一一列举, 就看一下比较常用的:
/**
* Wake lock flag: Turn the screen on when the wake lock is acquired.
*
* Normally wake locks don'
t actually wake the device, they just cause
* the screen to remain on once it'
s already on.Think of the video player
* application as the normal behavior.Notifications that pop up and want
* the device to be on are the exception;
use this flag to be like them.
*
* Cannot be used with PARTIAL_WAKE_LOCK.
* /
public static final int ACQUIRE_CAUSES_WAKEUP =
0x10000000;
/**
* Wake lock flag: When this wake lock is released, poke the user activity timer
* so the screen stays on for a little longer.
*
* Will not turn the screen on if it is not already on.
*
* Cannot be used with PARTIAL_WAKE_LOCK.
* /
public static final int ON_AFTER_RELEASE =
0x20000000;
..................
总结一下上面的内容, 如下所示:
文章图片
WakeLock Flag一般与WakeLock Level组合使用, 使用的时候参照一下注释即可。
3、WakeLock的构造函数
最后, 我们来看看WakeLock的构造函数:
WakeLock(int flags, String tag, String packageName) {
//level and flag
mFlags =
flags;
//创建类对应的打印Tag
mTag =
tag;
//创建类的类名
mPackageName =
packageName;
//创建一个Binder对象
//PMS将作为该Binder的客户端监听对应进程是否死亡
mToken =
new Binder();
mTraceName =
"
WakeLock ("
+
mTag +
"
)"
;
}
WakeLock的构造函数中需要注意的地方是, 创建了一个Binder对象。
回忆一下RIL.java中创建WakeLock的过程, 我们就知道这个Binder对象应该是创建在RIL.java所在的Phone进程中。
二、Acquire WakeLock
从上面的分析, 我们知道一个进程创建的WakeLock, 实际上表明了该进程执行某个工作时对电量的需求, 例如声明该工作需要保持屏幕处于点亮状态, 或该工作需要CPU处于唤醒态等。
因此, 进程创建了WakeLock后, 需要将WakeLock发送到PMS中, 让PMS明白该进程的需求。
这种将WakeLock通知到PMS的过程, 就被称为acquire WakeLock。
同样, 我们还是以RIL.java中的使用过程举例:
private void send(RILRequest rr) {
Message msg;
if (mSocket =
=
null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
return;
}msg =
mSender.obtainMessage(EVENT_SEND, rr);
//重点在这里
acquireWakeLock(rr, FOR_WAKELOCK);
msg.sendToTarget();
}
当AP侧向modem发送请求时, 将要调用RIL.java的send函数。send函数将会发送消息给RILSender, 后者利用socket将消息发送给rild进程。
从上面的代码可以看出, 在发送消息给RILSender之前, 调用了acquireWakeLock函数:
private void acquireWakeLock(RILRequest rr, int wakeLockType) {
synchronized(rr) {
.............
switch(wakeLockType) {
case FOR_WAKELOCK:
synchronized (mWakeLock) {
//调用acquire函数
mWakeLock.acquire();
mWakeLockCount+
+
;
mWlSequenceNum+
+
;
............
}
break;
.........
}
rr.mWakeLockType =
wakeLockType;
}
}
我们跟进一下PowerManager中WakeLock的acquire函数:
public void acquire() {
synchronized (mToken) {
acquireLocked();
}
}private void acquireLocked() {
//前面已经提过,
RIL.java中已经将mRefCounted置为false
//如果不将mRefCounted置为false,
意味着acquire和release必须一一对应
//那么每个WakeLock只能acquire一次
if (!mRefCounted || mCount+
+
=
=
0) {
........
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
mHistoryTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld =
true;
}
}
容易看出实际的工作流程将通过Binder通信进入到PMS中:
public void acquireWakeLock(IBinder lock, int flags, String tag, String packageName,
WorkSource ws, String historyTag) {
//参数和权限检查
............
final int uid =
Binder.getCallingUid();
final int pid =
Binder.getCallingPid();
final long ident =
Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}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;
//PMS中维持了一个ArrayList,
记录当前已申请的WakeLock
//findWakeLockIndexLocked查找ArrayList,
判断参数对应的WakeLock,
是否在之前被申请过
int index =
findWakeLockIndexLocked(lock);
boolean notifyAcquire;
if (index >
=
0) {
//如果index大于0,
说明此时Acquire的是一个旧的WakeLock
//例如RIL会多次调用send函数,
于是除第一次外,
都会进入这个分支
wakeLock =
mWakeLocks.get(index);
//这是判断WakeLock对应的成员变量是否发生改变
if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
// Update existing wake lock.This shouldn'
t happen but is harmless.
notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
uid, pid, ws, historyTag);
//若wakelock属性发生了变化,
更新该属性
wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);
}
notifyAcquire =
false;
} else {
//创建一个新的WakeLock,
例如RIL第一次调用send就会进入该分支
wakeLock =
new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid);
try {
//1、监控申请WakeLock的进程是否死亡
lock.linkToDeath(wakeLock, 0);
} catch (RemoteException ex) {
throw new IllegalArgumentException("
Wake lock is already dead."
);
}
//添加到wakelock列表
mWakeLocks.add(wakeLock);
//2、特殊处理PARTIAL_WAKE_LOCK
//实际上,
根据Doze模式的白名单更新wakelock的disabled变量
setWakeLockDisabledStateLocked(wakeLock);
notifyAcquire =
true;
}//3、处理WakeLock对应的Flag
//实际上判断WakeLock是否有ACQUIRE_CAUSES_WAKEUP,
在必要时唤醒屏幕
applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);
mDirty |=
DIRTY_WAKE_LOCKS;
//更新电源状态,
以后单独分析
updatePowerStateLocked();
if (notifyAcquire) {
// This needs to be done last so we are sure we have acquired the
// kernel wake lock.Otherwise we have a race where the system may
// go to sleep between the time we start the accounting in battery
// stats and when we actually get around to telling the kernel to
// stay awake.
//通知wakeLock发生变化
//电量统计服务做相关统计
notifyWakeLockAcquiredLocked(wakeLock);
}
}
}
如上代码中标注的注释, acquireWakeLockInternal中有几处比较重要的地方, 我们一起来分析一下。
1、监听客户端进程死亡
上面的代码中, 第一次创建WakeLock后, 调用了:
.........
lock.linkToDeath(wakeLock, 0);
.........
我们将acquire WakeLock的进程定义为PMS的客户端进程, 那么上面代码的lock, 就是客户端进程中创建的Binder对象的代理。对于RIL而言, 就是存在于Phone进程中的Binder的代理。
PMS调用Binder代理的linkToDeath, 实际上使得PMS成为了对应进程Binder的客户端。于是, 当对应进程死亡后, 将通知PMS。
linkToDeath传入的必须是继承IBinder.DeathRecipient的对象, 作为进程死亡的”讣告”接收者。
我们看看PMS中WakeLock与此相关的定义:
private final class WakeLock implements IBinder.DeathRecipient {
...........
@
Override
public void binderDied() {
//发现客户端进程死亡后,
调用PMS的handleWakeLockDeath进行处理,
传入的参数为WakeLock自己
PowerManagerService.this.handleWakeLockDeath(this);
}
.......
}
我们看看PMS的handleWakeLockDeath函数:
private void handleWakeLockDeath(WakeLock wakeLock) {
synchronized (mLock) {
..........
int index =
mWakeLocks.indexOf(wakeLock);
if (index <
0) {
return;
}removeWakeLockLocked(wakeLock, index);
}
}
跟进removeWakeLockLocked函数:
private void removeWakeLockLocked(WakeLock wakeLock, int index) {
mWakeLocks.remove(index);
//通知到BatteryStatsService
notifyWakeLockReleasedLocked(wakeLock);
//处理WakeLock对应的flag,
与后文applyWakeLockFlagsOnAcquireLocked一起分析
//实际上是判断是否需要立即息屏
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |=
DIRTY_WAKE_LOCKS;
//锁移除后,
还是利用updatePowerStateLocked更新电源状态
updatePowerStateLocked();
}
2、特殊处理PARTIAL_WAKE_LOCK
PMS处理第一次创建的WakeLock时, 还会调用setWakeLockDisabledStateLocked函数进行处理:
private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
//仅会特殊处理PARTIAL_WAKE_LOCK,
毕竟PARTIAL_WAKE_LOCK要求按Power键后CPU依然可以工作
if ((wakeLock.mFlags &
PowerManager.WAKE_LOCK_LEVEL_MASK)
=
=
PowerManager.PARTIAL_WAKE_LOCK) {
boolean disabled =
false;
//设备处于Doze定义的device idle模式时
if (mDeviceIdleMode) {
final int appid =
UserHandle.getAppId(wakeLock.mOwnerUid);
// If we are in idle mode, we will ignore all partial wake locks that are
// for application uids that are not whitelisted.//判断是否为非系统应用
if (appid >
=
Process.FIRST_APPLICATION_UID &
&
//白名单search
Arrays.binarySearch(mDeviceIdleWhitelist, appid) <
0 &
&
Arrays.binarySearch(mDeviceIdleTempWhitelist, appid) <
0 &
&
//判断进程的类型
//ActivityManager中定义的数字最小的为:
常驻的操作UI的系统进程
//因此大概可理解为:
数字越大,
对处理事件的时效性要求越低
mUidState.get(wakeLock.mOwnerUid,
ActivityManager.PROCESS_STATE_CACHED_EMPTY)
>
ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
disabled =
true;
}
}
if (wakeLock.mDisabled !=
disabled) {
wakeLock.mDisabled =
disabled;
return true;
}
}
return false;
}
上面代码大致的含义就是:
在Android Doze模式下, 当终端处于device Idle Mode时,
对于一个非系统应用而言, 如果该应用不在系统定义的白名单中,
并且该应用所在进程的类型表明, 该进程对事件处理的时效性要求不高,
那么即使该应用申请了PARTIAL_WAKE_LOCK, 也不能阻止系统进入休眠状态。
有些设备商, 为了优化系统的功耗, 就修改了这个地方。
例如, 有些系统应用其实也很耗电, 因此可以去掉该函数中对非系统应用的限制, 对系统应用也进行管控。
3、处理WakeLock对应的Flag
前面的代码已经提到, 当acquire WakeLock时, 将调用applyWakeLockFlagsOnAcquireLocked处理WakeLock对应的flag;
当由于进程死亡, 释放WakeLock时, 会调用applyWakeLockFlagsOnReleaseLocked处理WakeLock对应的flag。
从函数命名来看, 这两个函数应该有相似的地方。
3.1 applyWakeLockFlagsOnAcquireLocked
我们先看看applyWakeLockFlagsOnAcquireLocked:
private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
//仅处理ACQUIRE_CAUSES_WAKEUP flag,
同时要求WakeLock的level是与screen有关的,
//即FULL_WAKE_LOCK、SCREEN_BRIGHT_WAKE_LOCK和SCREEN_DIM_WAKE_LOCK
if ((wakeLock.mFlags &
PowerManager.ACQUIRE_CAUSES_WAKEUP) !=
0
&
&
isScreenLock(wakeLock)) {
..............
wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
opPackageName, opUid);
}
}private boolean wakeUpNoUpdateLocked(long eventTime, String reason, int reasonUid,
String opPackageName, int opUid) {
............
//不满足以下条件,
没有唤醒屏幕的必要
if (eventTime <
mLastSleepTime || mWakefulness =
=
WAKEFULNESS_AWAKE
|| !mBootCompleted || !mSystemReady) {
return false;
}try {
mLastWakeTime =
eventTime;
//修改PMS的一些成员变量,
并进行通知
//其中主要的是将mDirty变量的DIRTY_WAKEFULNESS位置为了1
//PMS根据mDirty的位信息管理电源状态,
同时唤醒屏幕
setWakefulnessLocked(WAKEFULNESS_AWAKE, 0);
//通知给电源统计服务
mNotifier.onWakeUp(reason, reasonUid, opPackageName, opUid);
//调用userActivityNoUpdateLocked函数
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
} .....
return true;
}
3.1.1 setWakefulnessLocked
我们看看唤醒屏幕相关的操作:
private void setWakefulnessLocked(int wakefulness, int reason) {
if (mWakefulness !=
wakefulness) {
mWakefulness =
wakefulness;
mWakefulnessChanging =
true;
mDirty |=
DIRTY_WAKEFULNESS;
//定义于frameworks/base/services/core/java/com/android/server/power/Notifier.java中
mNotifier.onWakefulnessChangeStarted(wakefulness, reason);
}
}
public void onWakefulnessChangeStarted(final int wakefulness, int reason) {
final boolean interactive =
PowerManagerInternal.isInteractive(wakefulness);
.......
// Tell the activity manager about changes in wakefulness, not just interactivity.
mHandler.post(new Runnable() {
@
Override
public void run() {
mActivityManagerInternal.onWakefulnessChanged(wakefulness);
}
});
// Handle any early interactive state changes.
// Finish pending incomplete ones from a previous cycle.
if (mInteractive !=
interactive) {
// Finish up late behaviors if needed.
if (mInteractiveChanging) {
handleLateInteractiveChange();
}// Start input as soon as we start waking up or going to sleep.
mInputManagerInternal.setInteractive(interactive);
mInputMethodManagerInternal.setInteractive(interactive);
// Notify battery stats.
try {
mBatteryStats.noteInteractive(interactive);
} catch (RemoteException ex) { }// Handle early behaviors.
mInteractive =
interactive;
mInteractiveChangeReason =
reason;
mInteractiveChanging =
true;
//重点在这个位置
handleEarlyInteractiveChange();
}
}/**
* Handle early interactive state changes such as getting applications or the lock
* screen running and ready for the user to see (such as when turning on the screen).
*/
private void handleEarlyInteractiveChange() {
synchronized (mLock) {
if (mInteractive) {
// Waking up...
mHandler.post(new Runnable() {
@
Override
public void run() {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
//mPolicy对应于PhoneWindowManager
mPolicy.startedWakingUp();
}
});
// Send interactive broadcast.
mPendingInteractiveState =
INTERACTIVE_STATE_AWAKE;
mPendingWakeUpBroadcast =
true;
updatePendingBroadcastLocked();
} else {
// Going to sleep...
// Tell the policy that we started going to sleep.
final int why =
translateOffReason(mInteractiveChangeReason);
mHandler.post(new Runnable() {
@
Override
public void run() {
mPolicy.startedGoingToSleep(why);
}
});
}
}
}
从上面的代码来看, 应该是PhoneWindowManager完成亮屏前的初始化工作, 然后回调到PowerManager的wakeUp函数。
整个过程还是比较复杂的, 需要单独进行分析, 此处不做进一步说明。
3.2 applyWakeLockFlagsOnReleaseLocked
现在我们再看看applyWakeLockFlagsOnReleaseLocked函数:
private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
//仅处理ON_AFTER_RELEASE,
同样要求WakeLock的level是与screen有关的
//ON_AFTER_RELEASE并不会立即息屏
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);
}
}
可以看出applyWakeLockFlagsOnAcquireLocked和applyWakeLockFlagsOnReleaseLocked最后均会调用userActivityNoUpdateLocked函数, 只是参数不同。
3.3 userActivityNoUpdateLocked
我们一起来看一下userActivityNoUpdateLocked:
private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {
.............
//过时的事件不需要处理
if (eventTime <
mLastSleepTime || eventTime <
mLastWakeTime
|| !mBootCompleted || !mSystemReady) {
return false;
}...........
try {
if (eventTime >
mLastInteractivePowerHintTime) {
//调用native加载的动态库的powerHint函数,
具体意义不是很清楚
powerHintInternal(POWER_HINT_INTERACTION, 0);
mLastInteractivePowerHintTime =
eventTime;
}//调用BatteryStatsService的noteUserActivity函数,
看代码好像是做一些记录
mNotifier.onUserActivity(event, uid);
//根据参数信息修改mDirty的一些变量
.............
} finally {
........
}
}
从以上代码来看, acquire WakeLock将申请信息递交给PMS统一进行处理。
PMS根据WakeLock的level和flag, 完成修改一些变量、通知BatteryStatsService等工作后,
最终还是依赖于updatePowerStateLocked函数来进行实际的电源状态更新操作。
PMS类中有很多***NoUpdateLocked()方法, 这些方法都有一些共性, 就是仅更新状态, 不负责具体的执行。因为PMS中具体的执行逻辑都是在updatePowerStateLocked方法中。
上述acquire WakeLock主要的工作大致可以总结为下图:
文章图片
三、释放WakeLock
当进程完成工作后, 需要释放之前申请的WakeLock。我们同样以RIL.java中的操作为例:
private void processResponse (Parcel p) {
int type;
type =
p.readInt();
if (type =
=
RESPONSE_UNSOLICITED || type =
=
RESPONSE_UNSOLICITED_ACK_EXP) {
...........
} else if (type =
=
RESPONSE_SOLICITED || type =
=
RESPONSE_SOLICITED_ACK_EXP) {
//处理请求对应的回复信息
RILRequest rr =
processSolicited (p, type);
if (rr !=
null) {
if (type =
=
RESPONSE_SOLICITED) {
//重点在这里
decrementWakeLock(rr);
}
rr.release();
return;
}
} else if (type =
=
RESPONSE_SOLICITED_ACK) {
...........
}
}
我们跟进decrementWakeLock函数:
private void decrementWakeLock(RILRequest rr) {
synchronized(rr) {
switch(rr.mWakeLockType) {
case FOR_WAKELOCK:
synchronized (mWakeLock) {
//前面已经提到过,
RIL.java多个请求复用同一个WakeLock
//并且利用mWakeLockCount记录复用的次数
//这么设计的目的是:
RIL发送请求的数量非常多,
复用WakeLock可以避免多次构造释放
//同时减少与PMS之间Binder通信的次数
if (mWakeLockCount >
1) {
mWakeLockCount--;
} else {
mWakeLockCount =
0;
//所有请求均得到了处理,
调用PowerManager中WakeLock的release函数
mWakeLock.release();
}
}
break;
........
}
}
........
}
现在我们跟进PowerManager中WakeLock定义的release函数:
/**
* Releases the wake lock with flags to modify the release behavior.
*
* This method releases your claim to the CPU or screen being on.
* The screen may turn off shortly after you release the wake lock, or it may
* not if there are other wake locks still held.
*
*/
public void release(int flags) {
synchronized (mToken) {
if (!mRefCounted || --mCount =
=
0) {
mHandler.removeCallbacks(mReleaser);
if (mHeld) {
.......
try {
//还是会调用到PMS中的函数
mService.releaseWakeLock(mToken, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mHeld =
false;
}}
....
}
}
最后一起来看看PMS中释放WakeLock的函数:
public void releaseWakeLock(IBinder lock, int flags) {
//参数和权限检查
.............
final long ident =
Binder.clearCallingIdentity();
try {
releaseWakeLockInternal(lock, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}private void releaseWakeLockInternal(IBinder lock, int flags) {
synchronized (mLock) {
//根据Binder代理,
从存储的ArrayList中找到对应WakeLock的序号
int index =
findWakeLockIndexLocked(lock);
...........
WakeLock wakeLock =
mWakeLocks.get(index);
...........
//RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY,
表示当sensor判断终端离物体较远时,
//才真正释放PROXIMITY_SCREEN_OFF_WAKE_LOCK等级的WakeLock
if ((flags &
PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) !=
0) {
mRequestWaitForNegativeProximity =
true;
}//PMS不再关注客户端进程是否死亡
wakeLock.mLock.unlinkToDeath(wakeLock, 0);
removeWakeLockLocked(wakeLock, index);
}
}private void removeWakeLockLocked(WakeLock wakeLock, int index) {
mWakeLocks.remove(index);
//通知BatteryStatsService
notifyWakeLockReleasedLocked(wakeLock);
//之前分析过,
会做一些记录信息等
applyWakeLockFlagsOnReleaseLocked(wakeLock);
mDirty |=
DIRTY_WAKE_LOCKS;
//依然靠updatePowerStateLocked函数更新终端的电源状态
updatePowerStateLocked();
}
整个release的过程大致可以总结为下图:
文章图片
四、总结
通过前面的分析, 我们知道了向PMS申请电量的基本用法类似于:
........
//1、创建
PowerManager pm =
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock =
pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);
......
//2、acquire
mWakeLock.acquire();
.........
//3、release
mWakeLock.release();
...........
【Android7.0 PowerManagerService WakeLock的使用及流程】当申请发送到PMS后, PMS将针对WakeLock的level和flag信息进行一些处理。
无论是acquire还是release WakeLock, PMS最终将利用updatePowerStateLocked函数对终端的电源状态进行调整。
我们将单独分析一下PMS核心的updatePowerStateLocked函数。
推荐阅读
- Android最佳实践——深入浅出WebSocket协议
- Android JobService的使用及源码分析
- 关于使用Android新版Camera即Camera2的使用介绍 暨解决Camera.PreviewCallback和MediaRecorder无法同时进行
- Android Studio如何轻松整理字符串到string.xml中
- Android Studio 生成aar包多Module引用问题
- Android Studio 生成aar包,并在其他项目中引用
- Android Studio 生成aar包,并非debug包,而是release包
- android 调用 screenrecord 实现录屏
- Android APP使用系统签名