Android 电源管理-Healthd(1)

OS:Android 4.4.2

Android电源管理底层用的是Linux power supply框架。驱动部分不叙述。只看JAVA、JNI和CPP应用层。

从Android 4.4开始,Google专门提供了一个healthd来监控电源状态。它的路径在:system/core/healthd,编译出来的文件为/sbin/healthd

看一下healthd.cpp中的main函数:

int main(int argc, char **argv) {
    int ch;

    klog_set_level(KLOG_LEVEL);

    while ((ch = getopt(argc, argv, "n")) != -1) {
        switch (ch) {
        case 'n':
            nosvcmgr = true;
            break;
        case '?':
        default:
            KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
        }
    }

    healthd_board_init(&healthd_config);
    wakealarm_init();
    uevent_init();
    binder_init();
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config, nosvcmgr);

    healthd_mainloop();
    return 0;
}

这里引人关注的是最后调用的healthd_mainloop(),仅凭函数名就能知道会进入一个无限循环,这样也就能达到监控电源状态的目的了。跟踪代码看一下这个函数的定义:

static void healthd_mainloop(void) {
    struct epoll_event ev;
    int epollfd;
    int maxevents = 0;

    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) {
        KLOG_ERROR(LOG_TAG,
                   "healthd_mainloop: epoll_create failed; errno=%d\n",
                   errno);
        return;
    }

    if (uevent_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr = (void *)uevent_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
    }

    if (wakealarm_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr = (void *)wakealarm_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
   }

    if (binder_fd >= 0) {
        ev.events = EPOLLIN | EPOLLWAKEUP;
        ev.data.ptr= (void *)binder_event;
        if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1)
            KLOG_ERROR(LOG_TAG,
                       "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n",
                       errno);
        else
            maxevents++;
   }

    while (1) {
        struct epoll_event events[maxevents];
        int nevents;

        IPCThreadState::self()->flushCommands();
        nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);

        if (nevents == -1) {
            if (errno == EINTR)
                continue;
            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
            break;
        }

        for (int n = 0; n < nevents; ++n) {
            if (events[n].data.ptr)
                (*(void (*)())events[n].data.ptr)();
        }

        if (!nevents)
            periodic_chores();
    }

    return;
}

果不其然,我们看到了while(1)。Android使用了linux epoll来侦听三个文件描述符的变化,分别是uevent_fd、wakealarm_fd和binder_fd。以uevent_fd为例,在这个描述符上关心的事件有EPOLLIN和EPOLLWAKEUP,当底层出现这两个事件的时候,会回调函数uevent_event(),看看这个函数会做些什么:

#define UEVENT_MSG_LEN 1024
static void uevent_event(void) {
    char msg[UEVENT_MSG_LEN+2];
    char *cp;
    int n;

    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
    if (n <= 0)
        return;
    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
        return;

    msg[n] = '\0';
    msg[n+1] = '\0';
    cp = msg;

    while (*cp) {
        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
            battery_update();
            break;
        }

        /* advance to after the next \0 */
        while (*cp++)
            ;
    }
}

它会读取socket中的字符串,然后判断事件来源是否是由kernel的power_supply发出的,如果是,那就调用battery_update()更新电源状态。下面来看看battery_update()是如何更新电源状态的:

static void battery_update(void) {
    // Fast wake interval when on charger (watch for overheat);
    // slow wake interval when on battery (watch for drained battery).

   int new_wake_interval = gBatteryMonitor->update() ?
       healthd_config.periodic_chores_interval_fast :
           healthd_config.periodic_chores_interval_slow;

    if (new_wake_interval != wakealarm_wake_interval)
            wakealarm_set_interval(new_wake_interval);

    // During awake periods poll at fast rate.  If wake alarm is set at fast
    // rate then just use the alarm; if wake alarm is set at slow rate then
    // poll at fast rate while awake and let alarm wake up at slow rate when
    // asleep.

    if (healthd_config.periodic_chores_interval_fast == -1)
        awake_poll_interval = -1;
    else
        awake_poll_interval =
            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
                -1 : healthd_config.periodic_chores_interval_fast * 1000;
}

这里的重点是gBatteryMonitor->update(),gBatteryMonitor的类型为BatteryMonitor。那接下来就把目光专注到system/core/healthd/BatteryMonitor.hsystem/core/healthd/BatteryMonitor.cpp

在BatteryMonitor.h中,我们看到了一个enum,它列出了Android所支持的电源类型:

    enum PowerSupplyType {
        ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
        ANDROID_POWER_SUPPLY_TYPE_AC,
        ANDROID_POWER_SUPPLY_TYPE_USB,
        ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
        ANDROID_POWER_SUPPLY_TYPE_BATTERY
    };

