android休眠唤醒流程:

文章一:

android休眠唤醒流程:

power按键事件上报给android系统,最终由windownmanager接收到,当有按键事件时判断是否需要休眠后唤醒系统,然后调用powermanager系统服务去写/sys/power/state节点.
            此节点的写函数里判断收到的内容,来执行android的休眠early_suspend/唤醒late_resume流程.

android层:


    private int setScreenStateLocked(boolean on)                                         电源管理服务:    frameworks/base/services/java/com/android/server/PowerManagerService.java
         int err = Power.setScreenState(on);                                           
                                                                                        在文件frameworks/base/core/java/android/os/Power.java定义
                                                                                                79     public static native int setScreenState(boolean on);
                                                                                        具体实现在:frameworks/base/core/jni/android_os_Power.cpp
                                                                                                static int setScreenState(JNIEnv *env, jobject clazz, jboolean on);
                set_screen_state(on);                                                    在文件hardware/libhardware_legacy/power/power.c中定义并实现
                    write(g_fds[REQUEST_STATE], buf, len);                                写/sys/power/state节点

                   

内核层:

向节点写内容会调用kernel/power/main.c的写函数:
        static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)
            request_suspend_state(state);                                                调用此函数:    具体实现在./kernel/power/earlysuspend.c中
                state:    为休眠则调用early_suspend_work                                    android early_suspen工作队列
                        为唤醒则调用late_resume_work                                    android late_resume_work工作队列
                       
                                                                                        drivers/video/samsung/s3cfb_main.c中早已经注册屏幕开关
                                                                                            register_early_suspend(&fbdev[i]->early_suspend);
                                                                                        所以
                    为休眠则调用early_suspend_work中的屏幕休眠函数:
                            s3cfb_early_suspend()
                                backlight_on()---->s3cfb_backlight_on()                    在文件arch/arm/plat-s5p/dev-fimd-s5p.c中定义    npd->backlight_on = s3cfb_backlight_on;
                                        s3cfb_backlight_on中直接操作用来控制lcd的gpio关闭屏幕                                        arch/arm/mach-exynos/setup-fb-s5p.c
                                                   

                    为唤醒时调用late_resume_work中的屏幕唤醒函数:
                            s3cfb_late_resume()
                                backlight_off()---->s3cfb_backlight_off()                在文件arch/arm/plat-s5p/dev-fimd-s5p.c中定义    npd->backlight_off = s3cfb_backlight_off;
                                        s3cfb_backlight_off中直接操作用来控制lcd的gpio使能屏幕
               
                执行完所有的early_suspend后执行解锁main_wake_lock,以便休眠.
                wake_unlock(&main_wake_lock);
                    mod_timer(&expire_timer, jiffies + has_lock);                        在文件/kernel/power/wakelock.c中           
                                                                                                    此函数将启用expire_timer定时器,定时器内容即expire_wake_locks
                                                                                                    340 static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);

有四种方式可以引起休眠
        ①在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了, 就开始休眠
        ②在定时器到时间以后, 定时器的回调函数会查看是否有其他的wake lock, 如果没有, 就在这里让系统进入睡眠
        ③在wake_lock() 中, 对一个wake lock加锁以后, 会再次检查一下有没有锁,             刚加上锁,为什么要检查,有用吗????
        ④按power键,调用earlysuspend.使系统或应用程序释放锁.从而调用上述三个函数进入休眠
    if(has_lock ==0)
        queue_work(suspend_work_queue,&suspend_work);    由DECLARE_WORK(suspend_work, suspend);知道,队列中的内容即suspend函数.
            suspend();                                    kernel/power/wakelock.c
                pm_suspend(requested_suspend_state);
                    enter_state(state);                    kernel/power/suspend.c
                        suspend_devices_and_enter(state);
                            suspend_enter(state);
                                suspend_ops->enter(state);            -->调用平台相关的休眠函数,定义在中arch/arm/plat-samsung/pm.c:379:static const struct platform_suspend_ops s3c_pm_ops

                          即s3c_pm_enter

            s3c_pm_arch_stop_clocks()  -->               休眠时执行的的最后一个函数.系统停在此处,等待中断或rtc等唤醒源唤醒.

                               
                                从此处开始唤醒流程
                                enable_nonboot_cpus();
                            suspend_test_start();                    ---------->kernel已经被唤醒,当按键或中断来临后可以执行中断函数,上报唤醒事件.对于外部中断来说,上报power按键事件.
                        pm_restore_gfp_mask();            kernel/power/suspend.c
                    enter_state(state);
                 pm_suspend(suspend_state_t state);                                           
            suspend();                                    kernel/power/wakelock.c           

