Linux, Android电源管理:wakelock,autosleep
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()函数
然后最终调用到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";
更多相关文章
- Android VideoView状态跟踪和错误处理
- Android 判断屏幕开关状态方式总结
- Android 获取手机网络状态
- Android获取屏幕状态的方式
- 记录状态栏与布局重合,状态栏颜色问题
- Android沉浸式状态栏实现
- android生命周期函数大全——亲测
- android 3g状态及信号监测
- Android获取状态栏、标题栏、ActionBar以及屏幕的高度