可以看出Android当前支持的电源类型有4种:AC,USB,WIRELESS,BATTERY。在这里你可以增加你自己的电源类型,比如CAR(车载)… … 把目光聚焦到BatteryMonitor.cpp,这里有获取电源状态的核心代码。但是在继续看代码之前,我们先来看一下power_supply电源驱动是如何管理系统中的不同电源类型的。

呃,太晚咯,明天还加班,待续好了 。。。

Android 控制生成 ODEX

如果没有指定DISABLE_DEXPREOPT,那么user版将生成odex,其他版本不会生成odex文件

相关文件在build/core/main.mk

 ifneq (true,$(DISABLE_DEXPREOPT))
    ifeq ($(user_variant),user)
      ifeq ($(HOST_OS),linux)
        WITH_DEXPREOPT := true
      endif
    endif
  endif
  • 如果user版不想生成odex,那么需要定义

    DISABLE_DEXPREOPT := true
    
  • 如果eng版要生成odex,那么需要定义

    WITH_DEXPREOPT := true
    
  • 如果想单独控制某一应用是否生成odex,可以在应用的Android.mk中定义

    WITH_DEXPREOPT := true
    

Install Skype on Ubuntu 12.04 64bit

sudo dpkg -P skype

rm -rf ~/.Skype

sudo apt-get update

sudo apt-get install gdebi

sudo apt-get install sni-qt:i386

wget download.skype.com/linux/skype-ubuntu-precise4.3.0.37-1i386.deb

sudo gdebi skype-ubuntu-precise4.3.0.37-1i386.deb

Android Framework 自定义系统选项

Android系统默认提供了一些Contont Provider,如 Contacts ProviderDocuments ProviderSettings Provider等等。这些provider源码都放在frameworks/base/core/java/android/provider文件夹中。

当我们需要自定义一些系统设置,比如在Settings界面中增加了一个设置keyguard特效的选项,当用户在Settings界面中设置了某个特效后,keyguard那边能够读取这个设置项,并做出相应的改变。很显然的这里涉及到IPC,通常我们可以自己写一个Provider,但如果你有源码就完全可以复用Android提供的已有Settings Provider,达到简洁统一。

打开frameworks/base/core/java/android/provider/Settings.java文件,导航到NameValueCache类。在这个类中你会看到很多我们熟悉的一些系统设置(WIFI、亮度等等),这些值都会储存到数据库中(系统的或者用户的)。我们在这个类中增加一个常量来标记我们新增的设置项:

public static final String KEYGUARD_TRANSITION_TYPE = "keyguard_transition_type"; 

修改完之后需要重新编译一下framework。

现在Settings Provider中有了我们需要的字段,下一步就是怎么往里存储和读取了。方法很简单,从上面Settings.java文件中可以看到有很多put和get方法,我们可以使用这些方法来对NameValueCache中声明的字段进行存取操作。

import static android.provider.Settings.System.KEYGUARD_TRANSITION_TYPE;

Settings.System.putInt(getContentResolver(),
                    KEYGUARD_TRANSITION_TYPE, value);

int currentValue = Settings.System.getInt(
                    getContentResolver(), KEYGUARD_TRANSITION_TYPE,);

Settings.System.putIntForUser(getContentResolver(),
                    KEYGUARD_TRANSITION_TYPE, value, UserHandle.USER_CURRENT);

int currentValue = Settings.System.getIntForUser(
                    getContentResolver(), KEYGUARD_TRANSITION_TYPE,
                    UserHandle.USER_CURRENT);

需要注意的是上面带有用户参数的方法(*ForUser)是Hide属性的。

最后需要一个数据监听器,这样才能做到实时更新选项:

    private static final Uri mUri = Settings.System.getUriFor(KEYGUARD_TRANSITION_TYPE);

    private final ContentObserver mObserver = new ContentObserver(new Handler()) {
        public void onChange(boolean selfChange) {
           //you can do something here
        }
    };

    getContentResolver().registerContentObserver(mUri, false, mObserver);

Android 系统监听切换用户事件

try{                                                                                                                                                                           
        ActivityManagerNative.getDefault().registerUserSwitchObserver(
            new IUserSwitchObserver.Stub() {
              @Override
              public void onUserSwitching(int newUserId, IRemoteCallback reply) {
                switchUser(newUserId, reply);//Here, you can do something you want
              }
         
              @Override
              public void onUserSwitchComplete(int newUserId) throws RemoteException {
              }
            });  
      } catch (RemoteException e) { 
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
}

ref : frameworks/base/services/java/com/android/server/WallpaperManagerService.java : 519