文章二:

   Android系统关机或重启的几种实现方式:

(1)发送广播方式

(2)通过init.rc启动系统服务来运行sh文件

(3)Runtime调用Linux-shell

(4)PowerManager reboot以及反射调用PowerManagerService shutdown

具体说明:

一. 发送广播方式

Broadcast是Android的四大基本组件之一,也就是我们常说的广播。Android系统本身就包含了许多广播,时时刻刻在监听着系统中注册的每一个广播并随时准备响应操作。其中,就有关于关机或重启的广播:Intent.ACTION_REQUEST_SHUTDOWN和Intent.ACTION_REBOOT,通过发送这两个广播,Android就能自动接收广播,并响应关机或重启的操作。ACTION_REQUEST和ACTION_REBOOT是Intent.java是声明的两个字符串常量

   public static final String ACTION_REBOOT =
              "android.intent.action.REBOOT";
   public static final String ACTION_REQUEST_SHUTDOWN = "android.intent.action.ACTION_REQUEST_SHUTDOWN";

Intent.java位于源码/frameworks/base/core/java/android/content/Intent.java下面。具体实现方法如下
//广播方式关机重启
			case R.id.shutdown_btn1:
				Log.v(TAG, "broadcast->shutdown");
                Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
                intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
                //其中false换成true,会弹出是否关机的确认窗口
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
				break;
			case R.id.reboot_btn1:
				Log.v(TAG, "broadcast->reboot");
                Intent intent2 = new Intent(Intent.ACTION_REBOOT);
                intent2.putExtra("nowait", 1);
                intent2.putExtra("interval", 1);
                intent2.putExtra("window", 0);
                sendBroadcast(intent2);  
				break;

需要注意的几点是:

第一,如前面所说,需要将APP提升至系统权限,具体做法是在AndroidMenifest.xml中添加如下代码

android:sharedUserId="android.uid.system"

第二,同时需要添加关机权限
第三,在Eclipse中,代码中的Intent.ACTION_REQUEST_SHUTDOWN 及 Intent.EXTRA_KEY_CONFIRM 在Eclipse IDE中报错,还是和前面说的一样,这两个属性不对上层开放,如果把项目放在源码中进行编译,是可以编译通过的。

第四,由于需要在源码中编译项目,所以需要为项目编写mk文件,在项目根目录下添加Android.mk文件,内容如下所示:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := PowerActionDemo
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE) 
最后,将编译生成的apk文件,通过adb push到机器上就可以验证功能了。


二. 通过init.rc启动系统服务来运行sh文件

Android启动文件系统后调用的会调用第一个应用程序是/init,此文件一个很重要的内容就是解析了init.rc和init.xxx.rc,然后执行解析出来的任务。而init.rc,可以在系统的初始化过程中进行一些简单的初始化操作。利用这一点,可以编写简单的关机或重启的sh脚本文件,通过系统init解析,执行相应的关机或重启操作。

1.首先,编写关机和重启的sh脚本。比如,新建

重启脚本 system_reboot.sh,内容如下:

#!/system/bin/sh  
reboot
关机脚本 system_shutdown.sh
#!/system/bin/sh  
reboot -p

注意:此处关机命令并不是shutdown,而是reboot -p

2. 编写Android.mk编译脚本,目的是在源码编译的时候,将这两个sh文件一起编译到/system/bin目录下

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_PREBUILT_EXECUTABLES := system_shutdown.sh system_reboot.sh
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

