Linux内核中wakelock的实现

//kernel/kernel/power/Makefileccflags-$(CONFIG_PM_DEBUG) := -DDEBUGobj-y               += qos.oobj-$(CONFIG_PM)       += main.oobj-$(CONFIG_VT_CONSOLE_SLEEP) += console.o //??obj-$(CONFIG_FREEZER)      += process.oobj-$(CONFIG_SUSPEND)      += suspend.oobj-$(CONFIG_PM_TEST_SUSPEND)  += suspend_test.oobj-$(CONFIG_HIBERNATION)  += hibernate.o snapshot.o swap.o user.o \                   block_io.o //这个目前没有定义,所以PM_HIBERNATION_PREPARE等还没有!!obj-$(CONFIG_PM_AUTOSLEEP) += autosleep.oobj-$(CONFIG_PM_WAKELOCKS) += wakelock.oobj-$(CONFIG_SUSPEND_TIME) += suspend_time.oobj-$(CONFIG_MAGIC_SYSRQ)  += poweroff.oobj-$(CONFIG_SUSPEND)  += wakeup_reason.o//kernel/driver/power/Makefileobj-$(CONFIG_PM)   += sysfs.o generic_ops.o common.o qos.oobj-$(CONFIG_PM_SLEEP) += main.o wakeup.oobj-$(CONFIG_PM_RUNTIME)   += runtime.o //obj-$(CONFIG_PM_TRACE_RTC) += trace.oobj-$(CONFIG_PM_OPP)   += opp.oobj-$(CONFIG_PM_GENERIC_DOMAINS)   +=  domain.o domain_governor.oobj-$(CONFIG_HAVE_CLK) += clock_ops.occflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG

Linux wake_lock的实现

1) 内核中调用wake_lock函数,其定义在wakelock.h文件中

static inline void wake_lock(struct wake_lock *lock){    __pm_stay_awake(&lock->ws);}

CONFIG_PM_WAKELOCKS使能之后,会向上层提供wake_lock节点

#ifdef CONFIG_PM_WAKELOCKSstatic ssize_t wake_lock_show(struct kobject *kobj,                  struct kobj_attribute *attr,                  char *buf){    return pm_show_wakelocks(buf, true);}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); //这个函数也是调用上面提供的__pm_stay_awake()函数!    return error ? error : n;}power_attr(wake_lock);static ssize_t wake_unlock_show(struct kobject *kobj,                struct kobj_attribute *attr,                char *buf){    return pm_show_wakelocks(buf, false);}static ssize_t wake_unlock_store(struct kobject *kobj,                 struct kobj_attribute *attr,                 const char *buf, size_t n){    int error = pm_wake_unlock(buf);    return error ? error : n;}power_attr(wake_unlock);

从上面的代码可以看到,wake_lock就是调用__pm_stay_awake()函数来完成阻止系统进入睡眠的功能。
__pm_stay_awake() 怎么防止系统进入syspend状态的呢??这些都是通过wakeup_source来完成的。
如果某个驱动想要阻止,允许进入睡眠,可以通过wakeup_source_init(struct wakeup_source *ws,const char *name)函数初始化一个wake source并调用__pm_stay_awake() ,或者对应地使用 __pm_relax()来阻止/允许进入睡眠。
这个wake_source在suspend流程中可以看到,在try_to_suspend()函数中会调用check wakeup_sources()来检查wake_source并决定是否进入pm_suspend()。

Linux进入suspend状态的路径

上面说wake_lock怎么去阻止系统进入suspend状态的,还需要解释一下Linux进入syspend状态的流程。
1)一个是/sys/power/state中写入mem或者on等进行suspend或者唤醒(/kernel/kernel/power/main.c)
这个过程需要再分析PowerManagerService(??)
2) 从/kernel/kernel/power/autosleep.c文件中的try_to_suspend()函数,这个也是PowerManagerService
从/sys/power/autosleep发来的(??)。

