2011年10月1日 星期六

Android在標準linux基礎上對休眠喚醒的實現(三)(轉貼)


四、android层源码解析
linux之上经过android的软件堆层层封装,最终在上层的java应用程序中使用。休眠唤醒也是从最上层发出的命令,然后一层一层地将参数解析,往最底层传,最后走上标准linux的休眠唤醒之路。
这一部分将会初略分析休眠唤醒机制上linux之上所走的路线。
linux之上,存在一个hal层,专门做和linux内核设备打交道的事情,这里也不例外。休眠唤醒机制的hal层源码位于:@hardware/libhardware_legacy/power/power.c
该文件源码比较简单,下面列举重点片段:
enum {
ACQUIRE_PARTIAL_WAKE_LOCK = 0,
RELEASE_WAKE_LOCK,
REQUEST_STATE,
OUR_FD_COUNT
};
const char * const NEW_PATHS[] = {
"/sys/power/wake_lock",
"/sys/power/wake_unlock",
"/sys/power/state"
};
static int g_initialized = 0;
static int g_fds[OUR_FD_COUNT];
static const char *off_state = "mem";
static const char *on_state = "on";
static int open_file_descriptors(const char * const paths[])
{
int i;
for (i=0; i<OUR_FD_COUNT; i++) {
int fd = open(paths[i], O_RDWR);
if (fd < 0) {
fprintf(stderr, "fatal error opening \"%s\"\n", paths[i]);
g_error = errno;
return -1;
}
g_fds[i] = fd;
}
g_error = 0;
return 0;
}
static inline void initialize_fds(void)
{
if (g_initialized == 0) {
if(open_file_descriptors(NEW_PATHS) < 0) {
open_file_descriptors(OLD_PATHS);
on_state = "wake";
off_state = "standby";
}
g_initialized = 1;
}
}
int acquire_wake_lock(int lock, const char* id)
{
initialize_fds();
if (g_error) return g_error;
int fd;
if (lock == PARTIAL_WAKE_LOCK) { // 上层传下来的lock type
fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
}
else {
return EINVAL;
}
return write(fd, id, strlen(id));
}
int release_wake_lock(const char* id)
{
initialize_fds();
// LOGI("release_wake_lock id='%s'\n", id);
if (g_error) return g_error;
ssize_t len = write(g_fds[RELEASE_WAKE_LOCK], id, strlen(id));
return len >= 0;
}
int set_screen_state(int on)
{
QEMU_FALLBACK(set_screen_state(on));
LOGI("*** set_screen_state %d", on);
initialize_fds();
if (g_error) return g_error;
char buf[32];
int len;
if(on)
len = sprintf(buf, on_state);
else
len = sprintf(buf, off_state);
len = write(g_fds[REQUEST_STATE], buf, len);
if(len < 0) {
LOGE("Failed setting last user activity: g_error=%d\n", g_error);
}
return 0;
}
Hal层的代码在jni层中被使用,源码位于:frameworks/base/core/jni/android_os_Power.cpp,代码片段如下:
static void acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj)
{
if (idObj == NULL) {
throw_NullPointerException(env, "id is null");
return ;
}
const char *id = env->GetStringUTFChars(idObj, NULL);
acquire_wake_lock(lock, id);
env->ReleaseStringUTFChars(idObj, id);
}// wakelock加锁函数
static void releaseWakeLock(JNIEnv *env, jobject clazz, jstring idObj)
{
if (idObj == NULL) {
throw_NullPointerException(env, "id is null");
return ;
}
const char *id = env->GetStringUTFChars(idObj, NULL);
release_wake_lock(id);
env->ReleaseStringUTFChars(idObj, id);
}// wakelock解锁函数
static int setScreenState(JNIEnv *env, jobject clazz, jboolean on)
{
return set_screen_state(on);
}// 休眠唤醒的函数
Jni的方法需要注册到上层才可以使用,同时也需要在上层的对应java类中声明了native才可以使用。那么这里的方法在java中对应的声明在哪里呢?frameworks/base/core/java/android/os/Power.java,该文件定义一个java类,如下:
public class Power
{
// can't instantiate this class
private Power()
{
}
/**
* Wake lock that ensures that the CPU is running. The screen might
* not be on.
*/
public static final int PARTIAL_WAKE_LOCK = 1;
/**
* Wake lock that ensures that the screen is on.
*/
public static final int FULL_WAKE_LOCK = 2;
public static native void acquireWakeLock(int lock, String id);
public static native void releaseWakeLock(String id);
/**
* Turn the screen on or off
*
* @param on Whether you want the screen on or off
*/
public static native int setScreenState(boolean on);
}
声明的jni接口应该是被java server在使用,这里就是专门的电源管理服务:PowerManagerService使用,具体源码位置在:frameworks/base/services/java/com/android/server/PowerManagerService.javaandroid在最上层还提供了现场的android.os.PowerManager
(frameworks/base/core/java/android/os/PowerManager.java)来供app使用,PowerManager类会调用java服务PowerManagerService的方法来完成与wakelock相关的工作。
@ frameworks/base/core/java/android/os/PowerManager.java
PowerManager中内嵌了一个WakeLock类,另外还定义了wakelock的类型,下面是代码片段:
public class PowerManager
{
private static final String TAG = "PowerManager";
/**
* Wake lock that ensures that the CPU is running. The screen might
* not be on.
*/
public static final int PARTIAL_WAKE_LOCK = WAKE_BIT_CPU_STRONG;
/**
* Wake lock that ensures that the screen and keyboard are on at
* full brightness.
*/
public static final int FULL_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT | WAKE_BIT_KEYBOARD_BRIGHT;
/**
* Wake lock that ensures that the screen is on at full brightness;
* the keyboard backlight will be allowed to go off.
*/
public static final int SCREEN_BRIGHT_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_BRIGHT;
/**
* Wake lock that ensures that the screen is on (but may be dimmed);
* the keyboard backlight will be allowed to go off.
*/
public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
/**
* Wake lock that turns the screen off when the proximity sensor activates.
* Since not all devices have proximity sensors, use
* {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
* this wake lock mode is supported.
*
* {@hide}
*/
public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK
= WAKE_BIT_PROXIMITY_SCREEN_OFF;
public class WakeLock
{
WakeLock(int flags, String tag)
{
switch (flags & LOCK_MASK) {
case PARTIAL_WAKE_LOCK:
case SCREEN_DIM_WAKE_LOCK:
case SCREEN_BRIGHT_WAKE_LOCK:
case FULL_WAKE_LOCK:
case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
break;
default:
throw new IllegalArgumentException();
}
mFlags = flags;
mTag = tag;
mToken = new Binder();
}
public void acquire()
{
synchronized (mToken) {
if (!mRefCounted || mCount++ == 0) {
try {
mService.acquireWakeLock(mFlags, mToken, mTag);
} catch (RemoteException e) {
}
mHeld = true;
}
}
}
public void release(int flags)
{
synchronized (mToken) {
if (!mRefCounted || --mCount == 0) {
try {
mService.releaseWakeLock(mToken, flags);
} catch (RemoteException e) {
}
mHeld = false;
}
if (mCount < 0) {
throw new RuntimeException("WakeLock under-locked " + mTag);
}
}
}
}
public WakeLock newWakeLock(int flags, String tag)
{
if (tag == null) {
throw new NullPointerException("tag is
null in PowerManager.newWakeLock");
}
return new WakeLock(flags, tag);
}
public void goToSleep(long time)
{
try {
mService.goToSleep(time);
} catch (RemoteException e) {
}
}
public PowerManager(IPowerManager service, Handler handler)
{
mService = service;

Android在標準linux基礎上對休眠喚醒的實現(二)(轉貼)


三、 kernel層源碼解析- wakelock的重要地位
wakelockandroid的休眠喚醒機制中扮演著及其重要的角色,主要源碼位於文件:
kernel/kernel/power/wakelock.c
kernel/include/linux/wakelock.h中。

wakelocks_init()函數所做的工作是整個wakelock可以工作起來的基礎,所有這裡先說說這個函數。
static int __init wakelocks_init (void)
{
       int ret;
       int i;

       for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
              INIT_LIST_HEAD(&active_wake_locks[i]);
       //初始化active_wake_locks數組中的兩個類型鎖鍊錶: WAKE_LOCK_SUSPEND,WAKE_LOCK_IDLE

#ifdef CONFIG_WAKELOCK_STAT         // defined
       wake_lock_init(& deleted_wake_locks , WAKE_LOCK_SUSPEND,
                     "deleted_wake_locks");
       //初始化wakelock deleted_wake_locks,同時將其加入到非活動鎖鍊錶中
#endif
       wake_lock_init(& main_wake_lock , WAKE_LOCK_SUSPEND, "main");
       wake_lock_init(& sys_sync_wake_lock , WAKE_LOCK_SUSPEND, "sys_sync");
       wake_lock(&main_wake_lock);
       wake_lock_init(& unknown_wakeup , WAKE_LOCK_SUSPEND, "unknown_wakeups");
       //初始化wakelock: main, sys_sync, unknown_wakeups,同時將其加入到非活動鎖鍊錶中
       //main_wake_lock加鎖
      
       ret = platform_device_register(&power_device) ;
       if (ret) {
              pr_err("[wakelocks_init]: platform_device_register failed/n");
              goto err_platform_device_register;
       }
       ret = platform_driver_register(&power_driver) ;
       if (ret) {
              pr_err("[wakelocks_init]: platform_driver_register failed/n");
              goto err_platform_driver_register;
       }

       //新建工作隊列和工作者內核線程: sys_sync_work_queue, fs_sync
       // suspend_work_queue, suspend                                                     
       sys_sync_work_queue = create_singlethread_workqueue("fs_sync");
       if (sys_sync_work_queue == NULL) {
              pr_err("[wakelocks_init] fs_sync workqueue create failed/n");
       }

       suspend_work_queue = create_singlethread_workqueue("suspend");
       if (suspend_work_queue == NULL) {
              ret = -ENOMEM;
              goto err_suspend_work_queue;
       }

#ifdef CONFIG_WAKELOCK_STAT
       proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
       //創建proc接口
#endif

       return 0;

err_suspend_work_queue:
       platform_driver_unregister(&power_driver);
err_platform_driver_register:
       platform_device_unregister(&power_device);
err_platform_device_register:
       wake_lock_destroy(&unknown_wakeup);
       wake_lock_destroy(&main_wake_lock);
#ifdef CONFIG_WAKELOCK_STAT
       wake_lock_destroy(&deleted_wake_locks);
#endif
       return ret;
}

可以看到該初始化函數中新建了幾個wakelock: deleted_wake_locksmain_wake_locksys_sync_wake_lockunknown_wakeup,他們全部都是WAKE_LOCK_SUSPEND類型的wakelock,說到這裡不得不提到wakelock的兩種類型了:
1.      WAKE_LOCK_SUSPEND –這種鎖如果被某個task持有,那麼系統將無法進入休眠。
2.      WAKE_LOCK_IDLE –這種鎖不會影響到系統進入休眠,但是如果這種鎖被持有,那麼系統將無法進入idle空閒模式。

不過常用的所類型還是WAKE_LOCK_SUSPEND,包括userwakelock.c提供給用戶空間的新建wakelock的接口,都是建立的第一種鎖。另外系統為了分開管理這兩種不同類型的鎖,建立了兩個鍊錶來統一鏈接不同類型的鎖:active_wake_locks[],這個是具有兩個鍊錶頭的數組,元素0是掛接WAKE_LOCK_SUSPEND類型的鎖,而元素1就是掛接WAKE_LOCK_IDLE類型的wakelock了。

       接著上面說,這個初始化函數新建這些鎖之後,直接將主鎖(main_wake_lock)給上鎖了,其餘都是非鎖狀態。新建wakelock使用函數wake_lock_init(),該函數設置鎖的名字,類型,最後將新建的鎖掛接到一個專門鏈接這些非鎖狀態的鍊錶inactive_locks(新建的wakelock初期都是出於非鎖狀態的,除非顯示調用函數wake_lock來上鎖)。接著如果使用函數wake_lock()來給特定的wakelock上鎖的話,會將該鎖從鍊錶inactive_locks上移動到對應類型的專用鍊錶上active_wake_locks[type]上。
      
       wakelock有兩種形式的鎖:超時鎖和非超時鎖,這兩種形式的鎖都是使用函數wake_lock_init()來初始化,只是在上鎖的時候會有一點點差別,超時鎖使用函數wake_lock_timeout(),而非超時鎖使用函數wake_lock(),這個兩個函數會最終調用到同一個函數wake_lock_internal(),該函數依靠傳入的 ​​不同參數來選擇不同的路徑來工作。值得注意的是,非超時鎖必須手工解鎖,否則係統永遠不能進入睡眠。下面是wake_lock_internal()函數的片段:
       if (!(lock->flags & WAKE_LOCK_ACTIVE))
              lock->flags |= WAKE_LOCK_ACTIVE; // wakelock狀態為inactive,則更改為active
       …
       if ( has_timeout ) { // wake_lock_timeout() 會傳入1
              if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
                     pr_info("[wake_lock_internal]: %s, type %d, timeout %ld.%03lu/n",
                            lock->name, type, timeout / HZ,
                            (timeout % HZ) * MSEC_PER_SEC / HZ);
              lock->expires = jiffies + timeout; //       設置超時時間
              lock->flags |= WAKE_LOCK_AUTO_EXPIRE; //       超時鎖標誌
              list_add_tail(&lock->link, &active_wake_locks[type]) ;
       }
    // acquire a non-timeout wakelock添加一個非超時鎖
       else { // wake_lock ()      會傳入0
              if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
                     pr_info("[wake_lock_internal]: %s, type %d/n", lock->name, type);
              lock->expires = LONG_MAX; //    設置成超時時間最大值
              lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE; //非超時鎖標誌
              list_add(&lock->link, &active_wake_locks[type]);
              //將剛剛設置的非超時鎖加到對應類型的活動鎖鍊錶中
       }
      
       解鎖的時候,這兩種形式的鎖所使用函數都是一樣了:wake_unlock(),該函數中會首先作如下操作:
       lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
       //清除鎖活動標誌和自動超時標誌
       list_del(&lock->link); //   從鎖對應的活動鍊錶上摘除
       list_add(&lock->link, &inactive_locks);   
//unlock的鎖掛接到非活動鍊錶inactive_locks

前面已經說了只有類型為WAKE_LOCK_SUSPENDwakelock被上鎖才會阻止系統進入suspend,那麼也就是說只要鍊錶active_wake_locks[WAKE_LOCK_SUSPEND]NULL,那麼系統就可以執行suspend的流程了。Androidlinux的改造,讓其可以在三種情況下進入linux的標準suspend的流程:
1.               wake_unlock(),這個應該是最容易想到的,只要係統有對WAKE_LOCK_SUSPEND類型的wakelock解鎖的動作,都有可能會進入suspend流程開始休眠,為什麼是有可能呢?因為可能還有超時鎖沒有被超時解鎖。下面看一下代碼片段:
void wake_unlock(struct wake_lock *lock)
{
       …
       if ( type == WAKE_LOCK_SUSPEND ) //貌似只在處理這個類型的wakelock
    {
              long has_lock = has_wake_lock_locked(type) ;
              //這個函數蠻重要,它來檢查type類型的鍊錶上是否還有鎖被上鎖了。
        //其返回值如果是0,說明沒有該類型的鎖被持有了;返回非0表明就是這個類型的活動鍊錶上還存在超時鎖但是沒有非超時鎖了,這個返回值就是當前時間距離最後超時的鎖超時時間的jiffies值;如果返回-1,那表明還有該類型的非超時鎖被持有。
        if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
            pr_info("[wake_unlock]: has_​​lock = 0x%x/n" , has_​​lock);       
              if ( has_lock > 0 ) {
                     if (wakelock_debug_mask & DEBUG_EXPIRE)
                            pr_info("[wake_unlock]: %s, start expire timer, "
                                   "%ld/n", lock->name, has_​​lock);
                     mod_timer(&expire_timer, jiffies + has_​​lock);
//修改定時器的超時值並add該定時器
              }
              else //已經沒有超時鎖了
              {
                     if ( del_timer(&expire_timer) ) //            刪除定時器
                            if (wakelock_debug_mask & DEBUG_EXPIRE)
                                   pr_info("[wake_unlock]: %s, stop expire "
                                          "timer/n", lock->name);
                     if ( has_lock == 0 )
// !=0,表明還有該類型的非超時鎖被持有,現在還不能進入suspend
                     {
                      pr_info("[wake_unlock]: (%s) suspend_work_queue suspend_work/n" , lock->name);
                            queue_work(suspend_work_queue, &suspend_work);
                            //提交suspend的工作項,開始執行標準linuxsuspend流程
                     }    
              }
              …
       }
       spin_unlock_irqrestore(&list_lock, irqflags);
}

2.             超時鎖超時之後,定時器的回調函數會執行會查看是否有其他的wakelock,如果沒有,就在這裡讓系統進入睡眠。
static void expire_wake_locks (unsigned long data)
{
       long has_​​lock;
       unsigned long irqflags;
       if (debug_mask & DEBUG_EXPIRE)
              pr_info("expire_wake_locks: start/n");
       spin_lock_irqsave(&list_lock, irqflags);
       if (debug_mask & DEBUG_SUSPEND)
              print_active_locks(WAKE_LOCK_SUSPEND);
       has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND) ;
       if (debug_mask & DEBUG_EXPIRE)
              pr_info("expire_wake_locks: done, has_​​lock %ld/n", has_​​lock);
       if ( has_lock == 0 )
//如果沒有SUSPEND類型的wakelock處於active,那麼將調用suspend
              queue_work(suspend_work_queue, &suspend_work);
       spin_unlock_irqrestore(&list_lock, irqflags);
}
       static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
列出以下一個重要的函數源碼:
static long has_wake_lock_locked (int type)
{
       struct wake_lock *lock, *n;
       long max_timeout = 0;

       BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
       list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
              if ( lock->flags & WAKE_LOCK_AUTO_EXPIRE ) {
                     long timeout = lock->expires - jiffies;
                     if (timeout <= 0)
                            expire_wake_lock(lock);
                     else if (timeout > max_timeout)
                            max_timeout = timeout;
              } else
                     return -1;
       }
       return max_timeout;
}

3.               這個可能有人覺得匪夷所思,就是在wake_lock{_ _timeout}()函數中,調用了內部函數wake_lock_internal()。這裡只有在對超時鎖上鎖的時候才有可能進入休眠,如果對一個費超時鎖上鎖的話,那麼就沒有必要去檢查活動鍊錶了。
static void wake_lock_internal(
       struct wake_lock *lock, long timeout, int has_​​timeout)
{
if ( type == WAKE_LOCK_SUSPEND ) {
              current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
              if (lock == &main_wake_lock)
                     update_sleep_wait_stats_locked(1);
              else if (!wake_lock_active(&main_wake_lock))
                     update_sleep_wait_stats_locked(0);
#endif
              if (has_timeout) //   超時鎖的時候傳進來的是1
                     expire_in = has_​​wake_lock_locked(type);
                     //檢查當前鎖類型鍊錶上是否還有鎖處於active的狀態,無返回0
              else
                     expire_in = -1;
//如果是非超時鎖的話,這裡直接賦值-1,省去了活動鍊錶檢查步驟了
              if ( expire_in > 0 ) {
                     if (debug_mask & DEBUG_EXPIRE)
                            pr_info("wake_lock: %s, start expire timer, "
                                   "%ld/n", lock->name, expire_in);
                     // modify the time wakelock is expired
                     mod_timer(&expire_timer, jiffies + expire_in);
              } else {
                     if ( del_timer(&expire_timer) )
                            if (debug_mask & DEBUG_EXPIRE)
                                   pr_info("wake_lock: %s, stop expire timer/n",
                                          lock->name);
                     if ( expire_in == 0 ) //沒有鎖處於active狀態後,準備調用suspend
                     {
                      pr_info("[wake_lock]: suspend_work_queue suspend_work/n ");
                            queue_work(suspend_work_queue, &suspend_work) ;
                     }
              }
       }
       spin_unlock_irqrestore(&list_lock, irqflags);
}

下面是suspend的工作項,經過上面三種情況的檢查,ok之後將會提交該工作項給工作隊列suspend_work_queue,如下:
static void suspend(struct work_struct *work)
{
       int ret;
       int entry_event_num;

    // there are still some wakelock
       if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
              if (wakelock_debug_mask & DEBUG_SUSPEND)
                     pr_info("[suspend]: abort suspend/n");
              return;
       }