3. init.rc添加关机和重启的服务,打开init.rc文件,在最后面添加如下内容:
service system_shutdown /system/bin/system_shutdown.sh
        oneshot
        disabled 

service system_reboot /system/bin/system_reboot.sh
        oneshot
        disabled

oneshot选项表示该服务只启动一次,而如果没有oneshot选项,这个可执行程序会一直存在--如果可执行程序被杀死,则会重新启动。

disabled 表示禁用服务,此服务开机时不会自动启动,但是可以在应用程序中手动启动它。


4.新建一个目录,比如poweraction, 将以上的Android.mk , system_shutdown.sh, system_reboot.sh放在这个目录下,然后将poweraction这个目录拷贝到Android系统中,比如device路径下面。然后,编译Android源码,源码编译完成后, 查看生成的out/.../system/bin下面是不是包含system_shutdown.sh, system_reboot.sh两个sh文件,如果有,则说明编译成功。

5.最后,启动系统服务,进行关机或重启。

//启动系统服务进行关机或重启
			case R.id.shutdown_btn2:
				Log.v(TAG, "system service->shutdown");
				SystemProperties.set("ctl.start", "system_shutdwon");
				break;
			case R.id.reboot_btn2:
				Log.v(TAG, "system service->reboot");
				SystemProperties.set("ctl.start", "system_reboot");
				break; 

三. Runtime调用Linux-shell

我们知道,Runtime这个Java类是可以用来调用并执行shell命令的,而Android虚拟机是支持Linux-shell语言的,基于这一点,可以利用Runtime来执行 关机或重启的shell命令,这一点和上面介绍的方式二原理上大致相同。功能代码如下:

//Runtime执行linux-shell
			case R.id.shutdown_btn3:
				try{
					Log.v(TAG, "root Runtime->shutdown");
					//Process proc =Runtime.getRuntime().exec(new String[]{"su","-c","shutdown"});  //关机
					Process proc =Runtime.getRuntime().exec(new String[]{"su","-c","reboot -p"});  //关机
				    proc.waitFor();
				}catch(Exception e){
				    e.printStackTrace();
				}
				break;
			case R.id.reboot_btn3:
				try { 
					Log.v(TAG, "root Runtime->reboot");
					Process proc =Runtime.getRuntime().exec(new String[]{"su","-c","reboot "});  //关机
				    proc.waitFor();
				}catch (Exception ex){
					ex.printStackTrace();
				}
				break; 

使用该方法需要注意的是,普通用户是没有权限执行reboot和shutdown的,自然而然也无法实现关机或重启。使用的Android设备必须已经root过,上面的代码加上su命令其实也就是为了获取管理员权限。另外一点,需要注意的是,该方法能够奏效的前提是,你的android系统system/bin 目录下存在reboot和shutdown文件(其实跟上面的原理一样,也是调用bin目录下的文件),听说大部分设备存在reboot和shutdown这两个文件,可使用的Android系统偏偏没有shutdown文件,所以,无法直接使用
Runtime.getRuntime().exec(new String[]{"su","-c","shutdown"})

只能执行下面命令来进行关机(好神奇的p参数)
Runtime.getRuntime().exec(new String[]{"su","-c","reboot -p"});

四 . PowerManager reboot以及反射调用PowerManagerService shutdown

1. PowerManager提供了reboot等接口,因此,利用PowerManager实现重启,就比较简单。

PowerManager pManager=(PowerManager) getSystemService(Context.POWER_SERVICE);  //重启到fastboot模式
				pManager.reboot(""); 

2. PowerManager类并没有提供关机的shutdown接口,而是通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。PowerManagerService是PowerManager 类中定义的接口的具体实现,并进一步调用Power 类来与下一层进行通信. 在PowerManagerService实现了shutdown接口,power服务实现了关机功能
PowerManager的实现通过IPowerManager来调用Power服务的接口。 IPowerManager是AIDL文件自动生成的类,便于远程通信。IPowerManage.aidl文件目录
framework/base/core/java/android/os/IPowerManage.aidl 

