Android7.0 PowerManagerService(2) 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();
...........

当申请发送到PMS后,PMS将针对WakeLock的level和flag信息进行一些处理。

无论是acquire还是release WakeLock,PMS最终将利用updatePowerStateLocked函数对终端的电源状态进行调整。

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

时间: 2024-10-21 18:19:11

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

Android7.0 Doze模式分析(一)Doze介绍 &amp;amp; DeviceIdleController

 參考:http://blog.csdn.net/gaugamela/article/details/52981984 在Android M中,Google就引入了Doze模式.它定义了一种全新的.低能耗的状态. 在该状态,后台仅仅有部分任务被同意执行.其他任务都被强制停止. 在之前的博客中分析过Doze模式.就是device idle状态.可能有的地方分析的不是非常具体,如今在android7.0上又一次分析下. 一.基本原理 Doze模式能够简单概括为: 若推断用户在连续的一段时间内没有

Android7.0 Doze模式分析(一)Doze介绍 &amp; DeviceIdleController

 参考:http://blog.csdn.net/gaugamela/article/details/52981984 在Android M中,Google就引入了Doze模式.它定义了一种全新的.低能耗的状态. 在该状态,后台只有部分任务被允许运行,其它任务都被强制停止. 在之前的博客中分析过Doze模式,就是device idle状态.可能有的地方分析的不是很详细,现在在android7.0上重新分析下. 一.基本原理 Doze模式可以简单概括为: 若判断用户在连续的一段时间内没有使用手

[Android Pro] Android7.0系统 关于Android获取流量计数TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)返回-1解决方案

reference : http://blog.csdn.net/zhangyong7112/article/details/54574214 最近一个关于流量的项目在Android7.0系统的手机上运行,一直获取不到流量的使用数据,查看源码然后发现TrafficStats.getUidRxBytes(uid)和TrafficStats.getUidTxBytes(uid)一直都是返回的-1, // 获取某个网络UID接收和发送字节的总和 long total = TrafficStats.ge

WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码

上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们今天就来看看到底我们该如何理解Android系统中的Window和窗口. 窗口这个概念,从不同的角度来看它的含义不一样,如果我们从WmS(WindowManagerService)的角度来看窗口,那么这个窗口并不是一个Window类,而是一个View.用户发来的消息被WmS接收之后并不能直接发给各个

Android7.0新特性,及Android N适配

新特性部分 Android 7.0 Nougat 提供新功能以提升性能.生产效率和安全性,主要新增了以下的新特性和优化: 一.新的Notification Android N 增加了许多新的notifications API,进行了重新的设计,引入了新的风格. 模板更新: 开发者将能够充分利用新模板,只需进行少量的代码调整. 消息样式自定义: 新增自定义样式.消息回复.消息分组等更加灵活. 捆绑通知: 系统可以将消息组合在一起(例如,按消息主题)并显示组.用户可以适当地进行 Dismiss 或

Android7.0对dlopen的改变

两个内存段 在同一个进程空间中dlopen一个.so文件,理论上在内存中是同一片区域,但实际调试中发现Android7.0(read "/proc/self/maps")中,先后读同一个.so内存中居然出现两个段! 这在低版本Android(比如4.x)中不曾出现. 如下一些blog中分析,与Android7.0对dlopen的改写有关,可能是不同命名空间下读取结果不一样,可能是对安全性的提升. Android 7.0 行为变更 NDK 应用链接至平台库 Android 7.0 dlo

拍照、本地图片工具类(兼容至Android7.0)

拍照.本地图片工具类:解决了4.4以上剪裁会提示"找不到文件"和6.0动态授予权限,及7.0报FileUriExposedException异常问题. package com.hb.weex.util; import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.ClipData; import android.content.Conten

Android7.0下载Apk自动安装

Android7.0下载Apk自动安装 1. 整体需求 下载APK文件 使用DownloadManager来下载 在应用界面中展示下载进度 安装下载后的APK文件 root模式: 可以自动安装,不需要用户主动点击 正常模式: 弹出安装应用页面,需要兼容7.0以上版本 2. DownloadManager DownloadManager是Android提供的用于下载的类,使用起来比较简单,它包含两个静态内部类DownloadManager.Query和DownloadManager.Request

鹅厂bugly应用升级不能安装(Android7.0的新变化)

app升级是每一个android应用的标配了,大部分应用都会有升级提醒和apk下载安装(如果系统允许静默安装估计就没有提醒这段了). 以前的升级是自己写http下载或者通过系统提供的DownloadManager进行下载.无意间发现bugly提供下载更新服务而且免费(后来又提供了热更新),就乐呵呵的接入了sdk,毕竟是大厂的东西,值得信赖.开始用的时候用的很稳定(估计是手机厂商还没用上牛轧糖),后来就有人反应更新失败,一直提示安装,开始有些不相信直接让用户卸载重装(还好用户是公司自己人,是外人的