作为移动终端,电量是一种稀缺资源,需要尽可能的节省。于是,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的等级和flagmWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);//默认WakeLocked会ReferenceCounted,即一次申请对应一次释放//设为false后,一次释放就可以对应所有的申请mWakeLock.setReferenceCounted(false);...........//RIL.java中自己维护了WakeLockCountmWakeLockCount = 0;...........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

从上面的代码,可以看出调用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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

从上面的代码注释可以看出,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;..................
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

总结一下上面的内容,如下所示: 
 
WakeLock Flag一般与WakeLock Level组合使用,使用的时候参照一下注释即可。

3、WakeLock的构造函数 
最后,我们来看看WakeLock的构造函数:

WakeLock(int flags, String tag, String packageName) {//level and flagmFlags = flags;//创建类对应的打印TagmTag = tag;//创建类的类名        mPackageName = packageName;//创建一个Binder对象//PMS将作为该Binder的客户端监听对应进程是否死亡mToken = new Binder();mTraceName = "WakeLock (" + mTag + ")";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

当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;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

我们跟进一下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;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

容易看出实际的工作流程将通过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);}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

如上代码中标注的注释,acquireWakeLockInternal中有几处比较重要的地方,我们一起来分析一下。

1、监听客户端进程死亡 
上面的代码中,第一次创建WakeLock后,调用了:

.........
lock.linkToDeath(wakeLock, 0);
.........
  • 1
  • 2
  • 3

我们将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 {...........@Overridepublic void binderDied() {//发现客户端进程死亡后,调用PMS的handleWakeLockDeath进行处理,传入的参数为WakeLock自己PowerManagerService.this.handleWakeLockDeath(this);}.......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们看看PMS的handleWakeLockDeath函数:

private void handleWakeLockDeath(WakeLock wakeLock) {synchronized (mLock) {..........int index = mWakeLocks.indexOf(wakeLock);if (index < 0) {return;}removeWakeLockLocked(wakeLock, index);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

跟进removeWakeLockLocked函数:

private void removeWakeLockLocked(WakeLock wakeLock, int index) {mWakeLocks.remove(index);//通知到BatteryStatsServicenotifyWakeLockReleasedLocked(wakeLock);//处理WakeLock对应的flag,与后文applyWakeLockFlagsOnAcquireLocked一起分析//实际上是判断是否需要立即息屏applyWakeLockFlagsOnReleaseLocked(wakeLock);mDirty |= DIRTY_WAKE_LOCKS;//锁移除后,还是利用updatePowerStateLocked更新电源状态updatePowerStateLocked();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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 &&//白名单searchArrays.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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

上面代码大致的含义就是: 
在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_LOCKif ((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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

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);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
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() {@Overridepublic 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() {@Overridepublic void run() {EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);//mPolicy对应于PhoneWindowManagermPolicy.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() {@Overridepublic void run() {mPolicy.startedGoingToSleep(why);}});}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

从上面的代码来看,应该是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);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看出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 {........}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

从以上代码来看,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) {...........}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

我们跟进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;........}}........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

现在我们跟进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;}}....}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

最后一起来看看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等级的WakeLockif ((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);//通知BatteryStatsServicenotifyWakeLockReleasedLocked(wakeLock);//之前分析过,会做一些记录信息等applyWakeLockFlagsOnReleaseLocked(wakeLock);mDirty |= DIRTY_WAKE_LOCKS;//依然靠updatePowerStateLocked函数更新终端的电源状态updatePowerStateLocked();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

整个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();
...........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

当申请发送到PMS后,PMS将针对WakeLock的level和flag信息进行一些处理。 
无论是acquire还是release WakeLock,PMS最终将利用updatePowerStateLocked函数对终端的电源状态进行调整。

我们将单独分析一下PMS核心的updatePowerStateLocked函数。

原文地址:http://blog.csdn.net/gaugamela/article/details/52813400

Android7.0 PowerManagerService(2) WakeLock的使用及流程相关推荐

  1. Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()...

    本篇注意接着上篇[Android7.0 PowerManagerService 之亮灭屏(一)]继续分析量灭屏的流程,这篇主要分析PMS的状态计算和更新流程,也是PMS中最为重要和复杂的一部分电源状态 ...

  2. Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)

    Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...

  3. Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)

    Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...

  4. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  5. Android7.0 PowerManagerService亮灭屏分析(一)

    绪论 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现类与功能如下所述: PowerManagerService.java:以 ...

  6. Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程

    前面的博客中,我们已经分析过,当Android中的进程要使用电量时,需要向PMS申请WakeLock:当进程完成工作后,需要释放对应的WakeLock.  PMS收到申请和释放WakeLock的请求后 ...

  7. Android 知识点 109 —— Android7.0 PowerManagerService 之亮灭屏

    原文地址: https://www.cnblogs.com/dyufei/p/8017604.html 写的太好了,粘过来! 本篇从按下power按键后,按键事件从InputManagerServic ...

  8. Android7.0 PowerManagerService亮灭屏分析(三)

    在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在DisplayPowerController中的animateScreenBri ...

  9. Android7.0 PowerManagerService亮灭屏分析(二)

    在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理.在亮屏过程中Disp ...

最新文章

  1. ssh开发实战之整合篇
  2. /dev、/sys/dev 和/sys/devices 和udev的关系
  3. 用计算机器提高正确率,计算机作业,正确率要90%
  4. python依照概率抽样_R语言之随机数与抽样模拟篇
  5. Element UI 在父类设置样式不起作用
  6. 第六章 图 学习小结
  7. 收录查询 php,google收录查询代码-PHP源码
  8. 万能电商Banner素材模板,一切产品为王
  9. 将多个文件绑在一起执行
  10. 2013应届毕业生“人人网”校招应聘总结
  11. Java NIO之缓冲区Buffer
  12. I2C 总线详解-转
  13. 【遗传编程/基因规划】python DEAP框架学习笔记
  14. 人脸检测FDDB测试ROC曲线生成
  15. matlab命令窗口双大于号不显示了 回车命令不管用了,怎么改才能恢复
  16. 一维数组实验题:大奖赛现场统分。已知某大奖赛有n个选手参赛,m(m>2)个评委为参赛选手评分(最高10分,最低0分)。统分规则为:在每个选手的m个得分中,去掉一个最高分和一个最低分后,取平均分作为该选
  17. tf.data.Dataset介绍1-from_tensor_slices
  18. 如何快速将小写字母转变为大写
  19. elementUI el-upload使用方法、上传限制数量且超出不显示上传按钮、删除闪一下、多个upload并排显示
  20. 11、合宙Air模块Luat开发:通过http协议获取天气信息

热门文章

  1. HUST1024 dance party(最大流)
  2. leetcode刷题实录:4
  3. XGBoost:Python下 安装
  4. 云炬随笔20211017(1)
  5. 起风了2019-12-26
  6. 有监督分类:集成分类(Bagging Boosting RandomForest)
  7. 串口通信模块1:串口基础知识
  8. SQLite-C语言实战
  9. 计算机网络第四章-网络层复习笔记
  10. Java中toString函数干嘛用?