root@xxx:/sys/power # lslsautosleepcpufreq_max_limitcpufreq_min_limitcpufreq_tablepm_asyncpm_freeze_timeoutstatewake_lockwake_unlockwakeup_count

当然这些过程都会检查wake source是否全是deactive状态。wakesource有active和deactive两种状态,当/sys/power/autosleep或者/sys/power/state写入mem等准备进入suspend的时候都会去检查wakesource链表中的所有wake source是否都处于deactive状态,是的话才会进入suspend状态。wake lock的实现也是封装了wake source实现的,用红黑树和LRU管理。wake source会在300秒之后被从wake lock的链表中移除(??)

Android系统中获取释放wake_lock

Android中wakelock的获取像上图所示。获取wake lock的代码如下:

1) 获取wake lock

final PowerManager powerManager = (PowerManager) context.getSystemService(               Context.POWER_SERVICE);mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);...mWakeLock.acquire();mWakeLock.setReferenceCounted(false); ////使用非计时Wakelock...

2) 释放wake lock
mWakeLock.release();

上面的PARTIAL_WAKE_LOCK似乎和内核没有什么联系,具体怎么实现的以后再看?????????。

下面是几种上层实现的锁的解释!!

newWakeLock(int flags, String tag);//取得相应层次的锁 flags参数说明:PARTIAL_WAKE_LOCK: Screen off, keyboard light off//保持CPU运转,允许屏幕和键盘灯有可能是关闭的SCREEN_DIM_WAKE_LOCK: screen dim, keyboard light off//保持CPU运转,保持屏幕显示但有可能是灰的,允许关闭键盘灯SCREEN_BRIGHT_WAKE_LOCK: screen bright, keyboard light off//保持CPU运转,保持屏幕高亮显示,允许关闭键盘灯FULL_WAKE_LOCK: screen bright, keyboard bright//保持CPU运转,保持屏幕高亮显示,键盘灯也保持亮度ACQUIRE_CAUSES_WAKEUP: 一旦有请求锁时强制打开Screen和keyboard lightON_AFTER_RELEASE: 在释放锁时reset activity timerNote:如果申请了partial wakelock,那么即使按Power键,系统也不会进Sleep,如Music播放时如果申请了其它的wakelocks,按Power键,系统还是会进Sleep如果Screen off timer时间到并且没有Full wake lock或者用户按了power key,那么系统状态将被切换到NOTIFICATION,并且调用所有已经注册的g_early_suspend_handlers函数, 通常会把LCD和Backlight驱动注册成early suspend类型,如有需要也可以把别的驱动注册成early suspend, 这样就会在第一阶段被关闭。接下来系统会判断是否有partial wake lock acquired, 如果有则等待其释放, 在等待的过程中如果有user activity事件发生,系统则马上回到AWAKE状态;如果没有partial wake lock acquired, 则再释放main_wake_lock系统开机时定义的内核态唤醒锁,然后系统会马上调用函数pm_suspend关闭其它相关的驱动, 让CPU进入休眠状态(进入Linux标准的内核休眠).

3) 权限获取
要进行电源的操作需要在AndroidManifest.xml中声明该应用有设置电源管理的权限。

<uses-permissionandroid:name="android.permission.WAKE_LOCK"/>你可能还需要<uses-permission android:name="android.permission.DEVICE_POWER"/>

以下是上面的acquire函数是怎么一步步调用到内核的wake_lock节点并获取wake_lock的步骤

mWakeLock.acquire()->PowerManager.acquireLocked()->PowerManagerServer.acquireWakeLock()->PowerManagerServer.acquireWakeLockInternal()->PowerManagerServer.updatePowerStateLocked()->PowerManagerServer.updateSuspendBlockerLocked()->PowerManagerServer.java文件中的acquire()->nativeAcquireSuspendBlocker()函数

Linux, Android电源管理:wakelock,autosleep_第1张图片

