Android 待机功能流程分析

来源:互联网 发布:python random.choice 编辑:程序博客网 时间:2024/04/30 11:08

Android智能手机或机顶盒子为了进入省电模式、所以就需要有待机功能。


PowerManager.java 类提供了如下的电源管理功能

public void goToSleep(long time) 强迫设备进入睡眠状态

public void reboot(String reason)  重启设备

提供了内部类: public final class WakeLock 

public void acquire(long timeout) 申请待机锁

        public void release(int flags)  释放待机锁


我这里讲的主要是:

利用Linux内核原有的睡眠唤醒模块上基础上,在Android系统上主要增加了下面三个机制:

     • Wake Lock 唤醒锁机制;
     • Early Suspend 预挂起机制;
     • Late Resume 迟唤醒机制;


基本原理:

当启动一个应用程序的时候,它可以申请一个wake_lock唤醒锁,每当申请成功之后都会在内核中注册一下(通知系统内核,现在已经有锁被申请,系统内核的wake_lock_store把它加入红黑树中),当应用程序在某种情况下释放wake_lock的时候,会注销之前所申请的wake_lock。特别要注意的是:只要是系统中有一个wake_lock的时候,系统此时都不能进行睡眠。但此时各个模块可以进行early_suspend。当系统中所有的wake_lock都被释放之后,系统就会进入真正的kernel的睡眠状态。在系统启动的时候会创建一个主唤醒锁main_wake_lock,该锁是内核初始化并持有的一个WAKE_LOCK_SUSPEND属性的非限时唤醒锁。因此,系统正常工作时,将始终因为该锁被内核持有而无法进入睡眠状态。也就是说在不添加新锁的情况下,只需将main_wake_lock 解锁,系统即可进入睡眠状态。


先看一下整体流程层次结构图:




下面按照层次结构从java层分析:

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

@public final class WakeLock  内部类

获取待机锁:        

public void acquire() {            synchronized (mToken) {                acquireLocked();            }        }        private void acquireLocked() {            if (!mRefCounted || mCount++ == 0) {                // Do this even if the wake lock is already thought to be held (mHeld == true)                // because non-reference counted wake locks are not always properly released.                // For example, the keyguard's wake lock might be forcibly released by the                // power manager without the keyguard knowing.  A subsequent call to acquire                // should immediately acquire the wake lock once again despite never having                // been explicitly released by the keyguard.                mHandler.removeCallbacks(mReleaser);                try {                    mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);                } catch (RemoteException e) {                }                mHeld = true;            }        }

 以上代码很清楚,判定标志mRefCounted 为false 或mCount 值为0即可进入获取待机锁、并将mCount加1 

 此时调用 mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);

 mService 就是 PowerManagerService 类


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

 @acquireWakeLock -> acquireWakeLockInternal ->updatePowerStateLocked 

重要的操作都在这里做的,五个步骤

private void updatePowerStateLocked() {        if (!mSystemReady || mDirty == 0) {            return;        }        // Phase 0: Basic state updates.        updateIsPoweredLocked(mDirty);        updateStayOnLocked(mDirty);        // Phase 1: Update wakefulness.        // Loop because the wake lock and user activity computations are influenced        // by changes in wakefulness.        final long now = SystemClock.uptimeMillis();        int dirtyPhase2 = 0;        for (;;) {            int dirtyPhase1 = mDirty;            dirtyPhase2 |= dirtyPhase1;            mDirty = 0;            updateWakeLockSummaryLocked(dirtyPhase1);            updateUserActivitySummaryLocked(now, dirtyPhase1);            if (!updateWakefulnessLocked(dirtyPhase1)) {                break;            }        }        // Phase 2: Update dreams and display power state.        updateDreamLocked(dirtyPhase2);        updateDisplayPowerStateLocked(dirtyPhase2);        // Phase 3: Send notifications, if needed.        if (mDisplayReady) {            sendPendingNotificationsLocked();        }        // Phase 4: Update suspend blocker.        // Because we might release the last suspend blocker here, we need to make sure        // we finished everything else first!        updateSuspendBlockerLocked();    }


    最后一个方法 updateSuspendBlockerLocked() 将会调用native方法

    @mWakeLockSuspendBlocker.acquire(); -> nativeAcquireSuspendBlocker

    private static native void nativeAcquireSuspendBlocker(String name);

    此时将进入jni 层:

    com_android_server_power_PowerManagerService.cpp

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

    实现相当简单、不解释了。

   hardware/libhardware_legacy/power/power.c 

int acquire_wake_lock(int lock, const char* id)  {    initialize_fds(); // 打开句柄 open("/sys/power/wake_lock")    if (g_error) return g_error;    int fd;    if (lock == PARTIAL_WAKE_LOCK) {        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];    }    else {        return EINVAL;    }    return write(fd, id, strlen(id)); // 写入命令}

此时将进入内核代码分析是如何做的。

kernel/power/main.c - PM subsystem core functionality.

static ssize_t wake_lock_store(struct kobject *kobj,       struct kobj_attribute *attr,       const char *buf, size_t n){int error = pm_wake_lock(buf);return error ? error : n;}

kernel/power/wakelock.c

int pm_wake_lock(const char *buf){const char *str = buf;struct wakelock *wl;u64 timeout_ns = 0;size_t len;int ret = 0;while (*str && !isspace(*str))str++;len = str - buf;if (!len)return -EINVAL;if (*str && *str != '\n') {/* Find out if there's a valid timeout string appended. */ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);if (ret)return -EINVAL;}mutex_lock(&wakelocks_lock);wl = wakelock_lookup_add(buf, len, true);if (IS_ERR(wl)) {ret = PTR_ERR(wl);goto out;}if (timeout_ns) {u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;do_div(timeout_ms, NSEC_PER_MSEC);__pm_wakeup_event(&wl->ws, timeout_ms);} else {__pm_stay_awake(&wl->ws);}wakelocks_lru_most_recent(wl); out:mutex_unlock(&wakelocks_lock);return ret;}


这块没有细看、有兴趣的朋友可以看看。本质就是利用红黑树查看锁的情况,最后该函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用 enter_state()来进入 linux的suspend流程。

机顶盒的资源也可以实现待机函数、这样子即可快速进入待机模式。


释放待机锁的流程与获取待机锁差不多就不多说了。


最后说明一下:

WakeLock 类型以及说明:

    PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
    SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
    SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
    FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
    ACQUIRE_CAUSES_WAKEUP:强制使屏幕亮起,这种锁主要针对一些必须通知用户的操作.
    ON_AFTER_RELEASE:当锁被释放时,保持屏幕亮起一段时间


最后 AndroidManifest.xml 声明权限:
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.DEVICE_POWER"/>


应用程序中如果要在待机前保存数据状态的话,要保证此过程中不会进入待机。可以在 onResume() 或者 onStart() 中申请 wakelock 锁,即调用acquireWakeLock()方法。

在 onPause() 或者 onDistroy() 中处理应用待机后再释放掉 wakelock 锁,此时调用releaseWakeLock()方法


最后一点需要注意下:

另外WakeLock的设置是 Activiy 级别的,不是针对整个Application应用的。所以application下有多个activity一定需要注意下!


1 0
原创粉丝点击