IPowerManager实现了shutdown接口,所以,如果我们能够获得Power服务的IBinder,通过反射调用shutdown方法就能实现关机功能。
需要注意的是,ServiceManager管理着系统的服务程序,它保存着所有服务的IBinder,通过服务名就能获取到这个服务的IBinder。
但ServiceManager这个类也是HIDE的,也需要反射进行调用。两次,通过两次反射调用,就能调用power服务实现的关机功能。
 try {
		                 
		                 //获得ServiceManager类
		                 Class> ServiceManager = Class
		                    .forName("android.os.ServiceManager");
		                 
		                 //获得ServiceManager的getService方法
		                 Method getService = ServiceManager.getMethod("getService", java.lang.String.class);
		                 
		                 //调用getService获取RemoteService
		                 Object oRemoteService = getService.invoke(null,Context.POWER_SERVICE);
		                 
		                 //获得IPowerManager.Stub类
		                 Class> cStub = Class
		                    .forName("android.os.IPowerManager$Stub");
		                 //获得asInterface方法
		                 Method asInterface = cStub.getMethod("asInterface", android.os.IBinder.class);
		                 //调用asInterface方法获取IPowerManager对象
		                 Object oIPowerManager = asInterface.invoke(null, oRemoteService);
		                 //获得shutdown()方法
		                 Method shutdown = oIPowerManager.getClass().getMethod("shutdown",boolean.class,boolean.class);
		                 //调用shutdown()方法
		                 shutdown.invoke(oIPowerManager,false,true);           
		           
		       } catch (Exception e) {         
		            Log.e(TAG, e.toString(), e);        
		       }

文章三:

Android 6.0变化之休眠模式


休眠和App 待机
休眠:
Android 6.0最大变化之一就是加入了新的电量管理模式:休眠模式,当设备一段时间不用的时候,当屏幕关闭的时候,系统会自动进入休眠模式。这样所有的App都将进入挂起模式,不能在接入 网络等一些操作。
当然系统也会定期的退出休眠模式,来完成App延迟的工作,在这个空窗期(我暂且就这么叫),系统会运行所有同步,工作,提醒等,并允许app接入网络。
当过了空窗期后,系统会重新进入休眠期,App也会随着挂起状态,随着时间的推移,空窗期越来越少,是为了帮助没有接入充电器的长期闲置的设备减少电池消耗。
只要用户唤醒设备,打开屏幕或者接入电源,系统会自动退出休眠模式,所有的活动都会恢复正常状态。
下面是当进入休眠期时的约束:
1. 延迟网络请求;
2. 系统忽略唤醒锁;
3. 标准的闹钟提醒(包括setExat()和setWindow())会被延时到下一个空窗期;
如果一定要在休眠期唤醒闹钟,可以用setAndAllowWhileIdle() 或者 setExactAndAllowWhileIdle();
闹钟设置setAlarmClock() 则继续保持常态,在唤醒这个闹钟前系统推出休眠期一段时间;
4. 禁用wifi scan;
5. 不允许同步;
6. 禁用JobScheduler ;
休眠容易影响 AlarmManager alarms and timers manage,因为当系统进入休眠状态,闹钟在android5.1(API level 22)或者更低不会唤醒 ;
为了帮助管理alarms,android 6.0(API level 23) 介绍了两个方法: setAndAllowWhileIdle()和setExactAndAllowWhileIdle(). 这样即使你再休眠期的时候 闹钟也会被唤醒;
PS: 即使这两种方法,每个App每15分钟唤醒次数也不能超过一次;
有了休眠,自然会影响我们的后台服务,比如 推送,google 建议是用GMC( Google Cloud Messaging)。
App待机:
app Standby 允许系统决定一个app是否是空闲,当用户不怎么用的时候;系统是通过用户一段时间内是否够触摸这个app来决定的,以下的几点是不在考虑范围的:
1、用户退出了App;
2、app有一个前台进程;
3、App生成一个用户能够在锁屏或通知栏上看到的通知;
当用户接入电源的时候,系统会释放待机状态,自动接入网络,完成延迟工作。如果设备闲置很长一段时间,系统会大约一天允许连接一次网络。
google建议使用GMC,可以省电。GMC的优先级要高于休眠模式和待机模式的,所以当处于休眠模式和待机模式的时候 是不会影响推送的。可以通过这个唤醒你的App,短时间内接通网络,然后继续回到休眠模式。并且不会影响其他app的待机模式。用GMC的高优先级消息。
PS:当然,为了更好地兼容,google也提供了一个白名单,可以设置某些应用不会进入休眠和待机状态。这些应用依然可以正常访问网络。不过也不是什么都可以做,一些同步或其他工作还是不行的。
调用这个方法,即可查看是否加入白名单:isIgnoringBatteryOptimizations()
可以通过一些参数:
ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS、REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 主动请求系统加入到白名单中。
PS : 详情文档:http://developer.android.com/intl/zh-cn/training/monitoring-device-state/doze-standby.html
不会所有的6.0系统的手机,都有这个毛病吧?