然后最终调用到android/hardware/libhardware_legacy/power/power.c文件中的
acquire_wake_lock()函数

int acquire_wake_lock(int lock, const char* id){    initialize_fds();// ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);    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));}//以下是打开Linux内核向上层提供的节点的函数,可以看到打开的节点就是/* const char * const NEW_PATHS[] = { "/sys/power/wake_lock", "/sys/power/wake_unlock", }; */static inline void initialize_fds(void){    // XXX: should be this:    //pthread_once(&g_initialized, open_file_descriptors);    // XXX: not this:    if (g_initialized == 0) {        if(open_file_descriptors(NEW_PATHS) < 0)            open_file_descriptors(OLD_PATHS);        g_initialized = 1;    }}

参考:http://www.wowotech.net/linux_kenrel/wakeup_events_framework.html

Linux,Android autosleep

Linux内核版本3.5和Android版本4.3开始已经不再使用Early suspend。而是都换成了autosleep。
kernel断CONFIG_HAS_EARLYSUSPEND也被disable掉换成了CONFIG_PM_AUTOSLEEP。
内核的实现和上面说的wakelock等都很像,都是/kernel/kernel/power/目录下的,只是有一个叫autosleep.c的文件负责创建autosleept节点给上层。

autosleep替换early suspend之后。之前使用early suspend的设备(比如TSP),现在都只能使用autosleep机制来是先了,已经不在内核注册early suspend等等,这部分被从内核移除。
之前early suspend上的resume和suspend函数被input_dev->open和input_dev->close()所取代。调用这些内容的也从内核去掉,移到上册HAL里边。但一直没有找到在哪里调用input_dev->open和input_dev->close()。比如关掉屏幕等操作的时候,是有DisplayManagerService.java文件中的BlankUnblankInSeperateThread进程在进行input_dev->open和input_dev->close()的调用,完成类似之前early suspend的功能的。

input_dev->open()和input_dev->close都是通过input_open_device()和input_close_device()调用下来。
而input_open_device和input_close_device()就是往/sys/class/input/event*/device/enabled节点里边写1或者写0来完成的。当然,需要睡眠的输入输出设备是在InputReader.cpp文件的addDeviceLocked()函数中加的。

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);    const char* deviceName = identifier.name.string();    //比如三星的设备,其触摸屏或者pen都是起来下面的名字。看到这些名字的输入输出设备就调用如下    //notifySuspendDevice()函数使能这个设备!!    if (strstr(deviceName, "sec_touchkey") || strstr(deviceName, "sec_touchscreen") ||                             strstr(deviceName, "sec_e-pen")) {        const char* path = mEventHub->getDevicePath(deviceId);        if (path != NULL) {            ALOGD("reporting suspendable device %s as number %s", deviceName, path + 16);            mPolicy->notifySuspendDevice(path + 16, deviceName);                 // 16 character should be excluded ("/dev/input/event")        }    }    ...}
//com_android_input_InputManagerService.cppvoid NativeInputManager::notifySuspendDevice(const char* number, const char* deviceName) {    android_server_PowerManagerService_addDevice(number, deviceName);    //[@input_fw disable suspendible device in case of sidesync    String8 path;    path.append("/sys/class/input/event");    path.append(number);    path.append("/device/enabled");    String8 name;    name.append(deviceName);    JNIEnv* env = jniEnv();    ScopedLocalRef<jstring> pathObj(env, env->NewStringUTF(path.string()));    ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(name.string()));    env->CallVoidMethod(mServiceObj,         gServiceClassInfo.getSuspendibleDevices, pathObj.get(), nameObj.get());    checkAndClearExceptionFromCallback(env, "getSuspendibleDevices");    //]}//上面的函数读/sys/class/input/event*/device/enabled文件并调用InputManagerService.java中的//getSuspendibleDevices()函数保存在mSuspendibleDevices中。//setSuspendibleDevices()函数就是mSuspendibleDevices中读取可以睡眠的输入输出设备,设置往其设备节点中的//enable节点写值,就会调用input_dev->open函数或者input_dev->close。//[@input_fw disable suspendible device in case of sidesyncprivate void getSuspendibleDevices(String path, String name) {    mSuspendibleDevices.put(name, path);    Log.d(TAG, "get suspendable device " + name + " as path " + path);}private boolean setSuspendibleDevices(String device, boolean enable) {    if (mSuspendibleDevices.isEmpty()) {        Log.d(TAG, "Not exist SuspendibleDevices");        return false;    }    if (device.equals("all")) {        String[] targetdevices = new String[mSuspendibleDevices.size()];        mSuspendibleDevices.keySet().toArray(targetdevices);        for(String targetdevice:targetdevices) {            sysfsWrite(mSuspendibleDevices.get(targetdevice), (enable ? 1 : 0));            Log.d(TAG, targetdevice + " is " + (enable ? "enabled" : "disabled"));        }    return true;    } else if (mSuspendibleDevices.containsKey(device)) {        sysfsWrite(mSuspendibleDevices.get(device), (enable ? 1 : 0));        Log.d(TAG, device + " is " + (enable ? "enabled" : "disabled"));        return true;    }    return false;}