       entry_event_num = current_event_num;
       sys_sync();
       if (debug_mask & DEBUG_SUSPEND)
              pr_info("suspend: enter suspend/n");
       ret = pm_suspend(requested_suspend_state) ;
       // requested_suspend_state這個全局變量在函數request_suspend_state()中被設置,也就是執行了eraly suspend或者late resume之後,主要是為suspend保留請求的省電狀態。
       if (debug_mask & DEBUG_EXIT_SUSPEND) {
              struct timespec ts;
              struct rtc_time tm;
              getnstimeofday(&ts);
              rtc_time_to_tm(ts.tv_sec, &tm);
              pr_info("suspend: exit suspend, ret = %d "
                     "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)/n", ret,
                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                     tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
       }
       if (current_event_num == entry_event_num) {
              if (debug_mask & DEBUG_SUSPEND)
                     pr_info("suspend: pm_suspend returned with no event/n");
              wake_lock_timeout(&unknown_wakeup, HZ / 2);
       }
}
static DECLARE_WORK(suspend_work, suspend);

@kernel/ke​​rnel/power/suspend.c
int pm_suspend (suspend_state_t state)
{
       if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
              return enter_state(state);
              //標準linuxsuspend流程函數
return -EINVAL;
}
EXPORT_SYMBOL(pm_suspend);

Wakelock的機制被文件userwakelock.c中的code封裝成了sys的接口sys/power/wake_locksys/power/wake_unlock文件,那麼上層如果需要新建wakelock或者註銷wakelock,或者是解鎖wakelock,都是操作這兩個sys接口文件。