文章四:

linux下休眠/待机命令

if you
# cat /sys/power/state
mem disk
you can
echo “mem” > /sys/power/state 这相当于待机
echo “disk” > /sys/power/state 这相当于休眠

from http://Linux.chinaunix.NET/bbs/viewthread.PHP?tid=1057578

命令行中执行如下:

[root@fsc feng]# file /sys/power/state
/sys/power/state: ASCII text
[root@fsc feng]# cat /sys/power/state
standby mem disk
[root@fsc feng]# echo “mem”>/sys/power/state
/sys/power/state是个文本文档,一个”mem”的导向就能使系统挂起到内存(待机)或硬盘(休眠),实在太神奇了,一定要深入学习了解一下其中原因!

—————————————–

关于Linux操作系统睡眠和休眠

from http://www.xxlinux.com/linux/article/accidence/technique/20080303/14073.html

在作之前,先检查一下你的内核能支持哪些方式:

# cat /sys/power/state

standby disk

在实际项目中我执行后的结果为:

# cat /sys/power/state

freeze mem

1. 睡眠 (sleep)

睡眠可能有两种方式:mem和standby,这两种方式都是suspend to RAM,简称STR,只是standby耗电更多一些,返回到正常工作方式时间更短一些而已。

只需要

# echo standby > /sys/power/state

就可以了。

2. 休眠 (hibernation)

休眠也有两种方式:shutdown和platform。shutdown是通常的方式,比较可靠一些。如果你的系统上ACPI支持非常好,那就有机会支持platform方式。激活的方式稍有不同:

# echo platform > /sys/power/disk; echo disk > /sys/power/state

or

# echo shutdown > /sys/power/disk; echo disk > /sys/power/state

注意休眠有一个前提,就是在系统启动时需要指定resume设备,也就是休眠的镜像需要保存的分区。一般都用swap分区来做。

指定方式是:

kernel /boot/vmlinuz root=/dev/sda1 resume=/dev/sda2 vga=0×314 …

这样在系统启动时,内核会检查resume中的内容,如果存在上次休眠的镜像,那内核便会将镜像读入内存,恢复正常工作状态。



文章五

系统挂起(Suspend)是电源管理(APM&ACPI)的一个特性,给用户带来了很大的方便。Linux在2.6系列核心中对电源管理有了较好的支持,下面就谈谈linux对系统挂起的支持情况。

Linux对系统挂起的支持

  Linux同时提供了对APM和ACPI的支持,当时两者是不兼容的,同一时刻只能有一种机制工作。由于ACPI的优越性,所以现在Linux将ACPI设为缺省的电源管理方案。对于一些比较旧的主板,如果其BIOS中ACPI的实现在2000年以前,那么Linux自动启用APM(可以通过核心命令行参数acpi=force来强制启用ACPI)。如果你下主板BIOS中对ACPI的支持有些问题导致Linux工作不正常,那么还可以使用核心命令行参数acpi=off来强制禁用ACPI,这样Linux会自动启用APM电源管理。