关于Early suspend:
http://blog.csdn.net/droidphone/article/details/6642081

http://blog.csdn.net/wlwl0071986/article/details/9746135

http://blog.csdn.net/wlsfling/article/details/46005409

http://blog.csdn.net/u010681466/article/details/19897851

设置背光LED灯

frameworks/base/services/core/java/com/android/server/lights/LightsService.java,
frameworks/base/services/core/java/com/android/server/lights/Light.java

DisplayPowerState.java等需要管理背光和LED等的地方,都是调用setBrightness()函数来实现。
setBrightness()函数实现在LightsService.java文件中。调用顺序如下:

1. setBrightness()[frameworks/base/services/core/java/com/android/server/lights/LightsService.java]2. setLight_native()[frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp]3.set_light()[android/hardware/qcom/display/liblight/Lights.c](高通)Lights.c文件中定义了所有LED灯的路径,有相关的驱动变更需要查看相关驱动的路径等~char const*const RED_LED_FILE        = "/sys/class/leds/red/brightness";char const*const GREEN_LED_FILE        = "/sys/class/leds/green/brightness";char const*const BLUE_LED_FILE        = "/sys/class/leds/blue/brightness";char const*const LCD_FILE        = "/sys/class/leds/lcd-backlight/brightness";char const*const BUTTON_FILE        = "/sys/class/leds/button-backlight/brightness";char const*const RED_BLINK_FILE        = "/sys/class/leds/red/blink";char const*const GREEN_BLINK_FILE        = "/sys/class/leds/green/blink";char const*const BLUE_BLINK_FILE        = "/sys/class/leds/blue/blink";

更多相关文章

  1. Android VideoView状态跟踪和错误处理
  2. Android 判断屏幕开关状态方式总结
  3. Android 获取手机网络状态
  4. Android获取屏幕状态的方式
  5. 记录状态栏与布局重合,状态栏颜色问题
  6. Android沉浸式状态栏实现
  7. android生命周期函数大全——亲测
  8. android 3g状态及信号监测
  9. Android获取状态栏、标题栏、ActionBar以及屏幕的高度

随机推荐

  1. Android(安卓)listview子控件的的点击事
  2. 自定义Android(安卓)Studio方法注释模板
  3. Android(安卓)Cordova 插件开发之创建项
  4. Android(安卓)ApiDemos示例解析(97):View
  5. android不一样的listView的empty
  6. 【Android(安卓)Studio】ParseError at [
  7. Android(安卓)Selinux 权限处理
  8. Android单元测试及日志输出
  9. 安卓第七天笔记--网络编程一
  10. 转:android下拉列表框 spinner