Monday, November 30, 2015

Android smart phone with longer battery life (2)

This time, let's start from the healthd battery health monitor service start entrance. It is the healthd.cpp file from https://github.com/android/platform_system_core/blob/master/healthd/healthd.cpp, here is the code snippets of main(...):
int main(int argc, char **argv) {
    int ch;
    int ret;

    klog_set_level(KLOG_LEVEL);  // logging into kernel
    healthd_mode_ops = &android_ops;  // setup the callback

    if (!strcmp(basename(argv[0]), "charger")) {
        healthd_mode_ops = &charger_ops;   // setup the charger only callback
    } else {
        while ((ch = getopt(argc, argv, "cr")) != -1) {
            switch (ch) {
            case 'c':
                healthd_mode_ops = &charger_ops;
                break;
            case 'r':
                healthd_mode_ops = &recovery_ops; // setup recovery mode callback
The global variable healthd_mode_ops is an instance of struct healthd_mode_ops. It is to register the callbacks of battery monitor initialization and operations of update. The service has three different callbacks for normal bootup, recovery mode, and charger only mode respectively.
struct healthd_mode_ops {
    void (*init)(struct healthd_config *config);
    int (*preparetowait)(void);
    void (*heartbeat)(void);
    void (*battery_update)(struct android::BatteryProperties *props);
};
Here is the normal mode callbacks:
static struct healthd_mode_ops android_ops = {
    .init = healthd_mode_android_init,
    .preparetowait = healthd_mode_android_preparetowait,
    .heartbeat = healthd_mode_nop_heartbeat,
    .battery_update = healthd_mode_android_battery_update,
};
The callback functions are implemented in file: https://github.com/android/platform_system_core/blob/2655256570b7c1c5af6d886735835eecb99f45f2/healthd/healthd_mode_android.cpp. We will re-visit the file in details a little later.
After the callback installation, healthd_init() and an endless loop healthd_mainloop() is followed.
    ret = healthd_init();
    if (ret) {
        KLOG_ERROR("Initialization failed, exiting\n");
        exit(2);
    }

    healthd_mainloop();
And here is the healthd_init(), which finishes the following:
  • create an epoll instance, it is a very scalable I/O event notification mechanism which is in here to monitor the kernel uevent updates, binder object, wakeup alarm timer etc.
  • call the registered init callback of healthd_mode_ops. We will re-visit later.
  • healthd_board_init(...), which actually does nothing here.
  • setup a wakeup alarm timer, add the timer file descriptor to be monitored by the epoll object. By default the fast timer internal is set.
  • setup a uevent, and add the uevent file descriptor to be monitored by the epoll object. This is here to get the battery subsystem updates from kernel.
  • create the BatteryMonitor object, and get BatteryMonitor initialized. The BatteryMonitor initialization has been visited in part I.
static int healthd_init() {
    epollfd = epoll_create(MAX_EPOLL_EVENTS);
    if (epollfd == -1) {
        KLOG_ERROR(LOG_TAG,
                   "epoll_create failed; errno=%d\n",
                   errno);
        return -1;
    }

    healthd_mode_ops->init(&healthd_config);
    healthd_board_init(&healthd_config);
    wakealarm_init();
    uevent_init();
    gBatteryMonitor = new BatteryMonitor();
    gBatteryMonitor->init(&healthd_config);
Then it is an endless loop healthd_mainloop(). That is how the battery health state is get monitored continuously and endlessly. It waits on the epoll object, once any event happens the event callback will be called, which could be one or several of the registered events.
static void healthd_mainloop(void) {
    while (1) {
        struct epoll_event events[eventct];
        int nevents;
        int timeout = awake_poll_interval;
        int mode_timeout;

        mode_timeout = healthd_mode_ops->preparetowait();
        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
            timeout = mode_timeout;
        nevents = epoll_wait(epollfd, events, eventct, timeout);

        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 (*)(int))events[n].data.ptr)(events[n].events);
        }

        if (!nevents)
            periodic_chores();

        healthd_mode_ops->heartbeat();
    }

More details about callbacks of healthd_mode_android.cpp

Let's look into more details about the callbacks of file: https://github.com/android/platform_system_core/blob/2655256570b7c1c5af6d886735835eecb99f45f2/healthd/healthd_mode_android.cpp.
void healthd_mode_android_init(struct healthd_config* /*config*/) is the initialize function, it creates a binder object, add the binder object into the epoll polling list and install the callback for the operation when the binder object is polled by the epoll.
void healthd_mode_android_init(struct healthd_config* /*config*/) {
    ProcessState::self()->setThreadPoolMaxThreadCount(0);
    IPCThreadState::self()->disableBackgroundScheduling(true);
    IPCThreadState::self()->setupPolling(&gBinderFd);

    if (gBinderFd >= 0) {
        if (healthd_register_event(gBinderFd, binder_event))
            KLOG_ERROR(LOG_TAG,
                       "Register for binder events failed\n");
    }
The installed epoll event callback for the binder is to process the binder commands:
static void binder_event(uint32_t /*epevents*/) {
    IPCThreadState::self()->handlePolledCommands();
}
Finally the initialize register self as a service called "batteryproperties" to the android service manager. The android Java space can listen to the service to get the battery health state updates. Use the command: service list, you shall see the service "batteryproperties" listed.
    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
    gBatteryPropertiesRegistrar->publish();
}
void healthd_mode_android_battery_update(...) is the update callback. It is to notify all the registered service clients when the battery health state gets updated. It is the service listener's implementation of how to handle the battery state updates.
void healthd_mode_android_battery_update(
    struct android::BatteryProperties *props) {
    if (gBatteryPropertiesRegistrar != NULL)
        gBatteryPropertiesRegistrar->notifyListeners(*props);

    return;
}

Callbacks of other two epoll objects

The uevent callback receives the kernel uevent updates, then parse the power_supply events, and reads the sysfs power_supply updates.
static void uevent_event(uint32_t /*epevents*/) {
    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)) { // power_supply only
            healthd_battery_update(); // reads the power_supply sysfs into memory
            break;
        }

        /* advance to after the next \0 */
        while (*cp++)
            ;
    }
}
The wakeup timer object setup the timer to wakeup from kernel sleep and gets the power_supply updates.
static void wakealarm_event(uint32_t /*epevents*/) {
    ... ...
    periodic_chores();
}

static void periodic_chores() {
    healthd_battery_update(); // reads the power_supply sysfs into memory
}
Will illustrate how to get the battery state updates from Java with an sample app in the next article.

1 comment:

Unknown said...

Thank you very much for this post. There is so little posted on this subject, that any explanation is very welcome.