Linux现在主要支持三种ACPI的节电方式:

S1:Stopgrant,即待机(standby)模式。显示屏自动断电,只是主机通电。这时敲任意键即可恢复原来状态。待机模式

S2 S3:STR(Suspend To Ram),即挂起到内存。系统把当前信息储存在内存中,只有内存等几个关键部件通电,这时计算机处在高度节电状态。此时系统不能从键盘唤醒。手工唤醒的方法只能是按前面板上的电源按钮。唤醒后,计算机从内存中读取信息很快恢复到原来状态。

睡眠可能有两种方式:mem和standby,这两种方式都是suspend to RAM,简称STR,只是standby耗电更多一些,返回到正常工作方式时间更短一些而已。

S4:STD(Suspend To Disk),即挂起到硬盘,也即休眠。计算机自动关机,关机前将当前数据存储在硬盘上,用户下次按开关键开机时计算机将无须启动操作系统,直接从硬盘读取数据,恢复原来状态。休眠模式

在Linux下查看核心支持ACPI情况的方法如下:

2.4核心下:

# cat /proc/acpi/sleep

S0 S1 S3 S4 S5

2.6核心下:

# cat /sys/power/state

standby mem disk

在实际项目中我执行后的结果为:

# cat /sys/power/state

freeze mem

上面的输出可知,我们系统中核心同时支持三种节电模式。

在/sys/power目录下还有一个文件:disk,文件的内容可以如下:

shutdown: 将系统状态保存到磁盘,让BIOS关闭计算机;

platform: 将系统状态保存到磁盘,让BIOS关闭计算机,并且点亮挂起指示灯;

firmware: 让BIOS自己将系统状态保存,并且关闭计算机,需要BIOS自己有挂起磁盘。大部分工作都由BIOS完成,对操作系统是透明的;

进入这三种节电模式的方法如下:

#echo standby > /sys/power/state ---->挂起(S1)

#echo mem > /sys/power/state ---->挂起到内存(S3)

#echo shutdown > /sys/power/disk; echo disk > /sys/power/state ---->挂起到磁盘(S4)#echo platform > /sys/power/disk; echo disk > /sys/power/state

Linux下的磁盘挂起(STD)是通过swsusp机制实现的:将系统当前状态保存的内存后,再把内存内容写入交换分区(swap)。这里要求交换分区容量最好大于内存容量。系统挂起到磁盘后,下次启动的时候需要向核心传递命令行参数resume=/dev/hdaX(/dev/hdaX是系统中的交换分区),这样系统就能够很快恢复到关机时的状态。

还有一个非正式的核心补丁可以实现STD:Software Suspend 2。该项目是一个快速发展的项目,设计上教swsusp有一些优势,但是还没有集成到核心正式发布中,实现方式与swsusp基本相同。

虽然Linux提供了系统挂起的机制,但是执行上面的挂起操作不一定能够成功一方面,这些操作除了需要BIOS支持以外,还需要外围硬件设备能够兼容,即设备支持节电状态,支持从节电状态或断电状态恢复;另一方面,这些设备驱动必须能够接收电源管理指令。目前,系统挂起的主要障碍就是那些还不太完善的驱动程序,如USB、显卡、声卡驱动等。

当然,现在Linux核心对系统挂起的支持还有待改进,主要表现在:

不支持SMP系统。

不支持大内存(>4G)。

核心中许多模块需要增加电源管理的支持。

缺少上层配置程序。

   不过以放心,所有的问题内核黑客们都能够解决!

另外,可以看看一下文档:

Linux电源管理_Generic PowerManager 之Suspend功能--(一)

蜗窝科技,www.wowotech.net。中:

Linux电源管理(6)_Generic PM之Suspend功能

http://www.wowotech.net/linux_kenrel/suspend_and_resume.html

Linux电源管理(10)_autosleep

http://www.tuicool.com/articles/MvUf6v

Linux电源管理(5)_Hibernate和Sleep功能介绍

http://www.wowotech.net/linux_kenrel/std_str_func.html





你可能感兴趣的:(linux驱动开发)