am 9259663c: am f17f5591: am 36d44740: Merge "Fix a bunch of small system/core bugs."

* commit '9259663cdb8f15226844e3ecfc6e3aa53614ad03':
  Fix a bunch of small system/core bugs.
diff --git a/charger/Android.mk b/charger/Android.mk
deleted file mode 100644
index b9d3473..0000000
--- a/charger/Android.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-ifneq ($(BUILD_TINY_ANDROID),true)
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-	charger.c
-
-ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
-LOCAL_CFLAGS := -DCHARGER_DISABLE_INIT_BLANK
-endif
-
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
-endif
-
-LOCAL_MODULE := charger
-LOCAL_MODULE_TAGS := optional
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_C_INCLUDES := bootable/recovery
-
-LOCAL_STATIC_LIBRARIES := libminui libpixelflinger_static libpng
-ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
-LOCAL_STATIC_LIBRARIES += libsuspend
-endif
-LOCAL_STATIC_LIBRARIES += libz libstdc++ libcutils liblog libm libc
-
-include $(BUILD_EXECUTABLE)
-
-define _add-charger-image
-include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_$(notdir $(1))
-LOCAL_MODULE_STEM := $(notdir $(1))
-_img_modules += $$(LOCAL_MODULE)
-LOCAL_SRC_FILES := $1
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
-include $$(BUILD_PREBUILT)
-endef
-
-_img_modules :=
-_images :=
-$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
-  $(eval $(call _add-charger-image,$(_img))))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_res_images
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := $(_img_modules)
-include $(BUILD_PHONY_PACKAGE)
-
-_add-charger-image :=
-_img_modules :=
-
-endif
diff --git a/charger/charger.c b/charger/charger.c
deleted file mode 100644
index 66ddeaf..0000000
--- a/charger/charger.c
+++ /dev/null
@@ -1,1015 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define DEBUG_UEVENTS
-#define CHARGER_KLOG_LEVEL 6
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/input.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <linux/netlink.h>
-
-#include <cutils/android_reboot.h>
-#include <cutils/klog.h>
-#include <cutils/list.h>
-#include <cutils/misc.h>
-#include <cutils/uevent.h>
-
-#ifdef CHARGER_ENABLE_SUSPEND
-#include <suspend/autosuspend.h>
-#endif
-
-#include "minui/minui.h"
-
-#ifndef max
-#define max(a,b) ((a) > (b) ? (a) : (b))
-#endif
-
-#ifndef min
-#define min(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
-
-#define MSEC_PER_SEC            (1000LL)
-#define NSEC_PER_MSEC           (1000000LL)
-
-#define BATTERY_UNKNOWN_TIME    (2 * MSEC_PER_SEC)
-#define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
-#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
-
-#define BATTERY_FULL_THRESH     95
-
-#define LAST_KMSG_PATH          "/proc/last_kmsg"
-#define LAST_KMSG_MAX_SZ        (32 * 1024)
-
-#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
-#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
-#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
-
-struct key_state {
-    bool pending;
-    bool down;
-    int64_t timestamp;
-};
-
-struct power_supply {
-    struct listnode list;
-    char name[256];
-    char type[32];
-    bool online;
-    bool valid;
-    char cap_path[PATH_MAX];
-};
-
-struct frame {
-    const char *name;
-    int disp_time;
-    int min_capacity;
-    bool level_only;
-
-    gr_surface surface;
-};
-
-struct animation {
-    bool run;
-
-    struct frame *frames;
-    int cur_frame;
-    int num_frames;
-
-    int cur_cycle;
-    int num_cycles;
-
-    /* current capacity being animated */
-    int capacity;
-};
-
-struct charger {
-    int64_t next_screen_transition;
-    int64_t next_key_check;
-    int64_t next_pwr_check;
-
-    struct key_state keys[KEY_MAX + 1];
-    int uevent_fd;
-
-    struct listnode supplies;
-    int num_supplies;
-    int num_supplies_online;
-
-    struct animation *batt_anim;
-    gr_surface surf_unknown;
-
-    struct power_supply *battery;
-};
-
-struct uevent {
-    const char *action;
-    const char *path;
-    const char *subsystem;
-    const char *ps_name;
-    const char *ps_type;
-    const char *ps_online;
-};
-
-static struct frame batt_anim_frames[] = {
-    {
-        .name = "charger/battery_0",
-        .disp_time = 750,
-        .min_capacity = 0,
-    },
-    {
-        .name = "charger/battery_1",
-        .disp_time = 750,
-        .min_capacity = 20,
-    },
-    {
-        .name = "charger/battery_2",
-        .disp_time = 750,
-        .min_capacity = 40,
-    },
-    {
-        .name = "charger/battery_3",
-        .disp_time = 750,
-        .min_capacity = 60,
-    },
-    {
-        .name = "charger/battery_4",
-        .disp_time = 750,
-        .min_capacity = 80,
-        .level_only = true,
-    },
-    {
-        .name = "charger/battery_5",
-        .disp_time = 750,
-        .min_capacity = BATTERY_FULL_THRESH,
-    },
-};
-
-static struct animation battery_animation = {
-    .frames = batt_anim_frames,
-    .num_frames = ARRAY_SIZE(batt_anim_frames),
-    .num_cycles = 3,
-};
-
-static struct charger charger_state = {
-    .batt_anim = &battery_animation,
-};
-
-static int char_width;
-static int char_height;
-
-/* current time in milliseconds */
-static int64_t curr_time_ms(void)
-{
-    struct timespec tm;
-    clock_gettime(CLOCK_MONOTONIC, &tm);
-    return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
-}
-
-static void clear_screen(void)
-{
-    gr_color(0, 0, 0, 255);
-    gr_fill(0, 0, gr_fb_width(), gr_fb_height());
-};
-
-#define MAX_KLOG_WRITE_BUF_SZ 256
-
-static void dump_last_kmsg(void)
-{
-    char *buf;
-    char *ptr;
-    unsigned sz = 0;
-    int len;
-
-    LOGI("\n");
-    LOGI("*************** LAST KMSG ***************\n");
-    LOGI("\n");
-    buf = load_file(LAST_KMSG_PATH, &sz);
-    if (!buf || !sz) {
-        LOGI("last_kmsg not found. Cold reset?\n");
-        goto out;
-    }
-
-    len = min(sz, LAST_KMSG_MAX_SZ);
-    ptr = buf + (sz - len);
-
-    while (len > 0) {
-        int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
-        char yoink;
-        char *nl;
-
-        nl = memrchr(ptr, '\n', cnt - 1);
-        if (nl)
-            cnt = nl - ptr + 1;
-
-        yoink = ptr[cnt];
-        ptr[cnt] = '\0';
-        klog_write(6, "<6>%s", ptr);
-        ptr[cnt] = yoink;
-
-        len -= cnt;
-        ptr += cnt;
-    }
-
-    free(buf);
-
-out:
-    LOGI("\n");
-    LOGI("************* END LAST KMSG *************\n");
-    LOGI("\n");
-}
-
-static int read_file(const char *path, char *buf, size_t sz)
-{
-    int fd;
-    size_t cnt;
-
-    fd = open(path, O_RDONLY, 0);
-    if (fd < 0)
-        goto err;
-
-    cnt = read(fd, buf, sz - 1);
-    if (cnt <= 0)
-        goto err;
-    buf[cnt] = '\0';
-    if (buf[cnt - 1] == '\n') {
-        cnt--;
-        buf[cnt] = '\0';
-    }
-
-    close(fd);
-    return cnt;
-
-err:
-    if (fd >= 0)
-        close(fd);
-    return -1;
-}
-
-static int read_file_int(const char *path, int *val)
-{
-    char buf[32];
-    int ret;
-    int tmp;
-    char *end;
-
-    ret = read_file(path, buf, sizeof(buf));
-    if (ret < 0)
-        return -1;
-
-    tmp = strtol(buf, &end, 0);
-    if (end == buf ||
-        ((end < buf+sizeof(buf)) && (*end != '\n' && *end != '\0')))
-        goto err;
-
-    *val = tmp;
-    return 0;
-
-err:
-    return -1;
-}
-
-static int get_battery_capacity(struct charger *charger)
-{
-    int ret;
-    int batt_cap = -1;
-
-    if (!charger->battery)
-        return -1;
-
-    ret = read_file_int(charger->battery->cap_path, &batt_cap);
-    if (ret < 0 || batt_cap > 100) {
-        batt_cap = -1;
-    }
-
-    return batt_cap;
-}
-
-static struct power_supply *find_supply(struct charger *charger,
-                                        const char *name)
-{
-    struct listnode *node;
-    struct power_supply *supply;
-
-    list_for_each(node, &charger->supplies) {
-        supply = node_to_item(node, struct power_supply, list);
-        if (!strncmp(name, supply->name, sizeof(supply->name)))
-            return supply;
-    }
-    return NULL;
-}
-
-static struct power_supply *add_supply(struct charger *charger,
-                                       const char *name, const char *type,
-                                       const char *path, bool online)
-{
-    struct power_supply *supply;
-
-    supply = calloc(1, sizeof(struct power_supply));
-    if (!supply)
-        return NULL;
-
-    strlcpy(supply->name, name, sizeof(supply->name));
-    strlcpy(supply->type, type, sizeof(supply->type));
-    snprintf(supply->cap_path, sizeof(supply->cap_path),
-             "/sys/%s/capacity", path);
-    supply->online = online;
-    list_add_tail(&charger->supplies, &supply->list);
-    charger->num_supplies++;
-    LOGV("... added %s %s %d\n", supply->name, supply->type, online);
-    return supply;
-}
-
-static void remove_supply(struct charger *charger, struct power_supply *supply)
-{
-    if (!supply)
-        return;
-    list_remove(&supply->list);
-    charger->num_supplies--;
-    free(supply);
-}
-
-#ifdef CHARGER_ENABLE_SUSPEND
-static int request_suspend(bool enable)
-{
-    if (enable)
-        return autosuspend_enable();
-    else
-        return autosuspend_disable();
-}
-#else
-static int request_suspend(bool enable)
-{
-    return 0;
-}
-#endif
-
-static void parse_uevent(const char *msg, struct uevent *uevent)
-{
-    uevent->action = "";
-    uevent->path = "";
-    uevent->subsystem = "";
-    uevent->ps_name = "";
-    uevent->ps_online = "";
-    uevent->ps_type = "";
-
-    /* currently ignoring SEQNUM */
-    while (*msg) {
-#ifdef DEBUG_UEVENTS
-        LOGV("uevent str: %s\n", msg);
-#endif
-        if (!strncmp(msg, "ACTION=", 7)) {
-            msg += 7;
-            uevent->action = msg;
-        } else if (!strncmp(msg, "DEVPATH=", 8)) {
-            msg += 8;
-            uevent->path = msg;
-        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {
-            msg += 10;
-            uevent->subsystem = msg;
-        } else if (!strncmp(msg, "POWER_SUPPLY_NAME=", 18)) {
-            msg += 18;
-            uevent->ps_name = msg;
-        } else if (!strncmp(msg, "POWER_SUPPLY_ONLINE=", 20)) {
-            msg += 20;
-            uevent->ps_online = msg;
-        } else if (!strncmp(msg, "POWER_SUPPLY_TYPE=", 18)) {
-            msg += 18;
-            uevent->ps_type = msg;
-        }
-
-        /* advance to after the next \0 */
-        while (*msg++)
-            ;
-    }
-
-    LOGV("event { '%s', '%s', '%s', '%s', '%s', '%s' }\n",
-         uevent->action, uevent->path, uevent->subsystem,
-         uevent->ps_name, uevent->ps_type, uevent->ps_online);
-}
-
-static void process_ps_uevent(struct charger *charger, struct uevent *uevent)
-{
-    int online;
-    char ps_type[32];
-    struct power_supply *supply = NULL;
-    int i;
-    bool was_online = false;
-    bool battery = false;
-
-    if (uevent->ps_type[0] == '\0') {
-        char *path;
-        int ret;
-
-        if (uevent->path[0] == '\0')
-            return;
-        ret = asprintf(&path, "/sys/%s/type", uevent->path);
-        if (ret <= 0)
-            return;
-        ret = read_file(path, ps_type, sizeof(ps_type));
-        free(path);
-        if (ret < 0)
-            return;
-    } else {
-        strlcpy(ps_type, uevent->ps_type, sizeof(ps_type));
-    }
-
-    if (!strncmp(ps_type, "Battery", 7))
-        battery = true;
-
-    online = atoi(uevent->ps_online);
-    supply = find_supply(charger, uevent->ps_name);
-    if (supply) {
-        was_online = supply->online;
-        supply->online = online;
-    }
-
-    if (!strcmp(uevent->action, "add")) {
-        if (!supply) {
-            supply = add_supply(charger, uevent->ps_name, ps_type, uevent->path,
-                                online);
-            if (!supply) {
-                LOGE("cannot add supply '%s' (%s %d)\n", uevent->ps_name,
-                     uevent->ps_type, online);
-                return;
-            }
-            /* only pick up the first battery for now */
-            if (battery && !charger->battery)
-                charger->battery = supply;
-        } else {
-            LOGE("supply '%s' already exists..\n", uevent->ps_name);
-        }
-    } else if (!strcmp(uevent->action, "remove")) {
-        if (supply) {
-            if (charger->battery == supply)
-                charger->battery = NULL;
-            remove_supply(charger, supply);
-            supply = NULL;
-        }
-    } else if (!strcmp(uevent->action, "change")) {
-        if (!supply) {
-            LOGE("power supply '%s' not found ('%s' %d)\n",
-                 uevent->ps_name, ps_type, online);
-            return;
-        }
-    } else {
-        return;
-    }
-
-    /* allow battery to be managed in the supply list but make it not
-     * contribute to online power supplies. */
-    if (!battery) {
-        if (was_online && !online)
-            charger->num_supplies_online--;
-        else if (supply && !was_online && online)
-            charger->num_supplies_online++;
-    }
-
-    LOGI("power supply %s (%s) %s (action=%s num_online=%d num_supplies=%d)\n",
-         uevent->ps_name, ps_type, battery ? "" : online ? "online" : "offline",
-         uevent->action, charger->num_supplies_online, charger->num_supplies);
-}
-
-static void process_uevent(struct charger *charger, struct uevent *uevent)
-{
-    if (!strcmp(uevent->subsystem, "power_supply"))
-        process_ps_uevent(charger, uevent);
-}
-
-#define UEVENT_MSG_LEN  1024
-static int handle_uevent_fd(struct charger *charger, int fd)
-{
-    char msg[UEVENT_MSG_LEN+2];
-    int n;
-
-    if (fd < 0)
-        return -1;
-
-    while (true) {
-        struct uevent uevent;
-
-        n = uevent_kernel_multicast_recv(fd, msg, UEVENT_MSG_LEN);
-        if (n <= 0)
-            break;
-        if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-            continue;
-
-        msg[n] = '\0';
-        msg[n+1] = '\0';
-
-        parse_uevent(msg, &uevent);
-        process_uevent(charger, &uevent);
-    }
-
-    return 0;
-}
-
-static int uevent_callback(int fd, short revents, void *data)
-{
-    struct charger *charger = data;
-
-    if (!(revents & POLLIN))
-        return -1;
-    return handle_uevent_fd(charger, fd);
-}
-
-/* force the kernel to regenerate the change events for the existing
- * devices, if valid */
-static void do_coldboot(struct charger *charger, DIR *d, const char *event,
-                        bool follow_links, int max_depth)
-{
-    struct dirent *de;
-    int dfd, fd;
-
-    dfd = dirfd(d);
-
-    fd = openat(dfd, "uevent", O_WRONLY);
-    if (fd >= 0) {
-        write(fd, event, strlen(event));
-        close(fd);
-        handle_uevent_fd(charger, charger->uevent_fd);
-    }
-
-    while ((de = readdir(d)) && max_depth > 0) {
-        DIR *d2;
-
-        LOGV("looking at '%s'\n", de->d_name);
-
-        if ((de->d_type != DT_DIR && !(de->d_type == DT_LNK && follow_links)) ||
-           de->d_name[0] == '.') {
-            LOGV("skipping '%s' type %d (depth=%d follow=%d)\n",
-                 de->d_name, de->d_type, max_depth, follow_links);
-            continue;
-        }
-        LOGV("can descend into '%s'\n", de->d_name);
-
-        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
-        if (fd < 0) {
-            LOGE("cannot openat %d '%s' (%d: %s)\n", dfd, de->d_name,
-                 errno, strerror(errno));
-            continue;
-        }
-
-        d2 = fdopendir(fd);
-        if (d2 == 0)
-            close(fd);
-        else {
-            LOGV("opened '%s'\n", de->d_name);
-            do_coldboot(charger, d2, event, follow_links, max_depth - 1);
-            closedir(d2);
-        }
-    }
-}
-
-static void coldboot(struct charger *charger, const char *path,
-                     const char *event)
-{
-    char str[256];
-
-    LOGV("doing coldboot '%s' in '%s'\n", event, path);
-    DIR *d = opendir(path);
-    if (d) {
-        snprintf(str, sizeof(str), "%s\n", event);
-        do_coldboot(charger, d, str, true, 1);
-        closedir(d);
-    }
-}
-
-static int draw_text(const char *str, int x, int y)
-{
-    int str_len_px = gr_measure(str);
-
-    if (x < 0)
-        x = (gr_fb_width() - str_len_px) / 2;
-    if (y < 0)
-        y = (gr_fb_height() - char_height) / 2;
-    gr_text(x, y, str, 0);
-
-    return y + char_height;
-}
-
-static void android_green(void)
-{
-    gr_color(0xa4, 0xc6, 0x39, 255);
-}
-
-/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(struct charger *charger, gr_surface surface)
-{
-    int w;
-    int h;
-    int x;
-    int y;
-
-    w = gr_get_width(surface);
-    h = gr_get_height(surface);
-    x = (gr_fb_width() - w) / 2 ;
-    y = (gr_fb_height() - h) / 2 ;
-
-    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
-    gr_blit(surface, 0, 0, w, h, x, y);
-    return y + h;
-}
-
-static void draw_unknown(struct charger *charger)
-{
-    int y;
-    if (charger->surf_unknown) {
-        draw_surface_centered(charger, charger->surf_unknown);
-    } else {
-        android_green();
-        y = draw_text("Charging!", -1, -1);
-        draw_text("?\?/100", -1, y + 25);
-    }
-}
-
-static void draw_battery(struct charger *charger)
-{
-    struct animation *batt_anim = charger->batt_anim;
-    struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
-
-    if (batt_anim->num_frames != 0) {
-        draw_surface_centered(charger, frame->surface);
-        LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n",
-             batt_anim->cur_frame, frame->name, frame->min_capacity,
-             frame->disp_time);
-    }
-}
-
-static void redraw_screen(struct charger *charger)
-{
-    struct animation *batt_anim = charger->batt_anim;
-
-    clear_screen();
-
-    /* try to display *something* */
-    if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
-        draw_unknown(charger);
-    else
-        draw_battery(charger);
-    gr_flip();
-}
-
-static void kick_animation(struct animation *anim)
-{
-    anim->run = true;
-}
-
-static void reset_animation(struct animation *anim)
-{
-    anim->cur_cycle = 0;
-    anim->cur_frame = 0;
-    anim->run = false;
-}
-
-static void update_screen_state(struct charger *charger, int64_t now)
-{
-    struct animation *batt_anim = charger->batt_anim;
-    int cur_frame;
-    int disp_time;
-
-    if (!batt_anim->run || now < charger->next_screen_transition)
-        return;
-
-    /* animation is over, blank screen and leave */
-    if (batt_anim->cur_cycle == batt_anim->num_cycles) {
-        reset_animation(batt_anim);
-        charger->next_screen_transition = -1;
-        gr_fb_blank(true);
-        LOGV("[%lld] animation done\n", now);
-        if (charger->num_supplies_online > 0)
-            request_suspend(true);
-        return;
-    }
-
-    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
-
-    /* animation starting, set up the animation */
-    if (batt_anim->cur_frame == 0) {
-        int batt_cap;
-        int ret;
-
-        LOGV("[%lld] animation starting\n", now);
-        batt_cap = get_battery_capacity(charger);
-        if (batt_cap >= 0 && batt_anim->num_frames != 0) {
-            int i;
-
-            /* find first frame given current capacity */
-            for (i = 1; i < batt_anim->num_frames; i++) {
-                if (batt_cap < batt_anim->frames[i].min_capacity)
-                    break;
-            }
-            batt_anim->cur_frame = i - 1;
-
-            /* show the first frame for twice as long */
-            disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
-        }
-
-        batt_anim->capacity = batt_cap;
-    }
-
-    /* unblank the screen  on first cycle */
-    if (batt_anim->cur_cycle == 0)
-        gr_fb_blank(false);
-
-    /* draw the new frame (@ cur_frame) */
-    redraw_screen(charger);
-
-    /* if we don't have anim frames, we only have one image, so just bump
-     * the cycle counter and exit
-     */
-    if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
-        LOGV("[%lld] animation missing or unknown battery status\n", now);
-        charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
-        batt_anim->cur_cycle++;
-        return;
-    }
-
-    /* schedule next screen transition */
-    charger->next_screen_transition = now + disp_time;
-
-    /* advance frame cntr to the next valid frame
-     * if necessary, advance cycle cntr, and reset frame cntr
-     */
-    batt_anim->cur_frame++;
-
-    /* if the frame is used for level-only, that is only show it when it's
-     * the current level, skip it during the animation.
-     */
-    while (batt_anim->cur_frame < batt_anim->num_frames &&
-           batt_anim->frames[batt_anim->cur_frame].level_only)
-        batt_anim->cur_frame++;
-    if (batt_anim->cur_frame >= batt_anim->num_frames) {
-        batt_anim->cur_cycle++;
-        batt_anim->cur_frame = 0;
-
-        /* don't reset the cycle counter, since we use that as a signal
-         * in a test above to check if animation is over
-         */
-    }
-}
-
-static int set_key_callback(int code, int value, void *data)
-{
-    struct charger *charger = data;
-    int64_t now = curr_time_ms();
-    int down = !!value;
-
-    if (code > KEY_MAX)
-        return -1;
-
-    /* ignore events that don't modify our state */
-    if (charger->keys[code].down == down)
-        return 0;
-
-    /* only record the down even timestamp, as the amount
-     * of time the key spent not being pressed is not useful */
-    if (down)
-        charger->keys[code].timestamp = now;
-    charger->keys[code].down = down;
-    charger->keys[code].pending = true;
-    if (down) {
-        LOGV("[%lld] key[%d] down\n", now, code);
-    } else {
-        int64_t duration = now - charger->keys[code].timestamp;
-        int64_t secs = duration / 1000;
-        int64_t msecs = duration - secs * 1000;
-        LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now,
-            code, secs, msecs);
-    }
-
-    return 0;
-}
-
-static void update_input_state(struct charger *charger,
-                               struct input_event *ev)
-{
-    if (ev->type != EV_KEY)
-        return;
-    set_key_callback(ev->code, ev->value, charger);
-}
-
-static void set_next_key_check(struct charger *charger,
-                               struct key_state *key,
-                               int64_t timeout)
-{
-    int64_t then = key->timestamp + timeout;
-
-    if (charger->next_key_check == -1 || then < charger->next_key_check)
-        charger->next_key_check = then;
-}
-
-static void process_key(struct charger *charger, int code, int64_t now)
-{
-    struct key_state *key = &charger->keys[code];
-    int64_t next_key_check;
-
-    if (code == KEY_POWER) {
-        if (key->down) {
-            int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
-            if (now >= reboot_timeout) {
-                LOGI("[%lld] rebooting\n", now);
-                android_reboot(ANDROID_RB_RESTART, 0, 0);
-            } else {
-                /* if the key is pressed but timeout hasn't expired,
-                 * make sure we wake up at the right-ish time to check
-                 */
-                set_next_key_check(charger, key, POWER_ON_KEY_TIME);
-            }
-        } else {
-            /* if the power key got released, force screen state cycle */
-            if (key->pending) {
-                request_suspend(false);
-                kick_animation(charger->batt_anim);
-            }
-        }
-    }
-
-    key->pending = false;
-}
-
-static void handle_input_state(struct charger *charger, int64_t now)
-{
-    process_key(charger, KEY_POWER, now);
-
-    if (charger->next_key_check != -1 && now > charger->next_key_check)
-        charger->next_key_check = -1;
-}
-
-static void handle_power_supply_state(struct charger *charger, int64_t now)
-{
-    if (charger->num_supplies_online == 0) {
-        request_suspend(false);
-        if (charger->next_pwr_check == -1) {
-            charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
-            LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
-                 now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
-        } else if (now >= charger->next_pwr_check) {
-            LOGI("[%lld] shutting down\n", now);
-            android_reboot(ANDROID_RB_POWEROFF, 0, 0);
-        } else {
-            /* otherwise we already have a shutdown timer scheduled */
-        }
-    } else {
-        /* online supply present, reset shutdown timer if set */
-        if (charger->next_pwr_check != -1) {
-            LOGI("[%lld] device plugged in: shutdown cancelled\n", now);
-            kick_animation(charger->batt_anim);
-        }
-        charger->next_pwr_check = -1;
-    }
-}
-
-static void wait_next_event(struct charger *charger, int64_t now)
-{
-    int64_t next_event = INT64_MAX;
-    int64_t timeout;
-    struct input_event ev;
-    int ret;
-
-    LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now,
-         charger->next_screen_transition, charger->next_key_check,
-         charger->next_pwr_check);
-
-    if (charger->next_screen_transition != -1)
-        next_event = charger->next_screen_transition;
-    if (charger->next_key_check != -1 && charger->next_key_check < next_event)
-        next_event = charger->next_key_check;
-    if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
-        next_event = charger->next_pwr_check;
-
-    if (next_event != -1 && next_event != INT64_MAX)
-        timeout = max(0, next_event - now);
-    else
-        timeout = -1;
-    LOGV("[%lld] blocking (%lld)\n", now, timeout);
-    ret = ev_wait((int)timeout);
-    if (!ret)
-        ev_dispatch();
-}
-
-static int input_callback(int fd, short revents, void *data)
-{
-    struct charger *charger = data;
-    struct input_event ev;
-    int ret;
-
-    ret = ev_get_input(fd, revents, &ev);
-    if (ret)
-        return -1;
-    update_input_state(charger, &ev);
-    return 0;
-}
-
-static void event_loop(struct charger *charger)
-{
-    int ret;
-
-    while (true) {
-        int64_t now = curr_time_ms();
-
-        LOGV("[%lld] event_loop()\n", now);
-        handle_input_state(charger, now);
-        handle_power_supply_state(charger, now);
-
-        /* do screen update last in case any of the above want to start
-         * screen transitions (animations, etc)
-         */
-        update_screen_state(charger, now);
-
-        wait_next_event(charger, now);
-    }
-}
-
-int main(int argc, char **argv)
-{
-    int ret;
-    struct charger *charger = &charger_state;
-    int64_t now = curr_time_ms() - 1;
-    int fd;
-    int i;
-
-    list_init(&charger->supplies);
-
-    klog_init();
-    klog_set_level(CHARGER_KLOG_LEVEL);
-
-    dump_last_kmsg();
-
-    LOGI("--------------- STARTING CHARGER MODE ---------------\n");
-
-    gr_init();
-    gr_font_size(&char_width, &char_height);
-
-    ev_init(input_callback, charger);
-
-    fd = uevent_open_socket(64*1024, true);
-    if (fd >= 0) {
-        fcntl(fd, F_SETFL, O_NONBLOCK);
-        ev_add_fd(fd, uevent_callback, charger);
-    }
-    charger->uevent_fd = fd;
-    coldboot(charger, "/sys/class/power_supply", "add");
-
-    ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
-    if (ret < 0) {
-        LOGE("Cannot load image\n");
-        charger->surf_unknown = NULL;
-    }
-
-    for (i = 0; i < charger->batt_anim->num_frames; i++) {
-        struct frame *frame = &charger->batt_anim->frames[i];
-
-        ret = res_create_surface(frame->name, &frame->surface);
-        if (ret < 0) {
-            LOGE("Cannot load image %s\n", frame->name);
-            /* TODO: free the already allocated surfaces... */
-            charger->batt_anim->num_frames = 0;
-            charger->batt_anim->num_cycles = 1;
-            break;
-        }
-    }
-
-    ev_sync_key_state(set_key_callback, charger);
-
-#ifndef CHARGER_DISABLE_INIT_BLANK
-    gr_fb_blank(true);
-#endif
-
-    charger->next_screen_transition = now - 1;
-    charger->next_key_check = -1;
-    charger->next_pwr_check = -1;
-    reset_animation(charger->batt_anim);
-    kick_animation(charger->batt_anim);
-
-    event_loop(charger);
-
-    return 0;
-}
diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk
index 76b28e2..0f32dbf 100644
--- a/fastbootd/Android.mk
+++ b/fastbootd/Android.mk
@@ -16,23 +16,89 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_C_INCLUDES := \
+    external/openssl/include \
+    external/mdnsresponder/mDNSShared \
+    $(LOCAL_PATH)/include \
+    external/zlib/ \
+
 LOCAL_SRC_FILES := \
     config.c \
     commands.c \
+    commands/boot.c \
+    commands/flash.c \
+    commands/partitions.c \
+    commands/virtual_partitions.c \
     fastbootd.c \
     protocol.c \
+    network_discovery.c \
+    socket_client.c \
+    secure.c \
     transport.c \
-    usb_linux_client.c
+    transport_socket.c \
+    trigger.c \
+    usb_linux_client.c \
+    utils.c \
 
 LOCAL_MODULE := fastbootd
 LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter -DFLASH_CERT
+LOCAL_LDFLAGS := -ldl
+
+LOCAL_SHARED_LIBRARIES := \
+    libhardware \
+    libcrypto \
+    libhardware_legacy \
+    libmdnssd
+
+LOCAL_STATIC_LIBRARIES := \
+    libsparse_static \
+    libc \
+    libcutils \
+    libz
+
+#LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    external/zlib/
+
+LOCAL_SRC_FILES := \
+    commands/partitions.c \
+    other/gptedit.c \
+    utils.c
+
+LOCAL_MODULE := gptedit
+LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
 
 LOCAL_STATIC_LIBRARIES := \
     libsparse_static \
     libc \
-    libcutils
+    libcutils \
+    libz
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+    $(EXTRA_STATIC_LIBS) \
+    libcutils
+
+LOCAL_SRC_FILES := \
+    other/vendor_trigger.c
+
+LOCAL_MODULE := libvendortrigger.default
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/fastbootd/commands.c b/fastbootd/commands.c
index 252f655..d8a601f 100644
--- a/fastbootd/commands.c
+++ b/fastbootd/commands.c
@@ -32,114 +32,304 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/reboot.h>
+#include <fcntl.h>
 
 #include "bootimg.h"
+#include "commands/boot.h"
+#include "commands/flash.h"
+#include "commands/partitions.h"
+#include "commands/virtual_partitions.h"
 #include "debug.h"
 #include "protocol.h"
+#include "trigger.h"
+#include "utils.h"
+
+#define ATAGS_LOCATION "/proc/atags"
 
 static void cmd_boot(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
+    int sz, atags_sz, new_atags_sz;
+    int rv;
     unsigned kernel_actual;
     unsigned ramdisk_actual;
-    static struct boot_img_hdr hdr;
-    char *ptr = ((char*) data);
+    unsigned second_actual;
+    void *kernel_ptr;
+    void *ramdisk_ptr;
+    void *second_ptr;
+    struct boot_img_hdr *hdr;
+    char *ptr = NULL;
+    char *atags_ptr = NULL;
+    char *new_atags = NULL;
+    int data_fd = 0;
 
-    if (sz < sizeof(hdr)) {
+    D(DEBUG, "cmd_boot %s\n", arg);
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    atags_ptr = read_atags(ATAGS_LOCATION, &atags_sz);
+    if (atags_ptr == NULL) {
+        fastboot_fail(phandle, "atags read error");
+        goto error;
+    }
+
+    // TODO: With cms we can also verify partition name included as
+    // cms signed attribute
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need the certificate");
+        return;
+    }
+
+    sz = get_file_size(data_fd);
+
+    ptr = (char *) mmap(NULL, sz, PROT_READ,
+                        MAP_POPULATE | MAP_PRIVATE, data_fd, 0);
+
+    hdr = (struct boot_img_hdr *) ptr;
+
+    if (ptr == MAP_FAILED) {
+        fastboot_fail(phandle, "internal fastbootd error");
+        goto error;
+    }
+
+    if ((size_t) sz < sizeof(*hdr)) {
         fastboot_fail(phandle, "invalid bootimage header");
-        return;
+        goto error;
     }
 
-    memcpy(&hdr, data, sizeof(hdr));
+    kernel_actual = ROUND_TO_PAGE(hdr->kernel_size, hdr->page_size);
+    ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, hdr->page_size);
+    second_actual = ROUND_TO_PAGE(hdr->second_size, hdr->page_size);
 
-    /* ensure commandline is terminated */
-    hdr.cmdline[BOOT_ARGS_SIZE-1] = 0;
+    new_atags = (char *) create_atags((unsigned *) atags_ptr, atags_sz, hdr, &new_atags_sz);
 
-    kernel_actual = ROUND_TO_PAGE(hdr.kernel_size);
-    ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size);
+    if (new_atags == NULL) {
+        fastboot_fail(phandle, "atags generate error");
+        goto error;
+    }
+    if (new_atags_sz > 0x4000) {
+        fastboot_fail(phandle, "atags file to large");
+        goto error;
+    }
 
-    if (2048 + kernel_actual + ramdisk_actual < sz) {
+    if ((int) (hdr->page_size + kernel_actual + ramdisk_actual) < sz) {
         fastboot_fail(phandle, "incomplete bootimage");
-        return;
+        goto error;
     }
 
-    /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size);
-    memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/
+    kernel_ptr = (void *)((unsigned) ptr + hdr->page_size);
+    ramdisk_ptr = (void *)((unsigned) kernel_ptr + kernel_actual);
+    second_ptr = (void *)((unsigned) ramdisk_ptr + ramdisk_actual);
+
+    D(INFO, "preparing to boot");
+    // Prepares boot physical address. Addresses from header are ignored
+    rv = prepare_boot_linux(hdr->kernel_addr, kernel_ptr, kernel_actual,
+                            hdr->ramdisk_addr, ramdisk_ptr, ramdisk_actual,
+                            hdr->second_addr, second_ptr, second_actual,
+                            hdr->tags_addr, new_atags, ROUND_TO_PAGE(new_atags_sz, hdr->page_size));
+    if (rv < 0) {
+        fastboot_fail(phandle, "kexec prepare failed");
+        goto error;
+    }
 
     fastboot_okay(phandle, "");
-    udc_stop();
 
+    free(atags_ptr);
+    munmap(ptr, sz);
+    free(new_atags);
+    close(data_fd);
 
-    /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR,
-           (const char*) hdr.cmdline, LINUX_MACHTYPE,
-           (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/
-#endif
+    D(INFO, "Kexec going to reboot");
+    reboot(LINUX_REBOOT_CMD_KEXEC);
+
+    fastboot_fail(phandle, "reboot error");
+
+    return;
+
+error:
+
+    if (atags_ptr != NULL)
+        free(atags_ptr);
+    if (ptr != NULL)
+        munmap(ptr, sz);
+
 }
 
 static void cmd_erase(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
+    int partition_fd;
+    char path[PATH_MAX];
+    D(DEBUG, "cmd_erase %s\n", arg);
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (path == NULL) {
+        fastboot_fail(phandle, "Couldn't find partition");
         return;
     }
 
-    if (flash_erase(ptn)) {
+    partition_fd = flash_get_partiton(path);
+    if (partition_fd < 0) {
+        fastboot_fail(phandle, "partiton file does not exists");
+    }
+
+    if (flash_erase(partition_fd)) {
+        fastboot_fail(phandle, "failed to erase partition");
+        flash_close(partition_fd);
+        return;
+    }
+
+    if (flash_close(partition_fd) < 0) {
+        D(ERR, "could not close device %s", strerror(errno));
         fastboot_fail(phandle, "failed to erase partition");
         return;
     }
     fastboot_okay(phandle, "");
-#endif
+}
+
+static int GPT_header_location() {
+    const char *location_str = fastboot_getvar("gpt_sector");
+    char *str;
+    int location;
+
+    if (!strcmp("", location_str)) {
+        D(INFO, "GPT location not specified using second sector");
+        return 1;
+    }
+    else {
+        location = strtoul(location_str, &str, 10);
+        D(INFO, "GPT location specified as %d", location);
+
+        if (*str != '\0')
+            return -1;
+
+        return location - 1;
+    }
+}
+
+static void cmd_gpt_layout(struct protocol_handle *phandle, const char *arg) {
+    struct GPT_entry_table *oldtable;
+    int location;
+    struct GPT_content content;
+    const char *device;
+    device = fastboot_getvar("blockdev");
+
+    if (!strcmp(device, "")) {
+        fastboot_fail(phandle, "blockdev not defined in config file");
+        return;
+    }
+
+    //TODO: add same verification as in cmd_flash
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no layout file");
+        return;
+    }
+
+    location = GPT_header_location();
+    oldtable = GPT_get_device(device, location);
+
+    GPT_default_content(&content, oldtable);
+    if (oldtable == NULL)
+        D(WARN, "Could not get old gpt table");
+    else
+        GPT_release_device(oldtable);
+
+    if (!GPT_parse_file(phandle->download_fd, &content)) {
+        fastboot_fail(phandle, "Could not parse partition config file");
+        return;
+    }
+
+    if (trigger_gpt_layout(&content)) {
+        fastboot_fail(phandle, "Vendor forbids this opperation");
+        GPT_release_content(&content);
+        return;
+    }
+
+    if (!GPT_write_content(device, &content)) {
+        fastboot_fail(phandle, "Unable to write gpt file");
+        GPT_release_content(&content);
+        return;
+    }
+
+    GPT_release_content(&content);
+    fastboot_okay(phandle, "");
 }
 
 static void cmd_flash(struct protocol_handle *phandle, const char *arg)
 {
-#if 0
-    struct ptentry *ptn;
-    struct ptable *ptable;
-    unsigned extra = 0;
+    int partition;
+    uint64_t sz;
+    char data[BOOT_MAGIC_SIZE];
+    char path[PATH_MAX];
+    ssize_t header_sz = 0;
+    int data_fd = 0;
 
-    ptable = flash_get_ptable();
-    if (ptable == NULL) {
+    D(DEBUG, "cmd_flash %s\n", arg);
+
+    if (try_handle_virtual_partition(phandle, arg)) {
+        return;
+    }
+
+    if (phandle->download_fd < 0) {
+        fastboot_fail(phandle, "no kernel file");
+        return;
+    }
+
+    if (flash_find_entry(arg, path, PATH_MAX)) {
         fastboot_fail(phandle, "partition table doesn't exist");
         return;
     }
 
-    ptn = ptable_find(ptable, arg);
-    if (ptn == NULL) {
-        fastboot_fail(phandle, "unknown partition name");
+    if (flash_validate_certificate(phandle->download_fd, &data_fd) != 1) {
+        fastboot_fail(phandle, "Access forbiden you need certificate");
         return;
     }
 
-    if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) {
+    // TODO: Maybe its goot idea to check whether the partition is bootable
+    if (!strcmp(arg, "boot") || !strcmp(arg, "recovery")) {
+        if (read_data_once(data_fd, data, BOOT_MAGIC_SIZE) < BOOT_MAGIC_SIZE) {
+            fastboot_fail(phandle, "incoming data read error, cannot read boot header");
+            return;
+        }
         if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
             fastboot_fail(phandle, "image is not a boot image");
             return;
         }
     }
 
-    if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata"))
-        extra = 64;
-    else
-        sz = ROUND_TO_PAGE(sz);
+    partition = flash_get_partiton(path);
 
-    D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name);
-    if (flash_write(ptn, extra, data, sz)) {
+    sz = get_file_size64(data_fd);
+
+    sz -= header_sz;
+
+    if (sz > get_file_size64(partition)) {
+        flash_close(partition);
+        D(WARN, "size of file too large");
+        fastboot_fail(phandle, "size of file too large");
+        return;
+    }
+
+    D(INFO, "writing %lld bytes to '%s'\n", sz, arg);
+
+    if (flash_write(partition, phandle->download_fd, sz, header_sz)) {
         fastboot_fail(phandle, "flash write failure");
         return;
     }
-    D(INFO, "partition '%s' updated\n", ptn->name);
-#endif
+    D(INFO, "partition '%s' updated\n", arg);
+
+    flash_close(partition);
+    close(data_fd);
+
     fastboot_okay(phandle, "");
 }
 
@@ -184,7 +374,6 @@
 
     phandle->download_fd = protocol_handle_download(phandle, len);
     if (phandle->download_fd < 0) {
-        //handle->state = STATE_ERROR;
         fastboot_fail(phandle, "download failed");
         return;
     }
@@ -192,14 +381,27 @@
     fastboot_okay(phandle, "");
 }
 
+static void cmd_oem(struct protocol_handle *phandle, const char *arg) {
+    const char *response = "";
+
+    //TODO: Maybe it should get download descriptor also
+    if (trigger_oem_cmd(arg, &response))
+        fastboot_fail(phandle, response);
+    else
+        fastboot_okay(phandle, response);
+}
+
 void commands_init()
 {
+    virtual_partition_register("partition-table", cmd_gpt_layout);
+
     fastboot_register("boot", cmd_boot);
     fastboot_register("erase:", cmd_erase);
     fastboot_register("flash:", cmd_flash);
     fastboot_register("continue", cmd_continue);
     fastboot_register("getvar:", cmd_getvar);
     fastboot_register("download:", cmd_download);
+    fastboot_register("oem", cmd_oem);
     //fastboot_publish("version", "0.5");
     //fastboot_publish("product", "swordfish");
     //fastboot_publish("kernel", "lk");
diff --git a/fastbootd/commands/boot.c b/fastbootd/commands/boot.c
new file mode 100644
index 0000000..8da9a28
--- /dev/null
+++ b/fastbootd/commands/boot.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "boot.h"
+#include "debug.h"
+#include "utils.h"
+#include "bootimg.h"
+
+
+#define KEXEC_ARM_ATAGS_OFFSET  0x1000
+#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+
+#define MEMORY_SIZE 0x0800000
+#define START_ADDRESS 0x44000000
+#define KERNEL_START (START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET)
+
+#define ATAG_NONE_TYPE      0x00000000
+#define ATAG_CORE_TYPE      0x54410001
+#define ATAG_RAMDISK_TYPE   0x54410004
+#define ATAG_INITRD2_TYPE   0x54420005
+#define ATAG_CMDLINE_TYPE   0x54410009
+
+#define MAX_ATAG_SIZE 0x4000
+
+struct atag_info {
+    unsigned size;
+    unsigned type;
+};
+
+struct atag_initrd2 {
+    unsigned start;
+    unsigned size;
+};
+
+struct atag_cmdline {
+    char cmdline[0];
+};
+
+struct atag {
+    struct atag_info info;
+    union {
+        struct atag_initrd2 initrd2;
+        struct atag_cmdline cmdline;
+    } data;
+};
+
+
+long kexec_load(unsigned int entry, unsigned long nr_segments,
+                struct kexec_segment *segment, unsigned long flags) {
+   return syscall(__NR_kexec_load, entry, nr_segments, segment, flags);
+}
+
+/*
+ * Prepares arguments for kexec
+ * Kernel address is not set into kernel_phys
+ * Ramdisk is set to position relative to kernel
+ */
+int prepare_boot_linux(unsigned kernel_phys, void *kernel_addr, int kernel_size,
+                       unsigned ramdisk_phys, void *ramdisk_addr, int ramdisk_size,
+                       unsigned second_phys, void *second_addr, int second_size,
+                       unsigned atags_phys, void *atags_addr, int atags_size) {
+    struct kexec_segment segment[4];
+    int segment_count = 2;
+    unsigned entry = START_ADDRESS + KEXEC_ARM_ZIMAGE_OFFSET;
+    int rv;
+    int page_size = getpagesize();
+
+    segment[0].buf = kernel_addr;
+    segment[0].bufsz = kernel_size;
+    segment[0].mem = (void *) KERNEL_START;
+    segment[0].memsz = ROUND_TO_PAGE(kernel_size, page_size);
+
+    if (kernel_size > MEMORY_SIZE - KEXEC_ARM_ZIMAGE_OFFSET) {
+        D(INFO, "Kernel image too big");
+        return -1;
+    }
+
+    segment[1].buf = atags_addr;
+    segment[1].bufsz = atags_size;
+    segment[1].mem = (void *) (START_ADDRESS + KEXEC_ARM_ATAGS_OFFSET);
+    segment[1].memsz = ROUND_TO_PAGE(atags_size, page_size);
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+
+    if (ramdisk_size != 0) {
+        segment[segment_count].buf = ramdisk_addr;
+        segment[segment_count].bufsz = ramdisk_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + ramdisk_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(ramdisk_phys, page_size);
+        ++segment_count;
+    }
+
+    D(INFO, "Ramdisk size is %d", ramdisk_size);
+    if (second_size != 0) {
+        segment[segment_count].buf = second_addr;
+        segment[segment_count].bufsz = second_size;
+        segment[segment_count].mem = (void *) (KERNEL_START + second_phys - kernel_phys);
+        segment[segment_count].memsz = ROUND_TO_PAGE(second_size, page_size);
+        entry = second_phys;
+        ++segment_count;
+    }
+
+    rv = kexec_load(entry, segment_count, segment, KEXEC_ARCH_DEFAULT);
+
+    if (rv != 0) {
+        D(INFO, "Kexec_load returned non-zero exit code: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 1;
+
+}
+
+unsigned *create_atags(unsigned *atags_position, int atag_size, const struct boot_img_hdr *hdr, int *size) {
+    struct atag *current_tag = (struct atag *) atags_position;
+    unsigned *current_tag_raw = atags_position;
+    unsigned *new_atags = malloc(ROUND_TO_PAGE(atag_size + BOOT_ARGS_SIZE * sizeof(char),
+                                               hdr->page_size));
+    //This pointer will point into the beggining of buffer free space
+    unsigned *natags_raw_buff = new_atags;
+    int new_atags_size = 0;
+    int current_size;
+    int cmdl_length;
+
+    // copy tags from current atag file
+    while (current_tag->info.type != ATAG_NONE_TYPE) {
+        switch (current_tag->info.type) {
+            case ATAG_CMDLINE_TYPE:
+            case ATAG_RAMDISK_TYPE:
+            case ATAG_INITRD2_TYPE: break;
+            default:
+                memcpy((void *)natags_raw_buff, (void *)current_tag_raw, current_tag->info.size * sizeof(unsigned));
+                natags_raw_buff += current_tag->info.size;
+                new_atags_size += current_tag->info.size;
+        }
+
+        current_tag_raw += current_tag->info.size;
+        current_tag = (struct atag *) current_tag_raw;
+
+        if (current_tag_raw >= atags_position + atag_size) {
+            D(ERR, "Critical error in atags");
+            return NULL;
+        }
+    }
+
+    // set INITRD2 tag
+    if (hdr->ramdisk_size > 0) {
+        current_size = (sizeof(struct atag_info) + sizeof(struct atag_initrd2)) / sizeof(unsigned);
+        *((struct atag *) natags_raw_buff) = (struct atag) {
+            .info = {
+                .size = current_size,
+                .type = ATAG_INITRD2_TYPE
+            },
+            .data = {
+                .initrd2 = (struct atag_initrd2) {
+                    .start = hdr->ramdisk_addr,
+                    .size = hdr->ramdisk_size
+                }
+            }
+        };
+
+        new_atags_size += current_size;
+        natags_raw_buff += current_size;
+    }
+
+    // set ATAG_CMDLINE
+    cmdl_length = strnlen((char *) hdr->cmdline, BOOT_ARGS_SIZE - 1);
+    current_size = sizeof(struct atag_info) + (1 + cmdl_length);
+    current_size = (current_size + sizeof(unsigned) - 1) / sizeof(unsigned);
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = current_size,
+            .type = ATAG_CMDLINE_TYPE
+        },
+    };
+
+    //copy cmdline and ensure that there is null character
+    memcpy(((struct atag *) natags_raw_buff)->data.cmdline.cmdline,
+           (char *) hdr->cmdline, cmdl_length);
+    ((struct atag *) natags_raw_buff)->data.cmdline.cmdline[cmdl_length] = '\0';
+
+    new_atags_size += current_size;
+    natags_raw_buff += current_size;
+
+    // set ATAG_NONE
+    *((struct atag *) natags_raw_buff) = (struct atag) {
+        .info = {
+            .size = 0,
+            .type = ATAG_NONE_TYPE
+        },
+    };
+    new_atags_size += sizeof(struct atag_info) / sizeof(unsigned);
+    natags_raw_buff += sizeof(struct atag_info) / sizeof(unsigned);
+
+    *size = new_atags_size * sizeof(unsigned);
+    return new_atags;
+}
+
+char *read_atags(const char * path, int *atags_sz) {
+    int afd = -1;
+    char *atags_ptr = NULL;
+
+    afd = open(path, O_RDONLY);
+    if (afd < 0) {
+        D(ERR, "wrong atags file");
+        return 0;
+    }
+
+    atags_ptr = (char *) malloc(MAX_ATAG_SIZE);
+    if (atags_ptr == NULL) {
+        D(ERR, "insufficient memory");
+        return 0;
+    }
+
+    *atags_sz = read(afd, atags_ptr, MAX_ATAG_SIZE);
+
+    close(afd);
+    return atags_ptr;
+}
+
diff --git a/fastbootd/commands/boot.h b/fastbootd/commands/boot.h
new file mode 100644
index 0000000..bfbf1de
--- /dev/null
+++ b/fastbootd/commands/boot.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOT_BOOT_H
+#define __FASTBOOT_BOOT_H
+
+#include <sys/cdefs.h>
+#include <uapi/linux/kexec.h>
+
+#include "bootimg.h"
+
+#define KEXEC_SEGMENT_MAX 16
+
+#define KEXEC_TYPE_DEFAULT 0
+#define KEXEC_TYPE_CRASH   1
+
+int prepare_boot_linux(unsigned, void *, int, unsigned, void *, int,
+                       unsigned, void *, int, unsigned, void *, int);
+unsigned *create_atags(unsigned *, int, const struct boot_img_hdr *, int *);
+long kexec_load(unsigned int, unsigned long, struct kexec_segment *, unsigned long);
+char *read_atags(const char *, int *);
+
+#endif /* _SYS_KEXEC_H */
+
diff --git a/fastbootd/commands/flash.c b/fastbootd/commands/flash.c
new file mode 100644
index 0000000..0954217
--- /dev/null
+++ b/fastbootd/commands/flash.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "flash.h"
+#include "protocol.h"
+#include "debug.h"
+#include "utils.h"
+#include "commands/partitions.h"
+
+#ifdef FLASH_CERT
+#include "secure.h"
+#endif
+
+#define ALLOWED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."
+#define BUFFER_SIZE 1024 * 1024
+#define MIN(a, b) (a > b ? b : a)
+
+
+int flash_find_entry(const char *name, char *out, size_t outlen)
+{
+//TODO: Assumption: All the partitions has they unique name
+
+    const char *path = fastboot_getvar("device-directory");
+    size_t length;
+    if (strcmp(path, "") == 0) {
+        D(ERR, "device-directory: not defined in config file");
+        return -1;
+    }
+
+    length = strspn(name, ALLOWED_CHARS);
+    if (length != strlen(name)) {
+        D(ERR, "Not allowed char in name: %c", name[length]);
+        return -1;
+    }
+
+    if (snprintf(out, outlen, "%s%s", path, name) >= (int) outlen) {
+        D(ERR, "Too long path to partition file");
+        return -1;
+    }
+
+    if (access(out, F_OK ) == -1) {
+        D(ERR, "could not find partition file %s", name);
+        return -1;
+    }
+
+    return 0;
+}
+
+int flash_erase(int fd)
+{
+    int64_t size;
+    size = get_block_device_size(fd);
+    D(DEBUG, "erase %llu data from %d\n", size, fd);
+
+    return wipe_block_device(fd, size);
+}
+
+int flash_write(int partition_fd, int data_fd, ssize_t size, ssize_t skip)
+{
+    ssize_t written = 0;
+    struct GPT_mapping input;
+    struct GPT_mapping output;
+
+    while (written < size) {
+        int current_size = MIN(size - written, BUFFER_SIZE);
+
+        if (gpt_mmap(&input, written + skip, current_size, data_fd)) {
+            D(ERR, "Error in writing data, unable to map data file %d at %d size %d", size, skip, current_size);
+            return -1;
+        }
+        if (gpt_mmap(&output, written, current_size, partition_fd)) {
+            D(ERR, "Error in writing data, unable to map output partition");
+            return -1;
+        }
+
+        memcpy(output.ptr, input.ptr, current_size);
+
+        gpt_unmap(&input);
+        gpt_unmap(&output);
+
+        written += current_size;
+    }
+
+    return 0;
+}
+
+#ifdef FLASH_CERT
+
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    int ret = 0;
+    const char *cert_path;
+    X509_STORE *store = NULL;
+    CMS_ContentInfo *content_info;
+    BIO *content;
+
+    cert_path = fastboot_getvar("certificate-path");
+    if (!strcmp(cert_path, "")) {
+        D(ERR, "could not find cert-key value in config file");
+        goto finish;
+    }
+
+    store = cert_store_from_path(cert_path);
+    if (store == NULL) {
+        D(ERR, "unable to create certification store");
+        goto finish;
+    }
+
+    if (cert_read(signed_fd, &content_info, &content)) {
+        D(ERR, "reading data failed");
+        goto finish;
+    }
+
+    ret = cert_verify(content, content_info, store, data_fd);
+    cert_release(content, content_info);
+
+    return ret;
+
+finish:
+    if (store != NULL)
+        cert_release_store(store);
+
+    return ret;
+}
+
+#else
+int flash_validate_certificate(int signed_fd, int *data_fd) {
+    return 1;
+}
+#endif
diff --git a/fastbootd/commands/flash.h b/fastbootd/commands/flash.h
new file mode 100644
index 0000000..5a64cab
--- /dev/null
+++ b/fastbootd/commands/flash.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_ERASE_H
+#define _FASTBOOTD_ERASE_H
+
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "debug.h"
+
+int flash_find_entry(const char *, char *, size_t);
+int flash_erase(int fd);
+
+static inline int flash_get_partiton(const char *path) {
+    return open(path, O_RDWR);
+}
+
+static inline int flash_close(int fd) {
+    return close(fd);
+}
+
+int flash_write(int partition, int data, ssize_t size, ssize_t skip);
+
+static inline ssize_t read_data_once(int fd, char *buffer, ssize_t size) {
+    ssize_t readcount = 0;
+    ssize_t len;
+
+    while ((len = TEMP_FAILURE_RETRY(read(fd, (void *) &buffer[readcount], size - readcount))) > 0) {
+        readcount += len;
+    }
+    if (len < 0) {
+        D(ERR, "Read error:%s", strerror(errno));
+        return len;
+    }
+
+    return readcount;
+}
+
+int flash_validate_certificate(int signed_fd, int *data_fd);
+
+#endif
+
diff --git a/fastbootd/commands/partitions.c b/fastbootd/commands/partitions.c
new file mode 100644
index 0000000..de80ea3
--- /dev/null
+++ b/fastbootd/commands/partitions.c
@@ -0,0 +1,771 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <endian.h>
+#include <zlib.h>
+#include <linux/hdreg.h>
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <cutils/config_utils.h>
+
+#include "partitions.h"
+#include "debug.h"
+#include "utils.h"
+#include "protocol.h"
+
+#define BLKRRPART  _IO(0x12,95) /* re-read partition table */
+#define BLKSSZGET  _IO(0x12,104)
+
+#define DIV_ROUND_UP(x, y) (((x) + (y) - 1)/(y))
+#define ALIGN(x, y) ((y) * DIV_ROUND_UP((x), (y)))
+#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))
+
+
+const uint8_t partition_type_uuid[16] = {
+    0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
+    0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
+};
+
+//TODO: There is assumption that we are using little endian
+
+static void GPT_entry_clear(struct GPT_entry_raw *entry)
+{
+    memset(entry, 0, sizeof(*entry));
+}
+
+/*
+ * returns mapped location to choosen area
+ * mapped_ptr is pointer to whole area mapped (it can be bigger then requested)
+ */
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd)
+{
+    unsigned int location_diff = location & ~PAGE_MASK;
+
+    mapping->size = ALIGN(size + location_diff, PAGE_SIZE);
+
+    uint64_t sz = get_file_size64(fd);
+    if (sz < size + location) {
+        D(ERR, "the location of mapping area is outside of the device size %lld", sz);
+        return 1;
+    }
+    location = ALIGN_DOWN(location, PAGE_SIZE);
+
+    mapping->map_ptr = mmap64(NULL, mapping->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, location);
+
+    if (mapping->map_ptr == MAP_FAILED) {
+        mapping->ptr = MAP_FAILED;
+        D(ERR, "map failed %d", (int) mapping->map_ptr);
+        return 1;
+    }
+
+    mapping->ptr = (void *)((char *) mapping->map_ptr + location_diff);
+    return 0;
+}
+
+void gpt_unmap(struct GPT_mapping *mapping) {
+    munmap(mapping->map_ptr, mapping->size);
+}
+
+
+#define LBA_ADDR(table, value)   ((uint64_t) (table)->sector_size * (value))
+
+int GPT_map_from_content(struct GPT_entry_table *table, const struct GPT_content *content)
+{
+
+    // Mapping header
+    if (gpt_mmap(&table->header_map, LBA_ADDR(table, content->header.current_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map header:%s\n", strerror(errno));
+        goto error_header;
+    }
+
+    table->header = (struct GPT_header *) table->header_map.ptr;
+
+    table->partition_table_size = ROUND_UP(content->header.entries_count * sizeof(*table->entries),
+                                           table->sector_size);
+
+    // Mapping entry table
+    if (gpt_mmap(&table->entries_map, LBA_ADDR(table, content->header.entries_lba),
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map entries");
+        goto error_signature;
+    }
+
+    table->entries = (struct GPT_entry_raw *) table->entries_map.ptr;
+
+    // Mapping secondary header
+    if (gpt_mmap(&table->sec_header_map, LBA_ADDR(table, content->header.backup_lba),
+                 table->sector_size, table->fd)) {
+        D(ERR, "unable to map backup gpt header");
+        goto error_sec_header;
+    }
+
+    // Mapping secondary entries table
+    if (gpt_mmap(&table->sec_entries_map,
+                 LBA_ADDR(table, content->header.backup_lba) - table->partition_table_size,
+                 table->partition_table_size, table->fd)) {
+        D(ERR, "unable to map secondary gpt table");
+        goto error_sec_entries;
+    }
+
+    table->second_header = (struct GPT_header *) table->sec_header_map.ptr;
+    table->second_entries = (struct GPT_entry_raw *) table->sec_entries_map.ptr;
+    table->second_valid = strcmp("EFI PART", (char *) table->second_header->signature) == 0;
+
+    return 0;
+
+error_sec_entries:
+    gpt_unmap(&table->sec_header_map);
+error_sec_header:
+    gpt_unmap(&table->entries_map);
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+int GPT_map(struct GPT_entry_table *table, unsigned header_lba)
+{
+    struct GPT_content content;
+    struct GPT_mapping mapping;
+    struct GPT_header *header;
+
+    if (gpt_mmap(&mapping, LBA_ADDR(table, header_lba), table->sector_size, table->fd)) {
+        D(ERR, "unable to map header: %s", strerror(errno));
+        goto error_header;
+    }
+
+    header = (struct GPT_header *) mapping.ptr;
+
+    if (strcmp("EFI PART", (char *) header->signature)) {
+        D(ERR, "GPT entry not valid");
+        goto error_signature;
+    }
+
+    content.header = *header;
+
+    gpt_unmap(&mapping);
+
+    return GPT_map_from_content(table, &content);
+
+error_signature:
+    gpt_unmap(&table->header_map);
+error_header:
+    return 1;
+}
+
+struct GPT_entry_table* GPT_get_device(const char *path, unsigned header_lba)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size, assuming 512");
+        table->sector_size = 512;
+    }
+
+    if (GPT_map(table, header_lba)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+static struct GPT_entry_table* GPT_get_from_content(const char *path, const struct GPT_content *content)
+{
+    struct GPT_entry_table *table;
+    size_t sector_bytes;
+
+    table = (struct GPT_entry_table *) malloc(sizeof(*table));
+    table->fd = open(path, O_RDWR);
+
+    if (table->fd < 0) {
+        D(ERR, "unable to open file %s:%s\n", path, strerror(errno));
+        return NULL;
+    }
+
+    if (!ioctl(table->fd, BLKSSZGET, &sector_bytes)) {
+        table->sector_size = (unsigned) sector_bytes;
+        D(INFO, "Got sector size %d", table->sector_size);
+    } else {
+        D(WARN, "unable to get sector size %s, assuming 512", strerror(errno));
+        table->sector_size = 512;
+    }
+
+    if (GPT_map_from_content(table, content)) {
+        D(ERR, "Could not map gpt");
+        return NULL;
+    }
+
+    return table;
+}
+
+
+void GPT_release_device(struct GPT_entry_table *table)
+{
+    gpt_unmap(&table->header_map);
+    gpt_unmap(&table->entries_map);
+    gpt_unmap(&table->sec_header_map);
+    gpt_unmap(&table->sec_entries_map);
+    close(table->fd);
+    free(table);
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry)
+{
+    struct GPT_entry_raw *current_entry = GPT_get_pointer(table, old_entry);
+
+    if (GPT_check_overlap_except(table, new_entry, current_entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (current_entry == NULL) {
+        D(ERR, "Couldn't find entry");
+        return;
+    }
+
+    *current_entry = *new_entry;
+}
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    struct GPT_entry_raw *raw = GPT_get_pointer(table, entry);
+
+    if (raw == NULL) {
+        D(ERR, "could not find entry");
+        return 1;
+    }
+    D(DEBUG, "Deleting gpt entry '%s'\n", raw->partition_guid);
+
+    // Entry in the middle of table may become empty
+    GPT_entry_clear(raw);
+
+    return 0;
+}
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    unsigned i;
+    int inserted = 0;
+    if (GPT_check_overlap(table, entry)) {
+        D(ERR, "Couldn't add overlaping partition");
+        return;
+    }
+
+    if (GPT_get_pointer(table, entry) != NULL) {
+        D(WARN, "Add entry fault, this entry already exists");
+        return;
+    }
+
+    struct GPT_entry_raw *entries = table->entries;
+
+    for (i = 0; i < table->header->entries_count; ++i) {
+        if (!entries[i].type_guid[0]) {
+            inserted = 1;
+            D(DEBUG, "inserting");
+            memcpy(&entries[i], entry, sizeof(entries[i]));
+            break;
+        }
+    }
+
+    if (!inserted) {
+        D(ERR, "Unable to find empty partion entry");
+    }
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    if (entry->partition_guid[0] != 0)
+        return GPT_get_pointer_by_guid(table, (const char *) entry->partition_guid);
+    else if (entry->name[0] != 0)
+        return GPT_get_pointer_by_UTFname(table, entry->name);
+
+    D(WARN, "Name or guid needed to find entry");
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *table, const char *name)
+{
+    int current = (int) table->header->entries_count;
+
+    for (current = current - 1; current >= 0; --current) {
+        if (strncmp((char *) name,
+                    (char *) table->entries[current].partition_guid, 16) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+int strncmp_UTF16_char(const uint16_t *s1, const char *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if (((*s1) & 127) != *s2++)
+            return (((unsigned char) ((*s1) & 127)) - *(unsigned char *)--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+int strncmp_UTF16(const uint16_t *s1, const uint16_t *s2, size_t n)
+{
+    if (n == 0)
+        return (0);
+    do {
+        if ((*s1) != *s2++)
+            return (*s1 - *--s2);
+        if (*s1++ == 0)
+            break;
+    } while (--n != 0);
+    return (0);
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *table, const char *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16_char(table->entries[current].name,
+                         (char *) name, 16) == 0) {
+                    return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+struct GPT_entry_raw *GPT_get_pointer_by_UTFname(struct GPT_entry_table *table, const uint16_t *name)
+{
+    int count = (int) table->header->entries_count;
+    int current;
+
+    for (current = 0; current < count; ++current) {
+        if (strncmp_UTF16(table->entries[current].name,
+                          name, GPT_NAMELEN) == 0) {
+                return &table->entries[current];
+        }
+    }
+
+    return NULL;
+}
+
+void GPT_sync(struct GPT_entry_table *table)
+{
+    uint32_t crc;
+
+    //calculate crc32
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->entries, table->header->entries_count * sizeof(*table->entries));
+    table->header->partition_array_checksum = crc;
+
+    table->header->header_checksum = 0;
+    crc = crc32(0, Z_NULL, 0);
+    crc = crc32(crc, (void*) table->header, table->header->header_size);
+    table->header->header_checksum = crc;
+
+    //sync secondary partion
+    if (table->second_valid) {
+        memcpy((void *)table->second_entries, (void *) table->entries, table->partition_table_size);
+        memcpy((void *)table->second_header, (void *)table->header, sizeof(*table->header));
+    }
+
+    if(!ioctl(table->fd, BLKRRPART, NULL)) {
+        D(WARN, "Unable to force kernel to refresh partition table");
+    }
+}
+
+void GPT_to_UTF16(uint16_t *to, const char *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i]) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+void GPT_from_UTF16(char *to, const uint16_t *from, int n)
+{
+    int i;
+    for (i = 0; i < (n - 1) && (to[i] = from[i] & 127) != '\0'; ++i);
+    to[i] = '\0';
+}
+
+static int GPT_check_overlap_except(struct GPT_entry_table *table,
+                                    struct GPT_entry_raw *entry,
+                                    struct GPT_entry_raw *exclude) {
+    int current = (int) table->header->entries_count;
+    int dontcheck;
+    struct GPT_entry_raw *current_entry;
+    if (entry->last_lba < entry->first_lba) {
+        D(WARN, "Start address have to be less than end address");
+        return 1;
+    }
+
+    for (current = current - 1; current >= 0; --current) {
+        current_entry = &table->entries[current];
+        dontcheck = strncmp((char *) entry->partition_guid,
+                           (char *) current_entry->partition_guid , 16) == 0;
+        dontcheck |= current_entry->type_guid[0] == 0;
+        dontcheck |= current_entry == exclude;
+
+        if (!dontcheck && ((entry->last_lba >= current_entry->first_lba &&
+                            entry->first_lba < current_entry->last_lba ))) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static int GPT_check_overlap(struct GPT_entry_table *table, struct GPT_entry_raw *entry)
+{
+    return GPT_check_overlap_except(table, entry, NULL);
+}
+
+static char *get_key_value(char *ptr, char **key, char **value)
+{
+    *key = ptr;
+    ptr = strchr(ptr, '=');
+
+    if (ptr == NULL)
+        return NULL;
+
+    *ptr++ = '\0';
+    *value = ptr;
+    ptr = strchr(ptr, ';');
+
+    if (ptr == NULL)
+        ptr = *value + strlen(*value);
+    else
+        *ptr = '\0';
+
+    *key = strip(*key);
+    *value = strip(*value);
+
+    return ptr;
+}
+
+//TODO: little endian?
+static int add_key_value(const char *key, const char *value, struct GPT_entry_raw *entry)
+{
+    char *endptr;
+    if (!strcmp(key, "type")) {
+        strncpy((char *) entry->type_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "guid")) {
+        strncpy((char *) entry->partition_guid, value, 16);
+        entry->type_guid[15] = 0;
+    }
+    else if (!strcmp(key, "firstlba")) {
+        entry->first_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "lastlba")) {
+        entry->last_lba = strtoul(value, &endptr, 10);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "flags")) {
+        entry->flags = strtoul(value, &endptr, 16);
+        if (*endptr != '\0') goto error;
+    }
+    else if (!strcmp(key, "name")) {
+        GPT_to_UTF16(entry->name, value, GPT_NAMELEN);
+    }
+    else {
+        goto error;
+    }
+
+    return 0;
+
+error:
+    D(ERR, "Could not find key or parse value: %s,%s", key, value);
+    return 1;
+}
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry)
+{
+    char *ptr = string;
+    char *key, *value;
+
+    while ((ptr = get_key_value(ptr, &key, &value)) != NULL) {
+        if (add_key_value(key, value, entry)) {
+            D(WARN, "key or value not valid: %s %s", key, value);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void entry_set_guid(int n, uint8_t *guid)
+{
+    int fd;
+    fd = open("/dev/urandom", O_RDONLY);
+    read(fd, guid, 16);
+    close(fd);
+
+    //rfc4122
+    guid[8] = (guid[8] & 0x3F) | 0x80;
+    guid[7] = (guid[7] & 0x0F) | 0x40;
+}
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table)
+{
+    if (table != NULL) {
+        memcpy(&content->header, table->header, sizeof(content->header));
+        content->header.header_size = sizeof(content->header);
+        content->header.entry_size = sizeof(struct GPT_entry_raw);
+    }
+    else {
+        D(WARN, "Could not locate old gpt table, using default values");
+        memset(&content->header, 0, sizeof(content->header) / sizeof(int));
+        content->header = (struct GPT_header) {
+            .revision = 0x10000,
+            .header_size = sizeof(content->header),
+            .header_checksum = 0,
+            .reserved_zeros = 0,
+            .current_lba = 1,
+            .backup_lba = 1,
+            .entry_size = sizeof(struct GPT_entry_raw),
+            .partition_array_checksum = 0
+        };
+        strncpy((char *)content->header.signature, "EFI PART", 8);
+        strncpy((char *)content->header.disk_guid, "ANDROID MMC DISK", 16);
+    }
+}
+
+static int get_config_uint64(cnode *node, uint64_t *ptr, const char *name)
+{
+    const char *tmp;
+    uint64_t val;
+    char *endptr;
+    if ((tmp = config_str(node, name, NULL))) {
+        val = strtoull(tmp, &endptr, 10);
+        if (*endptr != '\0') {
+            D(WARN, "Value for %s is not a number: %s", name, tmp);
+            return 1;
+        }
+        *ptr = val;
+        return 0;
+    }
+    return 1;
+}
+
+static int get_config_string(cnode *node, char *ptr, int max_len, const char *name)
+{
+    size_t begin, end;
+    const char *value = config_str(node, name, NULL);
+    if (!value)
+        return -1;
+
+    begin = strcspn(value, "\"") + 1;
+    end = strcspn(&value[begin], "\"");
+
+    if ((int) end > max_len) {
+        D(WARN, "Identifier \"%s\" too long", value);
+        return -1;
+    }
+
+    strncpy(ptr, &value[begin], end);
+    if((int) end < max_len)
+        ptr[end] = 0;
+    return 0;
+}
+
+static void GPT_parse_header(cnode *node, struct GPT_content *content)
+{
+    get_config_uint64(node, &content->header.current_lba, "header_lba");
+    get_config_uint64(node, &content->header.backup_lba, "backup_lba");
+    get_config_uint64(node, &content->header.first_usable_lba, "first_lba");
+    get_config_uint64(node, &content->header.last_usable_lba, "last_lba");
+    get_config_uint64(node, &content->header.entries_lba, "entries_lba");
+    get_config_string(node, (char *) content->header.disk_guid, 16, "guid");
+}
+
+static int GPT_parse_partitions(cnode *node, struct GPT_content *content)
+{
+    cnode *current;
+    int i;
+    uint64_t partition_size;
+    struct GPT_entry_raw *entry;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i) {
+        entry = &content->entries[i];
+        entry_set_guid(i, content->entries[i].partition_guid);
+        memcpy(&content->entries[i].type_guid, partition_type_uuid, 16);
+        if (get_config_uint64(current, &entry->first_lba, "first_lba")) {
+            D(ERR, "first_lba not specified");
+            return 1;
+        }
+        if (get_config_uint64(current, &partition_size, "partition_size")) {
+            D(ERR, "partition_size not specified");
+            return 1;
+        }
+        if (config_str(current, "system", NULL)) {
+            entry->flags |= GPT_FLAG_SYSTEM;
+        }
+        if (config_str(current, "bootable", NULL)) {
+            entry->flags |= GPT_FLAG_BOOTABLE;
+        }
+        if (config_str(current, "readonly", NULL)) {
+            entry->flags |= GPT_FLAG_READONLY;
+        }
+        if (config_str(current, "automount", NULL)) {
+            entry->flags |= GPT_FLAG_DOAUTOMOUNT;
+        }
+
+        get_config_uint64(current, &content->entries[i].flags, "flags");
+        content->entries[i].last_lba = content->entries[i].first_lba + partition_size - 1;
+        GPT_to_UTF16(content->entries[i].name, current->name, 16);
+    }
+    return 0;
+}
+
+static inline int cnode_count(cnode *node)
+{
+    int i;
+    cnode *current;
+    for (i = 0, current = node->first_child; current; current = current->next, ++i)
+        ;
+    return i;
+}
+
+
+static int GPT_parse_cnode(cnode *root, struct GPT_content *content)
+{
+    cnode *partnode;
+
+    if (!(partnode = config_find(root, "partitions"))) {
+        D(ERR, "Could not find partition table");
+        return 0;
+    }
+
+    GPT_parse_header(root, content);
+
+    content->header.entries_count = cnode_count(partnode);
+    content->entries = malloc(content->header.entries_count * sizeof(struct GPT_entry_raw));
+
+    if (GPT_parse_partitions(partnode, content)) {
+        D(ERR, "Could not parse partitions");
+        return 0;
+    }
+
+    return 1;
+}
+
+int GPT_parse_file(int fd, struct GPT_content *content)
+{
+    char *data;
+    int size;
+    int ret;
+    cnode *root = config_node("", "");
+
+    size = get_file_size(fd);
+    data = (char *) mmap(NULL, size + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+
+    if (data == NULL) {
+        if (size == 0)
+            D(ERR, "config file empty");
+        else
+            D(ERR, "Out of memory");
+        return 0;
+    }
+
+    data[size - 1] = 0;
+    config_load(root, data);
+
+    if (root->first_child == NULL) {
+        D(ERR, "Could not read config file");
+        return 0;
+    }
+
+    ret = GPT_parse_cnode(root, content);
+    munmap(data, size);
+    return ret;
+}
+
+void GPT_release_content(struct GPT_content *content)
+{
+    free(content->entries);
+}
+
+int GPT_write_content(const char *device, struct GPT_content *content)
+{
+    struct GPT_entry_table *maptable;
+
+    maptable = GPT_get_from_content(device, content);
+    if (maptable == NULL) {
+        D(ERR, "could not map device");
+        return 0;
+    }
+
+    memcpy(maptable->header, &content->header, sizeof(*maptable->header));
+    memcpy(maptable->entries, content->entries,
+           content->header.entries_count * sizeof(*maptable->entries));
+
+    GPT_sync(maptable);
+    GPT_release_device(maptable);
+
+    return 1;
+}
+
diff --git a/fastbootd/commands/partitions.h b/fastbootd/commands/partitions.h
new file mode 100644
index 0000000..9a6a88d
--- /dev/null
+++ b/fastbootd/commands/partitions.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#ifndef __FASTBOOTD_PATITIONS_
+#define __FASTBOOTD_PATITIONS_
+
+#include <stdint.h>
+
+#define GPT_ENTRIES 128
+#define GPT_NAMELEN 36
+
+#define GPT_FLAG_SYSTEM (1ULL << 0)
+#define GPT_FLAG_BOOTABLE (1ULL << 2)
+#define GPT_FLAG_READONLY (1ULL << 60)
+#define GPT_FLAG_DOAUTOMOUNT (1ULL << 63)
+
+// it should be passed in little endian order
+struct GPT_entry_raw {
+    uint8_t type_guid[16];
+    uint8_t partition_guid[16];
+    uint64_t first_lba; // little endian
+    uint64_t last_lba;
+    uint64_t flags;
+    uint16_t name[GPT_NAMELEN]; // UTF-16 LE
+};
+
+struct GPT_mapping {
+    void *map_ptr;
+    void *ptr;
+    unsigned size;
+};
+
+struct GPT_entry_table {
+    int fd;
+
+    struct GPT_mapping header_map;
+    struct GPT_mapping entries_map;
+    struct GPT_mapping sec_header_map;
+    struct GPT_mapping sec_entries_map;
+
+    struct GPT_header *header;
+    struct GPT_entry_raw *entries;
+    struct GPT_header *second_header;
+    struct GPT_entry_raw *second_entries;
+
+    unsigned sector_size;
+    unsigned partition_table_size;
+    int second_valid;
+};
+
+struct GPT_header {
+    uint8_t signature[8];
+    uint32_t revision;
+    uint32_t header_size;
+    uint32_t header_checksum;
+    uint32_t reserved_zeros;
+    uint64_t current_lba;
+    uint64_t backup_lba;
+    uint64_t first_usable_lba;
+    uint64_t last_usable_lba;
+    uint8_t disk_guid[16];
+    uint64_t entries_lba;
+    uint32_t entries_count;
+    uint32_t entry_size;
+    uint32_t partition_array_checksum;
+    // the rest should be filled with zeros
+} __attribute__((packed));
+
+struct GPT_content {
+    struct GPT_header header;
+    struct GPT_entry_raw *entries;
+};
+
+
+struct GPT_entry_table* GPT_get_device(const char *, unsigned lba);
+
+void GPT_release_device(struct GPT_entry_table *);
+
+void GPT_edit_entry(struct GPT_entry_table *table,
+                    struct GPT_entry_raw *old_entry,
+                    struct GPT_entry_raw *new_entry);
+
+int GPT_delete_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+void GPT_add_entry(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+
+struct GPT_entry_raw *GPT_get_pointer(struct GPT_entry_table *table, struct GPT_entry_raw *entry);
+struct GPT_entry_raw *GPT_get_pointer_by_guid(struct GPT_entry_table *, const char *);
+struct GPT_entry_raw *GPT_get_pointer_by_name(struct GPT_entry_table *, const char *);
+
+//Use after every edit operation
+void GPT_sync();
+
+void GPT_to_UTF16(uint16_t *, const char *, int );
+void GPT_from_UTF16(char *, const uint16_t *, int);
+
+int GPT_parse_entry(char *string, struct GPT_entry_raw *entry);
+
+void GPT_default_content(struct GPT_content *content, struct GPT_entry_table *table);
+
+void GPT_release_content(struct GPT_content *content);
+
+int GPT_parse_file(int fd, struct GPT_content *content);
+
+int GPT_write_content(const char *device, struct GPT_content *content);
+
+int gpt_mmap(struct GPT_mapping *mapping, uint64_t location, int size, int fd);
+
+void gpt_unmap(struct GPT_mapping *mapping);
+
+#endif
diff --git a/fastbootd/commands/virtual_partitions.c b/fastbootd/commands/virtual_partitions.c
new file mode 100644
index 0000000..813f485
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "commands/virtual_partitions.h"
+#include "debug.h"
+
+static struct virtual_partition *partitions = NULL;
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg)
+{
+    struct virtual_partition *current;
+
+    for (current = partitions; current != NULL; current = current->next) {
+        if (!strcmp(current->name, arg)) {
+            current->handler(handle, arg);
+        }
+    }
+
+    return 0;
+}
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg))
+{
+    struct virtual_partition *new;
+    new = malloc(sizeof(*new));
+    if (new) {
+        new->name = name;
+        new->handler = handler;
+        new->next = partitions;
+        partitions = new;
+    }
+    else {
+        D(ERR, "Out of memory");
+    }
+}
diff --git a/fastbootd/commands/virtual_partitions.h b/fastbootd/commands/virtual_partitions.h
new file mode 100644
index 0000000..88df71e
--- /dev/null
+++ b/fastbootd/commands/virtual_partitions.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef FASTBOOTD_VIRTUAL_PARTITIONS_H
+#define FASTBOOTD_VIRTUAL_PARTITIONS_H
+
+#include "protocol.h"
+
+struct virtual_partition {
+    struct virtual_partition *next;
+    const char *name;
+    void (*handler)(struct protocol_handle *phandle, const char *arg);
+};
+
+int try_handle_virtual_partition(struct protocol_handle *handle, const char *arg);
+
+void virtual_partition_register(
+        const char * name,
+        void (*handler)(struct protocol_handle *phandle, const char *arg));
+
+#endif
diff --git a/fastbootd/config.c b/fastbootd/config.c
index b8503fd..fe6da69 100644
--- a/fastbootd/config.c
+++ b/fastbootd/config.c
@@ -35,29 +35,13 @@
 #include <sys/types.h>
 
 #include "protocol.h"
+#include "utils.h"
 
 #include "debug.h"
 
 // TODO: change config path
 #define CONFIG_PATH "/data/fastboot.cfg"
 
-static char *strip(char *str)
-{
-    int n;
-
-    n = strspn(str, " \t");
-    str += n;
-
-    for (n = strlen(str) - 1; n >= 0; n--) {
-        if (str[n] == ' ' || str[n] == '\t')
-            str[n] = '\0';
-        else
-            break;
-    }
-
-    return str;
-}
-
 static int config_parse_line(char *line)
 {
     char *c;
diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c
index 98df0db..2b51b33 100644
--- a/fastbootd/fastbootd.c
+++ b/fastbootd/fastbootd.c
@@ -16,30 +16,85 @@
 
 #include <stdio.h>
 #include <unistd.h>
-
 #include <cutils/klog.h>
+#include <getopt.h>
+#include <stdlib.h>
 
 #include "debug.h"
+#include "trigger.h"
+#include "socket_client.h"
+#include "secure.h"
 
 unsigned int debug_level = DEBUG;
 
 void commands_init();
 void usb_init();
 void config_init();
+int transport_socket_init();
+int network_discovery_init();
+void ssh_server_start();
 
 int main(int argc, char **argv)
 {
+    int socket_client = 0;
+    int c;
+    int network = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"socket", no_argument, 0, 'S'},
+        {"nonetwork", no_argument, 0, 'n'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "Sn", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'S':
+            socket_client = 1;
+            break;
+        case 'n':
+            network = 0;
+            break;
+        case '?':
+            return 1;
+        default:
+            return 0;
+        }
+    }
+
     (void)argc;
     (void)argv;
 
     klog_init();
     klog_set_level(6);
 
-    config_init();
-    commands_init();
-    usb_init();
-    while (1) {
-        sleep(1);
+    if (socket_client) {
+        //TODO: Shouldn't we change current tty into raw mode?
+        run_socket_client();
+    }
+    else {
+        cert_init_crypto();
+        config_init();
+        load_trigger();
+        commands_init();
+        usb_init();
+
+        if (network) {
+            if (!transport_socket_init())
+                exit(1);
+            ssh_server_start();
+            network_discovery_init();
+        }
+
+        while (1) {
+            sleep(1);
+        }
     }
     return 0;
 }
diff --git a/fastbootd/include/vendor_trigger.h b/fastbootd/include/vendor_trigger.h
new file mode 100644
index 0000000..51204fa
--- /dev/null
+++ b/fastbootd/include/vendor_trigger.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __VENDOR_TRIGGER_H_
+#define __VENDOR_TRIGGER_H_
+
+#define TRIGGER_MODULE_ID "fastbootd"
+#include <hardware/hardware.h>
+
+__BEGIN_DECLS
+
+struct GPT_entry_raw;
+struct GPT_content;
+
+/*
+ * Structer with function pointers may become longer in the future
+ */
+
+struct vendor_trigger_t {
+    struct hw_device_t common;
+
+    /*
+     * This function runs at the beggining and shoud never be changed
+     *
+     * version is number parameter indicating version on the fastbootd side
+     * libversion is version indicateing version of the library version
+     *
+     * returns 0 if it can cooperate with the current version and 1 in opposite
+     */
+    int (*check_version)(const int version, int *libversion);
+
+
+    /*
+     * Return value -1 forbid the action from the vendor site and sets errno
+     */
+    int (* gpt_layout)(struct GPT_content *);
+    int (* oem_cmd)(const char *arg, const char **response);
+};
+
+__END_DECLS
+
+#endif
diff --git a/fastbootd/network_discovery.c b/fastbootd/network_discovery.c
new file mode 100644
index 0000000..1cd3e48
--- /dev/null
+++ b/fastbootd/network_discovery.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dns_sd.h>
+#include <cutils/properties.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "network_discovery.h"
+#include "utils.h"
+
+#define MDNS_SERVICE_NAME "mdnsd"
+#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
+#define FASTBOOTD_TYPE "_fastbootd._tcp"
+#define FASTBOOTD_DOMAIN "local."
+#define FASTBOOTD_NAME "fastbootd"
+
+
+static void reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+    const char *name, const char *regtype, const char *domain, void *context)
+{
+    (void)sdref;    // Unused
+    (void)flags;    // Unused
+    (void)context;  // Unused
+    if (errorCode == kDNSServiceErr_ServiceNotRunning) {
+        fprintf(stderr, "Error code %d\n", errorCode);
+    }
+
+
+    printf("Got a reply for service %s.%s%s: ", name, regtype, domain);
+
+    if (errorCode == kDNSServiceErr_NoError)
+    {
+        if (flags & kDNSServiceFlagsAdd)
+            printf("Name now registered and active\n");
+        else
+            printf("Name registration removed\n");
+        if (errorCode == kDNSServiceErr_NameConflict)
+            printf("Name in use, please choose another\n");
+        else
+            printf("Error %d\n", errorCode);
+
+        if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+    }
+}
+
+static int register_service() {
+    DNSServiceRef sdref = NULL;
+    const char *domain = FASTBOOTD_DOMAIN;
+    const char *type = FASTBOOTD_TYPE;
+    const char *host = NULL;
+    char name[PROP_VALUE_MAX];
+    uint16_t port = 22;
+    int flags = 0;
+    DNSServiceErrorType result;
+    property_get("ro.serialno", name, "");
+    if (!strcmp(name, "")) {
+        D(ERR, "No property serialno");
+        return -1;
+    }
+
+    result = DNSServiceRegister(&sdref, flags, kDNSServiceInterfaceIndexAny,
+                       name, type, domain, host, port,
+                       0, NULL, reg_reply, NULL);
+    if (result != kDNSServiceErr_NoError) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+    return 0;
+}
+
+
+int network_discovery_init()
+{
+    D(INFO, "Starting discovery");
+    if (service_start(MDNS_SERVICE_NAME)) {
+        D(ERR, "Unable to start discovery");
+        return -1;
+    }
+
+    if (register_service()) {
+        D(ERR, "Unable to register service");
+        return -1;
+    }
+
+    return 0;
+}
+
diff --git a/fastbootd/network_discovery.h b/fastbootd/network_discovery.h
new file mode 100644
index 0000000..75fda63
--- /dev/null
+++ b/fastbootd/network_discovery.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_NETWORK_DISCOVERY_H
+#define _FASTBOOTD_NETWORK_DISCOVERY_H
+
+int network_discovery_init();
+
+#endif
diff --git a/fastbootd/other/gptedit.c b/fastbootd/other/gptedit.c
new file mode 100644
index 0000000..16d34a5
--- /dev/null
+++ b/fastbootd/other/gptedit.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+
+#include <cutils/klog.h>
+
+#include "commands/partitions.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+//TODO: add tool to generate config file
+
+void usage() {
+    fprintf(stderr,
+            "usage: test_gpt [ <option> ] <file>\n"
+            "\n"
+            "options:\n"
+            "  -p                                       print partitions\n"
+            "  -c                                       print config file\n"
+            "  -a                                       adds new partition\n"
+            "  -d                                       deletes partition (-o needed)\n"
+            "\n"
+            "  -n name@startlba,endlba                  new partition detail\n"
+            "  -o                                       old partition name\n"
+            "  -t                                       type guid\n"
+            "  -g                                       partition guid\n"
+            "  -l gpt_location                          specyfies gpt secto\n"
+    );
+
+}
+
+void printGPT(struct GPT_entry_table *table);
+void addGPT(struct GPT_entry_table *table, const char *arg, const char *guid, const char *tguid);
+void deleteGPT(struct GPT_entry_table *table, const char *name);
+void configPrintGPT(struct GPT_entry_table *table);
+
+int main(int argc, char *argv[]) {
+    int print_cmd = 0;
+    int config_cmd = 0;
+    int add_cmd = 0;
+    int del_cmd = 0;
+    int sync_cmd = 0;
+    int c;
+    const char *new_partition = NULL;
+    const char *old_partition = NULL;
+    const char *type_guid = NULL;
+    const char *partition_guid = NULL;
+    unsigned gpt_location = 1;
+
+    klog_init();
+    klog_set_level(6);
+
+    const struct option longopts[] = {
+        {"print", no_argument, 0, 'p'},
+        {"config-print", no_argument, 0, 'c'},
+        {"add", no_argument, 0, 'a'},
+        {"del", no_argument, 0, 'd'},
+        {"new", required_argument, 0, 'n'},
+        {"old", required_argument, 0, 'o'},
+        {"type", required_argument, 0, 't'},
+        {"sync", required_argument, 0, 's'},
+        {"guid", required_argument, 0, 'g'},
+        {"location", required_argument, 0, 'l'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        c = getopt_long(argc, argv, "pcadt:g:n:o:sl:", longopts, NULL);
+        /* Alphabetical cases */
+        if (c < 0)
+            break;
+        switch (c) {
+        case 'p':
+            print_cmd = 1;
+            break;
+        case 'c':
+            config_cmd = 1;
+            break;
+        case 'a':
+            add_cmd = 1;
+            break;
+        case 'd':
+            del_cmd = 1;
+            break;
+        case 'n':
+            new_partition = optarg;
+            break;
+        case 'o':
+            old_partition = optarg;
+            break;
+        case 't':
+            type_guid = optarg;
+        case 'g':
+            partition_guid = optarg;
+            break;
+        case 's':
+            sync_cmd = 1;
+            break;
+        case 'l':
+            gpt_location = strtoul(optarg, NULL, 10);
+            fprintf(stderr, "Got offset as %d", gpt_location);
+            break;
+        case '?':
+            return 1;
+        default:
+            abort();
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1) {
+        usage();
+        return 1;
+    }
+
+    const char *path = argv[0];
+    struct GPT_entry_table *table = GPT_get_device(path, gpt_location);
+    if (table == NULL) {
+        fprintf(stderr, "unable to get GPT table from %s\n", path);
+        return 1;
+    }
+
+    if (add_cmd)
+        addGPT(table, new_partition, partition_guid, type_guid);
+    if (del_cmd)
+        deleteGPT(table, old_partition);
+    if (print_cmd)
+        printGPT(table);
+    if (config_cmd)
+        configPrintGPT(table);
+    if (sync_cmd)
+        GPT_sync(table);
+
+    GPT_release_device(table);
+
+    return 0;
+}
+
+void printGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+
+    printf("ptn  start block   end block     name\n");
+    printf("---- ------------- -------------\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+        printf("#%03d %13lld %13lld %s\n",
+            n + 1, entry->first_lba, entry->last_lba, name);
+    }
+}
+
+void configPrintGPT(struct GPT_entry_table *table) {
+    struct GPT_entry_raw *entry = table->entries;
+    unsigned n, m;
+    char name[GPT_NAMELEN + 1];
+    char temp_guid[17];
+    temp_guid[16] = 0;
+
+    printf("header_lba %lld\n", table->header->current_lba);
+    printf("backup_lba %lld\n", table->header->backup_lba);
+    printf("first_lba %lld\n", table->header->first_usable_lba);
+    printf("last_lba %lld\n", table->header->last_usable_lba);
+    printf("entries_lba %lld\n", table->header->entries_lba);
+    snprintf(temp_guid, 17, "%s", table->header->disk_guid);
+    printf("guid \"%s\"", temp_guid);
+
+    printf("\npartitions {\n");
+
+    for (n = 0; n < table->header->entries_count; n++, entry++) {
+        uint64_t size = entry->last_lba - entry->first_lba + 1;
+
+        if (entry->type_guid[0] == 0)
+            continue;
+        for (m = 0; m < GPT_NAMELEN; m++) {
+            name[m] = entry->name[m] & 127;
+        }
+        name[m] = 0;
+
+        printf("    %s {\n", name);
+        snprintf(temp_guid, 17, "%s", entry->partition_guid);
+        printf("        guid \"%s\"\n", temp_guid);
+        printf("        first_lba %lld\n", entry->first_lba);
+        printf("        partition_size %lld\n", size);
+        if (entry->flags & GPT_FLAG_SYSTEM)
+            printf("        system\n");
+        if (entry->flags & GPT_FLAG_BOOTABLE)
+            printf("        bootable\n");
+        if (entry->flags & GPT_FLAG_READONLY)
+            printf("        readonly\n");
+        if (entry->flags & GPT_FLAG_DOAUTOMOUNT)
+            printf("        automount\n");
+        printf("    }\n\n");
+    }
+    printf("}\n");
+}
+
+void addGPT(struct GPT_entry_table *table, const char *str  , const char *guid, const char *tguid) {
+    char *c, *c2;
+    char *arg = malloc(strlen(str));
+    char *name = arg;
+    unsigned start, end;
+    strcpy(arg, str);
+    if (guid == NULL || tguid == NULL) {
+        fprintf(stderr, "Type guid and partion guid needed");
+        free(arg);
+        return;
+    }
+
+    c = strchr(arg, '@');
+
+    if (c == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    *c++ = '\0';
+
+    c2 = strchr(c, ',');
+
+    if (c2 == NULL) {
+        fprintf(stderr, "Wrong entry format");
+        free(arg);
+        return;
+    }
+
+    start = strtoul(c, NULL, 10);
+    *c2++ = '\0';
+    end = strtoul(c2, NULL, 10);
+
+    struct GPT_entry_raw data;
+    strncpy((char *)data.partition_guid, guid, 15);
+    data.partition_guid[15] = '\0';
+    strncpy((char *)data.type_guid, tguid, 15);
+    data.type_guid[15] = '\0';
+    GPT_to_UTF16(data.name, name, GPT_NAMELEN);
+    data.first_lba = start;
+    data.last_lba = end;
+
+    fprintf(stderr, "Adding (%d,%d) %s as, [%s, %s]", start, end, name, (char *) data.type_guid, (char *) data.partition_guid);
+    GPT_add_entry(table, &data);
+    free(arg);
+}
+
+void deleteGPT(struct GPT_entry_table *table, const char *name) {
+    struct GPT_entry_raw *entry;
+
+    if (name == NULL) {
+        fprintf(stderr, "Need partition name");
+        return;
+    }
+
+    entry = GPT_get_pointer_by_name(table, name);
+
+    if (!entry) {
+        fprintf(stderr, "Unable to find partition: %s", name);
+        return;
+    }
+    GPT_delete_entry(table, entry);
+}
+
diff --git a/fastbootd/other/partitions.sample.cfg b/fastbootd/other/partitions.sample.cfg
new file mode 100644
index 0000000..49562cf
--- /dev/null
+++ b/fastbootd/other/partitions.sample.cfg
@@ -0,0 +1,60 @@
+
+header_lba 1
+backup_lba 101
+first_lba 43
+last_lba 100
+entries_lba 2
+
+partitions {
+    #You can generate this as output from gptedit -c
+    SOS {
+        first_lba 28672
+        partition_size 16384
+    }
+
+    DTB {
+        first_lba 45056
+        partition_size 8192
+    }
+
+    LNX {
+        first_lba 53248
+        partition_size 16384
+    }
+
+    APP {
+        first_lba 69632
+        partition_size 1048576
+    }
+
+    CAC {
+        first_lba 1118208
+        partition_size 1572864
+    }
+
+    MSC {
+        first_lba 2691072
+        partition_size 4096
+    }
+
+    USP {
+        first_lba 2695168
+        partition_size 65536
+    }
+
+    MDA {
+        first_lba 2760704
+        partition_size 4096
+    }
+
+    FCT {
+        first_lba 2764800
+        partition_size 32768
+    }
+
+    UDA {
+        first_lba 2797568
+        partition_size 27975680
+    }
+}
+
diff --git a/fastbootd/other/sign/src/SignImg.java b/fastbootd/other/sign/src/SignImg.java
new file mode 100644
index 0000000..338d427
--- /dev/null
+++ b/fastbootd/other/sign/src/SignImg.java
@@ -0,0 +1,181 @@
+package signtool;
+
+import java.io.*;
+import java.util.Properties;
+import java.util.ArrayList;
+
+import javax.mail.internet.*;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.activation.MailcapCommandMap;
+import javax.activation.CommandMap;
+
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.KeyFactory;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateEncodingException;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.asn1.ASN1InputStream;    
+import org.bouncycastle.asn1.DEROutputStream;
+import org.bouncycastle.asn1.ASN1Object;
+
+
+public class SignImg {
+
+    /* It reads private key in pkcs#8 formate
+     * Conversion:
+     * openssl pkcs8 -topk8 -nocrypt -outform DER < inkey.pem > outkey.pk8
+     */
+    private static PrivateKey getPrivateKey(String path) throws IOException, FileNotFoundException, NoSuchAlgorithmException, InvalidKeySpecException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(data);
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        PrivateKey privateKey = kf.generatePrivate(kspec);
+
+        return privateKey;
+    }
+
+    private static MimeBodyPart getContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        MimeBodyPart body = new MimeBodyPart();
+
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+
+        body.setContent(data, "application/octet-stream");
+
+        return body;
+    }
+
+    private static CMSProcessableByteArray getCMSContent(String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileInputStream fis = new FileInputStream(file);
+        byte[] data = new byte[(int)file.length()];
+        fis.read(data);
+        fis.close();
+        CMSProcessableByteArray cms = new CMSProcessableByteArray(data);
+
+        return cms;
+    }
+
+    private static X509Certificate readCert(String path) throws IOException, FileNotFoundException, CertificateException {
+        File file = new File(path);
+        FileInputStream is = new FileInputStream(file);
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        Certificate cert = cf.generateCertificate(is);
+        is.close();
+
+        return (X509Certificate) cert;
+    }
+
+    private static void save(MimeBodyPart content, String path) throws IOException, FileNotFoundException, MessagingException {
+        File file = new File(path);
+        FileOutputStream os = new FileOutputStream(file);
+
+        content.writeTo(os);
+
+        os.close();
+    }
+
+    private static Store certToStore(X509Certificate certificate) throws CertificateEncodingException {
+        ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
+        certList.add(certificate);
+        return new JcaCertStore(certList);
+    }
+
+    public static void setDefaultMailcap()
+    {
+        MailcapCommandMap _mailcap =
+            (MailcapCommandMap)CommandMap.getDefaultCommandMap();
+
+        _mailcap.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
+        _mailcap.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
+        _mailcap.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
+        _mailcap.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
+        _mailcap.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");
+
+        CommandMap.setDefaultCommandMap(_mailcap);
+    } 
+
+    public static void main(String[] args) {
+        try {
+            if (args.length < 4) {
+                System.out.println("Usage: signimg data private_key certificate output");
+                return;
+            }
+            System.out.println("Signing the image");
+            setDefaultMailcap();
+
+            Security.addProvider(new BouncyCastleProvider());
+
+            PrivateKey key = getPrivateKey(args[1]);
+            System.out.println("File read sucessfully");
+
+            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+
+            CMSTypedData body = getCMSContent(args[0]);
+            System.out.println("Content read sucessfully");
+
+            X509Certificate cert = (X509Certificate) readCert(args[2]);
+            System.out.println("Certificate read sucessfully");
+
+            ContentSigner sha256Signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(key);
+
+            Store certs = certToStore(cert);
+
+            generator.addCertificates(certs);
+            generator.addSignerInfoGenerator(
+                          new JcaSignerInfoGeneratorBuilder(
+                                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
+                          .build(sha256Signer, cert));
+
+            CMSSignedData signed = generator.generate(body, true);
+            System.out.println("Signed");
+
+            Properties props = System.getProperties();
+            Session session = Session.getDefaultInstance(props, null);
+            
+            File file = new File(args[3]);
+            FileOutputStream os = new FileOutputStream(file);
+
+            ASN1InputStream asn1 = new ASN1InputStream(signed.getEncoded());
+            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
+            DEROutputStream dOut = new DEROutputStream(os); 
+            dOut.writeObject(ASN1Object.fromByteArray(signed.getEncoded()));
+
+        }
+        catch (Exception ex) {
+            System.out.println("Exception during programm execution: " + ex.getMessage());
+        }
+    }
+}
diff --git a/fastbootd/other/vendor_trigger.c b/fastbootd/other/vendor_trigger.c
new file mode 100644
index 0000000..101959b
--- /dev/null
+++ b/fastbootd/other/vendor_trigger.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+#include "vendor_trigger.h"
+#include "debug.h"
+
+unsigned int debug_level = DEBUG;
+
+static const int version = 1;
+
+int check_version(const int fastboot_version, int *libversion) {
+    *libversion = version;
+    return !(fastboot_version == version);
+}
+
+int gpt_layout(struct GPT_content *table) {
+    D(DEBUG, "message from libvendor");
+    return 0;
+}
+
+int oem_cmd(const char *arg, const char **response) {
+    D(DEBUG, "message from libvendor, oem catched request %s", arg);
+    return 0;
+}
+
+static int close_triggers(struct vendor_trigger_t *dev)
+{
+    if (dev)
+        free(dev);
+
+    return 0;
+}
+
+static int open_triggers(const struct hw_module_t *module, char const *name,
+                         struct hw_device_t **device) {
+    struct vendor_trigger_t *dev = malloc(sizeof(struct vendor_trigger_t));
+    klog_init();
+    klog_set_level(6);
+
+    memset(dev, 0, sizeof(*dev));
+    dev->common.module = (struct hw_module_t *) module;
+    dev->common.close  = (int (*)(struct hw_device_t *)) close_triggers;
+
+    dev->gpt_layout = gpt_layout;
+    dev->oem_cmd = oem_cmd;
+
+    *device = (struct hw_device_t *) dev;
+
+    return 0;
+}
+
+
+static struct hw_module_methods_t trigger_module_methods = {
+    .open = open_triggers,
+};
+
+struct hw_module_t HAL_MODULE_INFO_SYM = {
+    .tag = HARDWARE_MODULE_TAG,
+    .version_major = 1,
+    .version_minor = 0,
+    .id = TRIGGER_MODULE_ID,
+    .name = "vendor trigger library for fastbootd",
+    .author = "Google, Inc.",
+    .methods = &trigger_module_methods,
+};
+
diff --git a/fastbootd/secure.c b/fastbootd/secure.c
new file mode 100644
index 0000000..a657ad4
--- /dev/null
+++ b/fastbootd/secure.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <openssl/pem.h>
+#include <openssl/engine.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "secure.h"
+#include "debug.h"
+#include "utils.h"
+
+
+void cert_init_crypto() {
+    CRYPTO_malloc_init();
+    ERR_load_crypto_strings();
+    OpenSSL_add_all_algorithms();
+    ENGINE_load_builtin_engines();
+}
+
+X509_STORE *cert_store_from_path(const char *path) {
+
+    X509_STORE *store;
+    struct stat st;
+    X509_LOOKUP *lookup;
+
+    if (stat(path, &st)) {
+        D(ERR, "Unable to stat cert path");
+        goto error;
+    }
+
+    if (!(store = X509_STORE_new())) {
+        goto error;
+    }
+
+    if (S_ISDIR(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else if(S_ISREG(st.st_mode)) {
+        lookup = X509_STORE_add_lookup(store,X509_LOOKUP_file());
+        if (lookup == NULL)
+            goto error;
+        if (!X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM)) {
+            D(ERR, "Error loading cert directory %s", path);
+            goto error;
+        }
+    }
+    else {
+        D(ERR, "cert path is not directory or regular file");
+        goto error;
+    }
+
+    return store;
+
+error:
+    return NULL;
+}
+
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output) {
+    BIO *input;
+    *output = NULL;
+
+
+    input = BIO_new_fd(fd, BIO_NOCLOSE);
+    if (input == NULL) {
+        D(ERR, "Unable to open input");
+        goto error;
+    }
+
+    //TODO:
+    // read with d2i_CMS_bio to support DER
+    // with java or just encode data with base64
+    *content = SMIME_read_CMS(input, output);
+    if (*content == NULL) {
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Unable to parse input file: %s", ERR_lib_error_string(err));
+        goto error_read;
+    }
+
+    BIO_free(input);
+
+    return 0;
+
+error_read:
+    BIO_free(input);
+error:
+    return 1;
+}
+
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd) {
+    BIO *output_temp;
+    int ret;
+
+    *out_fd = create_temp_file();
+    if (*out_fd < 0) {
+        D(ERR, "unable to create temporary file");
+        return -1;
+    }
+
+    output_temp = BIO_new_fd(*out_fd, BIO_NOCLOSE);
+    if (output_temp == NULL) {
+        D(ERR, "unable to create temporary bio");
+        close(*out_fd);
+        return -1;
+    }
+
+    ret = CMS_verify(content_info, NULL ,store, content, output_temp, 0);
+
+    if (ret == 0) {
+        char buf[256];
+        unsigned long err = ERR_peek_last_error();
+        D(ERR, "Verification failed with reason: %s, %s", ERR_lib_error_string(err),  ERR_error_string(err, buf));
+        D(ERR, "Data used: content %d", (int) content);
+    }
+
+    ERR_clear_error();
+    ERR_remove_state(0);
+
+    BIO_free(output_temp);
+
+    return ret;
+}
+
+void cert_release(BIO *content, CMS_ContentInfo *info) {
+    BIO_free(content);
+    CMS_ContentInfo_free(info);
+}
+
diff --git a/fastbootd/secure.h b/fastbootd/secure.h
new file mode 100644
index 0000000..878a643
--- /dev/null
+++ b/fastbootd/secure.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SECURE_H
+#define _FASTBOOTD_SECURE_H
+
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/pem.h>
+#include <openssl/cms.h>
+
+void cert_init_crypto();
+
+X509_STORE *cert_store_from_path(const char*stream);
+
+static inline void cert_release_store(X509_STORE *store) {
+    X509_STORE_free(store);
+}
+
+int cert_read(int fd, CMS_ContentInfo **content, BIO **output);
+int cert_verify(BIO *content, CMS_ContentInfo *content_info, X509_STORE *store, int *out_fd);
+void cert_release(BIO *content, CMS_ContentInfo *info);
+
+#endif
diff --git a/fastbootd/socket_client.c b/fastbootd/socket_client.c
new file mode 100644
index 0000000..da636db
--- /dev/null
+++ b/fastbootd/socket_client.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <cutils/sockets.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "utils.h"
+
+#define BUFFER_SIZE 256
+
+#define STDIN_FD 0
+#define STDOUT_FD 1
+#define STDERR_FD 2
+
+void run_socket_client() {
+    int fd;
+    char buffer[BUFFER_SIZE];
+    int n;
+    struct pollfd fds[2];
+
+    fd = socket_local_client("fastbootd",
+                         ANDROID_SOCKET_NAMESPACE_RESERVED,
+                         SOCK_STREAM);
+
+    if (fd < 0) {
+        fprintf(stderr, "ERROR: Unable to open fastbootd socket\n");
+        return;
+    }
+
+    fds[0].fd = STDIN_FD;
+    fds[0].events = POLLIN;
+    fds[1].fd = fd;
+    fds[1].events = POLLIN;
+
+    while(true) {
+        if (poll(fds, 2, -1) <= 0) {
+            fprintf(stderr, "ERROR: socket error");
+            return;
+        }
+
+        if (fds[0].revents & POLLIN) {
+            if ((n = read(STDIN_FD, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(fd, buffer, n) < 0) {
+                goto error;
+            }
+        }
+
+        if (fds[1].revents & POLLIN) {
+            if ((n = read(fd, buffer, BUFFER_SIZE)) < 0) {
+                goto error;
+            }
+
+            if (bulk_write(STDOUT_FD, buffer, n) < 0) {
+                goto error;
+            }
+        }
+    }
+
+error:
+    fprintf(stderr, "Transport error\n");
+}
diff --git a/fastbootd/socket_client.h b/fastbootd/socket_client.h
new file mode 100644
index 0000000..4481ff2
--- /dev/null
+++ b/fastbootd/socket_client.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOTD_SOCKET_CLIENT_H
+#define _FASTBOOTD_SOCKET_CLIENT_H
+
+void run_socket_client();
+
+#endif
diff --git a/fastbootd/transport.c b/fastbootd/transport.c
index 01a5a8a..19a705c 100644
--- a/fastbootd/transport.c
+++ b/fastbootd/transport.c
@@ -99,6 +99,7 @@
         }
         if (ret > 0) {
             buffer[ret] = 0;
+            //TODO: multiple threads
             protocol_handle_command(phandle, buffer);
         }
     }
diff --git a/fastbootd/transport_socket.c b/fastbootd/transport_socket.c
new file mode 100644
index 0000000..ff0f3bd
--- /dev/null
+++ b/fastbootd/transport_socket.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <cutils/sockets.h>
+
+#include "debug.h"
+#include "transport.h"
+#include "utils.h"
+
+
+#define container_of(ptr, type, member) \
+    ((type*)((char*)(ptr) - offsetof(type, member)))
+
+#define SOCKET_WORKING 0
+#define SOCKET_STOPPED -1
+
+
+struct socket_transport {
+    struct transport transport;
+
+    int fd;
+};
+
+struct socket_handle {
+    struct transport_handle handle;
+
+    int fd;
+};
+
+void socket_close(struct transport_handle *thandle)
+{
+    struct socket_handle * handle = container_of(thandle, struct socket_handle, handle);
+    close(handle->fd);
+}
+
+struct transport_handle *socket_connect(struct transport *transport)
+{
+    struct socket_handle *handle = calloc(sizeof(struct socket_handle), 1);
+    struct socket_transport *socket_transport = container_of(transport, struct socket_transport, transport);
+    struct sockaddr addr;
+    socklen_t alen = sizeof(addr);
+
+    handle->fd = accept(socket_transport->fd, &addr, &alen);
+
+    if (handle->fd < 0) {
+        D(WARN, "socket connect error");
+        return NULL;
+    }
+
+    D(DEBUG, "[ socket_thread - registering device ]");
+    return &handle->handle;
+}
+
+ssize_t socket_write(struct transport_handle *thandle, const void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to write (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_write(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_write done fd=%d ]", handle->fd);
+    return ret;
+}
+
+ssize_t socket_read(struct transport_handle *thandle, void *data, size_t len)
+{
+    ssize_t ret;
+    struct socket_handle *handle = container_of(thandle, struct socket_handle, handle);
+
+    D(DEBUG, "about to read (fd=%d, len=%d)", handle->fd, len);
+    ret = bulk_read(handle->fd, data, len);
+    if (ret < 0) {
+        D(ERR, "ERROR: fd = %d, ret = %zd", handle->fd, ret);
+        return -1;
+    }
+    D(DEBUG, "[ socket_read done fd=%d ret=%zd]", handle->fd, ret);
+    return ret;
+}
+
+static int listen_socket_init(struct socket_transport *socket_transport)
+{
+    int s = android_get_control_socket("fastbootd");
+
+    if (s < 0) {
+        D(WARN, "android_get_control_socket(fastbootd): %s\n", strerror(errno));
+        return 0;
+    }
+
+    if (listen(s, 4) < 0) {
+        D(WARN, "listen(control socket): %s\n", strerror(errno));
+        return 0;
+    }
+
+    socket_transport->fd = s;
+
+    return 1;
+}
+
+
+int transport_socket_init()
+{
+    struct socket_transport *socket_transport = malloc(sizeof(struct socket_transport));
+
+    socket_transport->transport.connect = socket_connect;
+    socket_transport->transport.close = socket_close;
+    socket_transport->transport.read = socket_read;
+    socket_transport->transport.write = socket_write;
+
+    if (!listen_socket_init(socket_transport)) {
+        D(ERR, "socket transport init failed");
+        free(socket_transport);
+        return 0;
+    }
+
+    transport_register(&socket_transport->transport);
+    return 1;
+}
+
diff --git a/fastbootd/trigger.c b/fastbootd/trigger.c
new file mode 100644
index 0000000..e63e64d
--- /dev/null
+++ b/fastbootd/trigger.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dlfcn.h>
+
+#include <hardware/hardware.h>
+#include "debug.h"
+#include "trigger.h"
+#include "protocol.h"
+#include "vendor_trigger.h"
+
+static const int version = 1;
+
+static struct vendor_trigger_t *triggers = NULL;
+
+int load_trigger() {
+    int err;
+    hw_module_t* module;
+    hw_device_t* device;
+    int libversion;
+
+    err = hw_get_module(TRIGGER_MODULE_ID, (hw_module_t const**)&module);
+
+    if (err == 0) {
+        err = module->methods->open(module, NULL, &device);
+
+        if (err == 0) {
+            triggers = (struct vendor_trigger_t *) device;
+        } else {
+            D(WARN, "Libvendor load error");
+            return 1;
+        }
+    }
+    else {
+        D(WARN, "Libvendor not load: %s", strerror(-err));
+        return 0;
+    }
+
+    if (triggers->check_version != NULL &&
+        triggers->check_version(version, &libversion)) {
+
+        triggers = NULL;
+        D(ERR, "Library report incompability");
+        return 1;
+    }
+    D(INFO, "libvendortrigger loaded");
+
+    return 0;
+}
+
+int trigger_oem_cmd(const char *arg, const char **response) {
+    if (triggers != NULL && triggers->oem_cmd != NULL)
+        return triggers->oem_cmd(arg, response);
+    return 0;
+}
+
+int trigger_gpt_layout(struct GPT_content *table) {
+    if (triggers != NULL && triggers->gpt_layout != NULL)
+        return triggers->gpt_layout(table);
+    return 0;
+}
+
diff --git a/fastbootd/trigger.h b/fastbootd/trigger.h
new file mode 100644
index 0000000..404acb4
--- /dev/null
+++ b/fastbootd/trigger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __FASTBOOTD_TRIGGER_H_
+#define __FASTBOOTD_TRIGGER_H_
+
+#include "commands/partitions.h"
+#include "vendor_trigger.h"
+
+int load_trigger();
+
+/* same as in struct triggers */
+
+int trigger_gpt_layout(struct GPT_content *table);
+int trigger_oem_cmd(const char *arg, const char **response);
+
+#endif
diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c
index 111cf35..7a8e46f 100644
--- a/fastbootd/usb_linux_client.c
+++ b/fastbootd/usb_linux_client.c
@@ -30,6 +30,7 @@
 
 #include "debug.h"
 #include "transport.h"
+#include "utils.h"
 
 #define   TRACE_TAG  TRACE_USB
 
@@ -50,8 +51,6 @@
 #define USB_FFS_FASTBOOT_OUT   USB_FFS_FASTBOOT_EP(ep1)
 #define USB_FFS_FASTBOOT_IN    USB_FFS_FASTBOOT_EP(ep2)
 
-#define READ_BUF_SIZE (16*1024)
-
 #define container_of(ptr, type, member) \
     ((type*)((char*)(ptr) - offsetof(type, member)))
 
@@ -212,26 +211,6 @@
     return -1;
 }
 
-static ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
-{
-    size_t count = 0;
-    ssize_t ret;
-
-    do {
-        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_in, length, errno, strerror(errno));
-            return -1;
-        } else {
-            count += ret;
-        }
-    } while (count < length);
-
-    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
-    return count;
-}
-
 static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len)
 {
     ssize_t ret;
@@ -248,30 +227,6 @@
     return ret;
 }
 
-static ssize_t bulk_read(int bulk_out, char *buf, size_t length)
-{
-    ssize_t ret;
-    size_t n = 0;
-
-    while (n < length) {
-        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
-        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
-        if (ret < 0) {
-            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
-                    bulk_out, length, errno, strerror(errno));
-            return ret;
-        }
-        n += ret;
-        if (ret < (ssize_t)to_read) {
-            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
-                    ret, to_read, n, length);
-            break;
-        }
-    }
-
-    return n;
-}
-
 ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len)
 {
     ssize_t ret;
diff --git a/fastbootd/utils.c b/fastbootd/utils.c
new file mode 100644
index 0000000..fe3f0f8
--- /dev/null
+++ b/fastbootd/utils.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <cutils/properties.h>
+
+#include "utils.h"
+#include "debug.h"
+
+#ifndef BLKDISCARD
+#define BLKDISCARD _IO(0x12,119)
+#endif
+
+#ifndef BLKSECDISCARD
+#define BLKSECDISCARD _IO(0x12,125)
+#endif
+
+#define READ_BUF_SIZE (16*1024)
+
+int get_stream_size(FILE *stream) {
+    int size;
+    fseek(stream, 0, SEEK_END);
+    size = ftell(stream);
+    fseek(stream, 0, SEEK_SET);
+    return size;
+}
+
+uint64_t get_block_device_size(int fd)
+{
+    uint64_t size = 0;
+    int ret;
+
+    ret = ioctl(fd, BLKGETSIZE64, &size);
+
+    if (ret)
+        return 0;
+
+    return size;
+}
+
+uint64_t get_file_size(int fd)
+{
+    struct stat buf;
+    int ret;
+    int64_t computed_size;
+
+    ret = fstat(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+uint64_t get_file_size64(int fd)
+{
+    struct stat64 buf;
+    int ret;
+    uint64_t computed_size;
+
+    ret = fstat64(fd, &buf);
+    if (ret)
+        return 0;
+
+    if (S_ISREG(buf.st_mode))
+        computed_size = buf.st_size;
+    else if (S_ISBLK(buf.st_mode))
+        computed_size = get_block_device_size(fd);
+    else
+        computed_size = 0;
+
+    return computed_size;
+}
+
+
+char *strip(char *str)
+{
+    int n;
+
+    n = strspn(str, " \t");
+    str += n;
+    n = strcspn(str, " \t");
+    str[n] = '\0';
+
+    return str;
+}
+
+int wipe_block_device(int fd, int64_t len)
+{
+    uint64_t range[2];
+    int ret;
+
+    range[0] = 0;
+    range[1] = len;
+    ret = ioctl(fd, BLKSECDISCARD, &range);
+    if (ret < 0) {
+        range[0] = 0;
+        range[1] = len;
+        ret = ioctl(fd, BLKDISCARD, &range);
+        if (ret < 0) {
+            D(WARN, "Discard failed\n");
+            return 1;
+        } else {
+            D(WARN, "Wipe via secure discard failed, used discard instead\n");
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+int create_temp_file() {
+    char tempname[] = "/dev/fastboot_data_XXXXXX";
+    int fd;
+
+    fd = mkstemp(tempname);
+    if (fd < 0)
+        return -1;
+
+    unlink(tempname);
+
+    return fd;
+}
+
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length)
+{
+    size_t count = 0;
+    ssize_t ret;
+
+    do {
+        ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count));
+        if (ret < 0) {
+            D(WARN, "[ bulk_write failed fd=%d length=%d errno=%d %s ]",
+                    bulk_in, length, errno, strerror(errno));
+            return -1;
+        } else {
+            count += ret;
+        }
+    } while (count < length);
+
+    D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in);
+    return count;
+}
+
+ssize_t bulk_read(int bulk_out, char *buf, size_t length)
+{
+    ssize_t ret;
+    size_t n = 0;
+
+    while (n < length) {
+        size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n;
+        ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read));
+        if (ret < 0) {
+            D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]",
+                    bulk_out, length, errno, strerror(errno));
+            return ret;
+        }
+        n += ret;
+        if (ret < (ssize_t)to_read) {
+            D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u",
+                    ret, to_read, n, length);
+            break;
+        }
+    }
+
+    return n;
+}
+
+#define NAP_TIME 200  // 200 ms between polls
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(NAP_TIME * 1000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+int service_start(const char *service_name)
+{
+    int result = 0;
+    char property_value[PROPERTY_VALUE_MAX];
+
+    property_get(service_name, property_value, "");
+    if (strcmp("running", property_value) != 0) {
+        D(INFO, "Starting %s", service_name);
+        property_set("ctl.start", service_name);
+        if (wait_for_property(service_name, "running", 5))
+            result = -1;
+    }
+
+    return result;
+}
+
+int service_stop(const char *service_name)
+{
+    int result = 0;
+
+    D(INFO, "Stopping MDNSD");
+    property_set("ctl.stop", service_name);
+    if (wait_for_property(service_name, "stopped", 5))
+        result = -1;
+
+    return result;
+}
+
+int ssh_server_start()
+{
+    return service_start("sshd");
+}
diff --git a/fastbootd/utils.h b/fastbootd/utils.h
new file mode 100644
index 0000000..3d98699
--- /dev/null
+++ b/fastbootd/utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2009-2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name of Google, Inc. nor the names of its contributors
+ *    may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _FASTBOOT_UTLIS_H
+#define _FASTBOOT_UTILS_H
+
+#include <stdio.h>
+
+int get_stream_size(FILE *);
+
+char *strip(char *str);
+
+uint64_t get_file_size64(int fd);
+uint64_t get_file_size(int fd);
+uint64_t get_block_device_size(int fd);
+int wipe_block_device(int fd, int64_t len);
+int create_temp_file();
+ssize_t bulk_read(int bulk_out, char *buf, size_t length);
+ssize_t bulk_write(int bulk_in, const char *buf, size_t length);
+int service_start(const char *service_name);
+int service_stop(const char *service_name);
+int ssh_server_start();
+
+#define ROUND_TO_PAGE(address,pagesize) ((address + pagesize - 1) & (~(pagesize - 1)))
+
+#define ROUND_UP(number,size) (((number + size - 1) / size) * size)
+
+#endif
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 473d375..5cd5ce1 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -13,6 +13,8 @@
 
 LOCAL_SRC_FILES := \
 	healthd.cpp \
+	healthd_mode_android.cpp \
+	healthd_mode_charger.cpp \
 	BatteryMonitor.cpp \
 	BatteryPropertiesRegistrar.cpp
 
@@ -22,9 +24,57 @@
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 
-LOCAL_STATIC_LIBRARIES :=  libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_CFLAGS := -D__STDC_LIMIT_MACROS
+
+ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true)
+LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK
+endif
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+endif
+
+LOCAL_C_INCLUDES := bootable/recovery
+
+LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpixelflinger_static libpng libz libutils libstdc++ libcutils liblog libm libc
+
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_STATIC_LIBRARIES += libsuspend
+endif
+
 LOCAL_HAL_STATIC_LIBRARIES := libhealthd
 
+# Symlink /charger to /sbin/healthd
+LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
+    && ln -sf /sbin/healthd $(TARGET_ROOT_OUT)/charger
+
 include $(BUILD_EXECUTABLE)
 
+
+define _add-charger-image
+include $$(CLEAR_VARS)
+LOCAL_MODULE := system_core_charger_$(notdir $(1))
+LOCAL_MODULE_STEM := $(notdir $(1))
+_img_modules += $$(LOCAL_MODULE)
+LOCAL_SRC_FILES := $1
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
+include $$(BUILD_PREBUILT)
+endef
+
+_img_modules :=
+_images :=
+$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
+  $(eval $(call _add-charger-image,$(_img))))
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_res_images
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := $(_img_modules)
+include $(BUILD_PHONY_PACKAGE)
+
+_add-charger-image :=
+_img_modules :=
+
 endif
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 688c7ff..3e6a4b1 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -18,7 +18,6 @@
 
 #include "healthd.h"
 #include "BatteryMonitor.h"
-#include "BatteryPropertiesRegistrar.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -28,6 +27,7 @@
 #include <unistd.h>
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
+#include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -170,7 +170,6 @@
 }
 
 bool BatteryMonitor::update(void) {
-    struct BatteryProperties props;
     bool logthis;
 
     props.chargerAcOnline = false;
@@ -178,23 +177,15 @@
     props.chargerWirelessOnline = false;
     props.batteryStatus = BATTERY_STATUS_UNKNOWN;
     props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
-    props.batteryCurrentNow = INT_MIN;
-    props.batteryChargeCounter = INT_MIN;
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
     else
-        props.batteryPresent = true;
+        props.batteryPresent = mBatteryDevicePresent;
 
     props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath);
     props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
-        props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath);
-
-    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
-        props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
-
     props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath);
 
     const int SIZE = 128;
@@ -244,7 +235,9 @@
 
     if (logthis) {
         char dmesgline[256];
-        snprintf(dmesgline, sizeof(dmesgline),
+
+        if (props.batteryPresent) {
+            snprintf(dmesgline, sizeof(dmesgline),
                  "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                  props.batteryLevel, props.batteryVoltage,
                  props.batteryTemperature < 0 ? "-" : "",
@@ -252,11 +245,16 @@
                  abs(props.batteryTemperature % 10), props.batteryHealth,
                  props.batteryStatus);
 
-        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
-            char b[20];
+            if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+                int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
+                char b[20];
 
-            snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000);
-            strlcat(dmesgline, b, sizeof(dmesgline));
+                snprintf(b, sizeof(b), " c=%d", c / 1000);
+                strlcat(dmesgline, b, sizeof(dmesgline));
+            }
+        } else {
+            snprintf(dmesgline, sizeof(dmesgline),
+                 "battery none");
         }
 
         KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline,
@@ -265,14 +263,91 @@
                   props.chargerWirelessOnline ? "w" : "");
     }
 
-    if (mBatteryPropertiesRegistrar != NULL)
-        mBatteryPropertiesRegistrar->notifyListeners(props);
-
+    healthd_mode_ops->battery_update(&props);
     return props.chargerAcOnline | props.chargerUsbOnline |
             props.chargerWirelessOnline;
 }
 
-void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) {
+status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
+    status_t ret = BAD_VALUE;
+
+    switch(id) {
+    case BATTERY_PROP_CHARGE_COUNTER:
+        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+            val->valueInt =
+                getIntField(mHealthdConfig->batteryChargeCounterPath);
+            ret = NO_ERROR;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_CURRENT_NOW:
+        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+            val->valueInt =
+                getIntField(mHealthdConfig->batteryCurrentNowPath);
+            ret = NO_ERROR;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_CURRENT_AVG:
+        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+            val->valueInt =
+                getIntField(mHealthdConfig->batteryCurrentAvgPath);
+            ret = NO_ERROR;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    if (ret != NO_ERROR)
+        val->valueInt = INT_MIN;
+
+    return ret;
+}
+
+void BatteryMonitor::dumpState(int fd) {
+    int v;
+    char vs[128];
+
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
+             props.chargerAcOnline, props.chargerUsbOnline,
+             props.chargerWirelessOnline);
+    write(fd, vs, strlen(vs));
+    snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
+             props.batteryStatus, props.batteryHealth, props.batteryPresent);
+    write(fd, vs, strlen(vs));
+    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
+             props.batteryLevel, props.batteryVoltage,
+             props.batteryTemperature);
+    write(fd, vs, strlen(vs));
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryCurrentNowPath);
+        snprintf(vs, sizeof(vs), "current now: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
+        snprintf(vs, sizeof(vs), "current avg: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryChargeCounterPath);
+        snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+}
+
+void BatteryMonitor::init(struct healthd_config *hc) {
     String8 path;
 
     mHealthdConfig = hc;
@@ -303,6 +378,8 @@
                 break;
 
             case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+                mBatteryDevicePresent = true;
+
                 if (mHealthdConfig->batteryStatusPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
@@ -358,6 +435,14 @@
                         mHealthdConfig->batteryCurrentNowPath = path;
                 }
 
+                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/current_avg",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCurrentAvgPath = path;
+                }
+
                 if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_counter",
@@ -400,24 +485,25 @@
 
     if (!mChargerNames.size())
         KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
-    if (mHealthdConfig->batteryStatusPath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
-    if (mHealthdConfig->batteryHealthPath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
-    if (mHealthdConfig->batteryPresentPath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
-    if (mHealthdConfig->batteryCapacityPath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
-    if (mHealthdConfig->batteryVoltagePath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
-    if (mHealthdConfig->batteryTemperaturePath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
-    if (mHealthdConfig->batteryTechnologyPath.isEmpty())
-        KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
-
-    if (nosvcmgr == false) {
-            mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this);
-            mBatteryPropertiesRegistrar->publish();
+    if (!mBatteryDevicePresent) {
+        KLOG_INFO(LOG_TAG, "No battery devices found\n");
+        hc->periodic_chores_interval_fast = -1;
+        hc->periodic_chores_interval_slow = -1;
+    } else {
+        if (mHealthdConfig->batteryStatusPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
+        if (mHealthdConfig->batteryHealthPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
+        if (mHealthdConfig->batteryPresentPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
+        if (mHealthdConfig->batteryCapacityPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
+        if (mHealthdConfig->batteryVoltagePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
+        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
+        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
     }
 }
 
diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h
index ba291af..4866cc2 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/BatteryMonitor.h
@@ -17,17 +17,15 @@
 #ifndef HEALTHD_BATTERYMONITOR_H
 #define HEALTHD_BATTERYMONITOR_H
 
+#include <batteryservice/BatteryService.h>
 #include <binder/IInterface.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
 #include "healthd.h"
-#include "BatteryPropertiesRegistrar.h"
 
 namespace android {
 
-class BatteryPropertiesRegistrar;
-
 class BatteryMonitor {
   public:
 
@@ -39,14 +37,16 @@
         ANDROID_POWER_SUPPLY_TYPE_BATTERY
     };
 
-    void init(struct healthd_config *hc, bool nosvcmgr);
+    void init(struct healthd_config *hc);
     bool update(void);
+    status_t getProperty(int id, struct BatteryProperty *val);
+    void dumpState(int fd);
 
   private:
     struct healthd_config *mHealthdConfig;
     Vector<String8> mChargerNames;
-
-    sp<BatteryPropertiesRegistrar> mBatteryPropertiesRegistrar;
+    bool mBatteryDevicePresent;
+    struct BatteryProperties props;
 
     int getBatteryStatus(const char* status);
     int getBatteryHealth(const char* status);
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index 6a33ad8..58da4ee 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -18,19 +18,20 @@
 #include <batteryservice/BatteryService.h>
 #include <batteryservice/IBatteryPropertiesListener.h>
 #include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
+#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
 #include <utils/Errors.h>
 #include <utils/Mutex.h>
 #include <utils/String16.h>
 
+#include "healthd.h"
+
 namespace android {
 
-BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) {
-    mBatteryMonitor = monitor;
-}
-
 void BatteryPropertiesRegistrar::publish() {
-    defaultServiceManager()->addService(String16("batterypropreg"), this);
+    defaultServiceManager()->addService(String16("batteryproperties"), this);
 }
 
 void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
@@ -53,7 +54,7 @@
         mListeners.add(listener);
         listener->asBinder()->linkToDeath(this);
     }
-    mBatteryMonitor->update();
+    healthd_battery_update();
 }
 
 void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
@@ -67,6 +68,23 @@
     }
 }
 
+status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
+    return healthd_get_property(id, val);
+}
+
+status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& args) {
+    IPCThreadState* self = IPCThreadState::self();
+    const int pid = self->getCallingPid();
+    const int uid = self->getCallingUid();
+    if ((uid != AID_SHELL) &&
+        !PermissionCache::checkPermission(
+                String16("android.permission.DUMP"), pid, uid))
+        return PERMISSION_DENIED;
+
+    healthd_dump_battery_state(fd);
+    return OK;
+}
+
 void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
     Mutex::Autolock _l(mRegistrationLock);
 
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
index 793ddad..8853874 100644
--- a/healthd/BatteryPropertiesRegistrar.h
+++ b/healthd/BatteryPropertiesRegistrar.h
@@ -17,10 +17,9 @@
 #ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
 #define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
 
-#include "BatteryMonitor.h"
-
 #include <binder/IBinder.h>
 #include <utils/Mutex.h>
+#include <utils/String16.h>
 #include <utils/Vector.h>
 #include <batteryservice/BatteryService.h>
 #include <batteryservice/IBatteryPropertiesListener.h>
@@ -28,22 +27,20 @@
 
 namespace android {
 
-class BatteryMonitor;
-
 class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
                                    public IBinder::DeathRecipient {
 public:
-    BatteryPropertiesRegistrar(BatteryMonitor* monitor);
     void publish();
     void notifyListeners(struct BatteryProperties props);
 
 private:
-    BatteryMonitor* mBatteryMonitor;
     Mutex mRegistrationLock;
     Vector<sp<IBatteryPropertiesListener> > mListeners;
 
     void registerListener(const sp<IBatteryPropertiesListener>& listener);
     void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
+    status_t getProperty(int id, struct BatteryProperty *val);
+    status_t dump(int fd, const Vector<String16>& args);
     void binderDied(const wp<IBinder>& who);
 };
 
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 9b84c3e..095ce9d 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -21,16 +21,17 @@
 #include "BatteryMonitor.h"
 
 #include <errno.h>
+#include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <batteryservice/BatteryService.h>
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
 #include <cutils/klog.h>
 #include <cutils/uevent.h>
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
+#include <utils/Errors.h>
 
 using namespace android;
 
@@ -49,13 +50,17 @@
     .batteryTemperaturePath = String8(String8::kEmptyString),
     .batteryTechnologyPath = String8(String8::kEmptyString),
     .batteryCurrentNowPath = String8(String8::kEmptyString),
+    .batteryCurrentAvgPath = String8(String8::kEmptyString),
     .batteryChargeCounterPath = String8(String8::kEmptyString),
 };
 
+static int eventct;
+static int epollfd;
+
 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
 
-// epoll events: uevent, wakealarm, binder
-#define MAX_EPOLL_EVENTS 3
+// epoll_create() parameter is actually unused
+#define MAX_EPOLL_EVENTS 40
 static int uevent_fd;
 static int wakealarm_fd;
 static int binder_fd;
@@ -67,7 +72,80 @@
 
 static BatteryMonitor* gBatteryMonitor;
 
-static bool nosvcmgr;
+struct healthd_mode_ops *healthd_mode_ops;
+
+// Android mode
+
+extern void healthd_mode_android_init(struct healthd_config *config);
+extern int healthd_mode_android_preparetowait(void);
+extern void healthd_mode_android_battery_update(
+    struct android::BatteryProperties *props);
+
+// Charger mode
+
+extern void healthd_mode_charger_init(struct healthd_config *config);
+extern int healthd_mode_charger_preparetowait(void);
+extern void healthd_mode_charger_heartbeat(void);
+extern void healthd_mode_charger_battery_update(
+    struct android::BatteryProperties *props);
+
+// NOPs for modes that need no special action
+
+static void healthd_mode_nop_init(struct healthd_config *config);
+static int healthd_mode_nop_preparetowait(void);
+static void healthd_mode_nop_heartbeat(void);
+static void healthd_mode_nop_battery_update(
+    struct android::BatteryProperties *props);
+
+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,
+};
+
+static struct healthd_mode_ops charger_ops = {
+    .init = healthd_mode_charger_init,
+    .preparetowait = healthd_mode_charger_preparetowait,
+    .heartbeat = healthd_mode_charger_heartbeat,
+    .battery_update = healthd_mode_charger_battery_update,
+};
+
+static struct healthd_mode_ops recovery_ops = {
+    .init = healthd_mode_nop_init,
+    .preparetowait = healthd_mode_nop_preparetowait,
+    .heartbeat = healthd_mode_nop_heartbeat,
+    .battery_update = healthd_mode_nop_battery_update,
+};
+
+static void healthd_mode_nop_init(struct healthd_config *config) {
+}
+
+static int healthd_mode_nop_preparetowait(void) {
+    return -1;
+}
+
+static void healthd_mode_nop_heartbeat(void) {
+}
+
+static void healthd_mode_nop_battery_update(
+    struct android::BatteryProperties *props) {
+}
+
+int healthd_register_event(int fd, void (*handler)(uint32_t)) {
+    struct epoll_event ev;
+
+    ev.events = EPOLLIN | EPOLLWAKEUP;
+    ev.data.ptr = (void *)handler;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+        KLOG_ERROR(LOG_TAG,
+                   "epoll_ctl failed; errno=%d\n", errno);
+        return -1;
+    }
+
+    eventct++;
+    return 0;
+}
 
 static void wakealarm_set_interval(int interval) {
     struct itimerspec itval;
@@ -89,7 +167,11 @@
         KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
 }
 
-static void battery_update(void) {
+status_t healthd_get_property(int id, struct BatteryProperty *val) {
+    return gBatteryMonitor->getProperty(id, val);
+}
+
+void healthd_battery_update(void) {
     // Fast wake interval when on charger (watch for overheat);
     // slow wake interval when on battery (watch for drained battery).
 
@@ -113,21 +195,17 @@
                 -1 : healthd_config.periodic_chores_interval_fast * 1000;
 }
 
-static void periodic_chores() {
-    battery_update();
+void healthd_dump_battery_state(int fd) {
+    gBatteryMonitor->dumpState(fd);
+    fsync(fd);
 }
 
-static void uevent_init(void) {
-    uevent_fd = uevent_open_socket(64*1024, true);
-
-    if (uevent_fd >= 0)
-        fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
-    else
-        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+static void periodic_chores() {
+    healthd_battery_update();
 }
 
 #define UEVENT_MSG_LEN 1024
-static void uevent_event(void) {
+static void uevent_event(uint32_t epevents) {
     char msg[UEVENT_MSG_LEN+2];
     char *cp;
     int n;
@@ -144,7 +222,7 @@
 
     while (*cp) {
         if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            battery_update();
+            healthd_battery_update();
             break;
         }
 
@@ -154,6 +232,31 @@
     }
 }
 
+static void uevent_init(void) {
+    uevent_fd = uevent_open_socket(64*1024, true);
+
+    if (uevent_fd < 0) {
+        KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
+        return;
+    }
+
+    fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
+    if (healthd_register_event(uevent_fd, uevent_event))
+        KLOG_ERROR(LOG_TAG,
+                   "register for uevent events failed\n");
+}
+
+static void wakealarm_event(uint32_t epevents) {
+    unsigned long long wakeups;
+
+    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
+        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
+        return;
+    }
+
+    periodic_chores();
+}
+
 static void wakealarm_init(void) {
     wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
     if (wakealarm_fd == -1) {
@@ -161,82 +264,24 @@
         return;
     }
 
+    if (healthd_register_event(wakealarm_fd, wakealarm_event))
+        KLOG_ERROR(LOG_TAG,
+                   "Registration of wakealarm event failed\n");
+
     wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
 }
 
-static void wakealarm_event(void) {
-    unsigned long long wakeups;
-
-    if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n");
-        return;
-    }
-
-    periodic_chores();
-}
-
-static void binder_init(void) {
-    ProcessState::self()->setThreadPoolMaxThreadCount(0);
-    IPCThreadState::self()->disableBackgroundScheduling(true);
-    IPCThreadState::self()->setupPolling(&binder_fd);
-}
-
-static void binder_event(void) {
-    IPCThreadState::self()->handlePolledCommands();
-}
-
 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];
+        struct epoll_event events[eventct];
         int nevents;
+        int timeout = awake_poll_interval;
+        int mode_timeout;
 
-        IPCThreadState::self()->flushCommands();
-        nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval);
+        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)
@@ -247,39 +292,69 @@
 
         for (int n = 0; n < nevents; ++n) {
             if (events[n].data.ptr)
-                (*(void (*)())events[n].data.ptr)();
+                (*(void (*)(int))events[n].data.ptr)(events[n].events);
         }
 
         if (!nevents)
             periodic_chores();
+
+        healthd_mode_ops->heartbeat();
     }
 
     return;
 }
 
-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);
-        }
+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();
-    binder_init();
     gBatteryMonitor = new BatteryMonitor();
-    gBatteryMonitor->init(&healthd_config, nosvcmgr);
+    gBatteryMonitor->init(&healthd_config);
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    int ch;
+    int ret;
+
+    klog_set_level(KLOG_LEVEL);
+    healthd_mode_ops = &android_ops;
+
+    if (!strcmp(basename(argv[0]), "charger")) {
+        healthd_mode_ops = &charger_ops;
+    } 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;
+                break;
+            case '?':
+            default:
+                KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n", ch);
+                exit(1);
+            }
+        }
+    }
+
+    ret = healthd_init();
+    if (ret) {
+        KLOG_ERROR("Initialization failed, exiting\n");
+        exit(2);
+    }
 
     healthd_mainloop();
-    return 0;
+    KLOG_ERROR("Main loop terminated, exiting\n");
+    return 3;
 }
diff --git a/healthd/healthd.h b/healthd/healthd.h
index 5374fb1..23a54bf 100644
--- a/healthd/healthd.h
+++ b/healthd/healthd.h
@@ -18,6 +18,7 @@
 #define _HEALTHD_H_
 
 #include <batteryservice/BatteryService.h>
+#include <utils/Errors.h>
 #include <utils/String8.h>
 
 // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at
@@ -61,9 +62,35 @@
     android::String8 batteryTemperaturePath;
     android::String8 batteryTechnologyPath;
     android::String8 batteryCurrentNowPath;
+    android::String8 batteryCurrentAvgPath;
     android::String8 batteryChargeCounterPath;
 };
 
+// Global helper functions
+
+int healthd_register_event(int fd, void (*handler)(uint32_t));
+void healthd_battery_update();
+android::status_t healthd_get_property(int id,
+    struct android::BatteryProperty *val);
+void healthd_dump_battery_state(int fd);
+
+struct healthd_mode_ops {
+    void (*init)(struct healthd_config *config);
+    int (*preparetowait)(void);
+    void (*heartbeat)(void);
+    void (*battery_update)(struct android::BatteryProperties *props);
+};
+
+extern struct healthd_mode_ops *healthd_mode_ops;
+
+// Charger mode
+
+void healthd_mode_charger_init(struct healthd_config *config);
+int healthd_mode_charger_preparetowait(void);
+void healthd_mode_charger_heartbeat(void);
+void healthd_mode_charger_battery_update(
+    struct android::BatteryProperties *props);
+
 // The following are implemented in libhealthd_board to handle board-specific
 // behavior.
 //
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
new file mode 100644
index 0000000..4887c8c
--- /dev/null
+++ b/healthd/healthd_mode_android.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "healthd-android"
+
+#include "healthd.h"
+#include "BatteryPropertiesRegistrar.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <cutils/klog.h>
+#include <sys/epoll.h>
+
+using namespace android;
+
+static int gBinderFd;
+static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
+
+void healthd_mode_android_battery_update(
+    struct android::BatteryProperties *props) {
+    if (gBatteryPropertiesRegistrar != NULL)
+        gBatteryPropertiesRegistrar->notifyListeners(*props);
+
+    return;
+}
+
+int healthd_mode_android_preparetowait(void) {
+    IPCThreadState::self()->flushCommands();
+    return -1;
+}
+
+static void binder_event(uint32_t epevents) {
+    IPCThreadState::self()->handlePolledCommands();
+}
+
+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");
+    }
+
+    gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
+    gBatteryPropertiesRegistrar->publish();
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
new file mode 100644
index 0000000..fe96c86
--- /dev/null
+++ b/healthd/healthd_mode_charger.cpp
@@ -0,0 +1,679 @@
+/*
+ * Copyright (C) 2011-2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include <batteryservice/BatteryService.h>
+#include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/misc.h>
+
+#ifdef CHARGER_ENABLE_SUSPEND
+#include <suspend/autosuspend.h>
+#endif
+
+#include "minui/minui.h"
+
+#include "healthd.h"
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
+
+#define MSEC_PER_SEC            (1000LL)
+#define NSEC_PER_MSEC           (1000000LL)
+
+#define BATTERY_UNKNOWN_TIME    (2 * MSEC_PER_SEC)
+#define POWER_ON_KEY_TIME       (2 * MSEC_PER_SEC)
+#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
+
+#define BATTERY_FULL_THRESH     95
+
+#define LAST_KMSG_PATH          "/proc/last_kmsg"
+#define LAST_KMSG_MAX_SZ        (32 * 1024)
+
+#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
+#define LOGI(x...) do { KLOG_INFO("charger", x); } while (0)
+#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+
+struct key_state {
+    bool pending;
+    bool down;
+    int64_t timestamp;
+};
+
+struct frame {
+    const char *name;
+    int disp_time;
+    int min_capacity;
+    bool level_only;
+
+    gr_surface surface;
+};
+
+struct animation {
+    bool run;
+
+    struct frame *frames;
+    int cur_frame;
+    int num_frames;
+
+    int cur_cycle;
+    int num_cycles;
+
+    /* current capacity being animated */
+    int capacity;
+};
+
+struct charger {
+    bool have_battery_state;
+    bool charger_connected;
+    int capacity;
+    int64_t next_screen_transition;
+    int64_t next_key_check;
+    int64_t next_pwr_check;
+
+    struct key_state keys[KEY_MAX + 1];
+
+    struct animation *batt_anim;
+    gr_surface surf_unknown;
+};
+
+static struct frame batt_anim_frames[] = {
+    {
+        .name = "charger/battery_0",
+        .disp_time = 750,
+        .min_capacity = 0,
+        .level_only = false,
+        .surface = NULL,
+    },
+    {
+        .name = "charger/battery_1",
+        .disp_time = 750,
+        .min_capacity = 20,
+        .level_only = false,
+        .surface = NULL,
+    },
+    {
+        .name = "charger/battery_2",
+        .disp_time = 750,
+        .min_capacity = 40,
+        .level_only = false,
+        .surface = NULL,
+    },
+    {
+        .name = "charger/battery_3",
+        .disp_time = 750,
+        .min_capacity = 60,
+        .level_only = false,
+        .surface = NULL,
+    },
+    {
+        .name = "charger/battery_4",
+        .disp_time = 750,
+        .min_capacity = 80,
+        .level_only = true,
+        .surface = NULL,
+    },
+    {
+        .name = "charger/battery_5",
+        .disp_time = 750,
+        .min_capacity = BATTERY_FULL_THRESH,
+        .level_only = false,
+        .surface = NULL,
+    },
+};
+
+static struct animation battery_animation = {
+    .run = false,
+    .frames = batt_anim_frames,
+    .cur_frame = 0,
+    .num_frames = ARRAY_SIZE(batt_anim_frames),
+    .cur_cycle = 0,
+    .num_cycles = 3,
+    .capacity = 0,
+};
+
+static struct charger charger_state;
+
+static int char_width;
+static int char_height;
+
+/* current time in milliseconds */
+static int64_t curr_time_ms(void)
+{
+    struct timespec tm;
+    clock_gettime(CLOCK_MONOTONIC, &tm);
+    return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);
+}
+
+static void clear_screen(void)
+{
+    gr_color(0, 0, 0, 255);
+    gr_fill(0, 0, gr_fb_width(), gr_fb_height());
+};
+
+#define MAX_KLOG_WRITE_BUF_SZ 256
+
+static void dump_last_kmsg(void)
+{
+    char *buf;
+    char *ptr;
+    unsigned sz = 0;
+    int len;
+
+    LOGI("\n");
+    LOGI("*************** LAST KMSG ***************\n");
+    LOGI("\n");
+    buf = (char *)load_file(LAST_KMSG_PATH, &sz);
+    if (!buf || !sz) {
+        LOGI("last_kmsg not found. Cold reset?\n");
+        goto out;
+    }
+
+    len = min(sz, LAST_KMSG_MAX_SZ);
+    ptr = buf + (sz - len);
+
+    while (len > 0) {
+        int cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);
+        char yoink;
+        char *nl;
+
+        nl = (char *)memrchr(ptr, '\n', cnt - 1);
+        if (nl)
+            cnt = nl - ptr + 1;
+
+        yoink = ptr[cnt];
+        ptr[cnt] = '\0';
+        klog_write(6, "<6>%s", ptr);
+        ptr[cnt] = yoink;
+
+        len -= cnt;
+        ptr += cnt;
+    }
+
+    free(buf);
+
+out:
+    LOGI("\n");
+    LOGI("************* END LAST KMSG *************\n");
+    LOGI("\n");
+}
+
+static int get_battery_capacity()
+{
+    return charger_state.capacity;
+}
+
+#ifdef CHARGER_ENABLE_SUSPEND
+static int request_suspend(bool enable)
+{
+    if (enable)
+        return autosuspend_enable();
+    else
+        return autosuspend_disable();
+}
+#else
+static int request_suspend(bool enable)
+{
+    return 0;
+}
+#endif
+
+static int draw_text(const char *str, int x, int y)
+{
+    int str_len_px = gr_measure(str);
+
+    if (x < 0)
+        x = (gr_fb_width() - str_len_px) / 2;
+    if (y < 0)
+        y = (gr_fb_height() - char_height) / 2;
+    gr_text(x, y, str, 0);
+
+    return y + char_height;
+}
+
+static void android_green(void)
+{
+    gr_color(0xa4, 0xc6, 0x39, 255);
+}
+
+/* returns the last y-offset of where the surface ends */
+static int draw_surface_centered(struct charger *charger, gr_surface surface)
+{
+    int w;
+    int h;
+    int x;
+    int y;
+
+    w = gr_get_width(surface);
+    h = gr_get_height(surface);
+    x = (gr_fb_width() - w) / 2 ;
+    y = (gr_fb_height() - h) / 2 ;
+
+    LOGV("drawing surface %dx%d+%d+%d\n", w, h, x, y);
+    gr_blit(surface, 0, 0, w, h, x, y);
+    return y + h;
+}
+
+static void draw_unknown(struct charger *charger)
+{
+    int y;
+    if (charger->surf_unknown) {
+        draw_surface_centered(charger, charger->surf_unknown);
+    } else {
+        android_green();
+        y = draw_text("Charging!", -1, -1);
+        draw_text("?\?/100", -1, y + 25);
+    }
+}
+
+static void draw_battery(struct charger *charger)
+{
+    struct animation *batt_anim = charger->batt_anim;
+    struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
+
+    if (batt_anim->num_frames != 0) {
+        draw_surface_centered(charger, frame->surface);
+        LOGV("drawing frame #%d name=%s min_cap=%d time=%d\n",
+             batt_anim->cur_frame, frame->name, frame->min_capacity,
+             frame->disp_time);
+    }
+}
+
+static void redraw_screen(struct charger *charger)
+{
+    struct animation *batt_anim = charger->batt_anim;
+
+    clear_screen();
+
+    /* try to display *something* */
+    if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
+        draw_unknown(charger);
+    else
+        draw_battery(charger);
+    gr_flip();
+}
+
+static void kick_animation(struct animation *anim)
+{
+    anim->run = true;
+}
+
+static void reset_animation(struct animation *anim)
+{
+    anim->cur_cycle = 0;
+    anim->cur_frame = 0;
+    anim->run = false;
+}
+
+static void update_screen_state(struct charger *charger, int64_t now)
+{
+    struct animation *batt_anim = charger->batt_anim;
+    int cur_frame;
+    int disp_time;
+
+    if (!batt_anim->run || now < charger->next_screen_transition)
+        return;
+
+    /* animation is over, blank screen and leave */
+    if (batt_anim->cur_cycle == batt_anim->num_cycles) {
+        reset_animation(batt_anim);
+        charger->next_screen_transition = -1;
+        gr_fb_blank(true);
+        LOGV("[%lld] animation done\n", now);
+        if (!charger->charger_connected)
+            request_suspend(true);
+        return;
+    }
+
+    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
+
+    /* animation starting, set up the animation */
+    if (batt_anim->cur_frame == 0) {
+        int batt_cap;
+        int ret;
+
+        LOGV("[%lld] animation starting\n", now);
+        batt_cap = get_battery_capacity();
+        if (batt_cap >= 0 && batt_anim->num_frames != 0) {
+            int i;
+
+            /* find first frame given current capacity */
+            for (i = 1; i < batt_anim->num_frames; i++) {
+                if (batt_cap < batt_anim->frames[i].min_capacity)
+                    break;
+            }
+            batt_anim->cur_frame = i - 1;
+
+            /* show the first frame for twice as long */
+            disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
+        }
+
+        batt_anim->capacity = batt_cap;
+    }
+
+    /* unblank the screen  on first cycle */
+    if (batt_anim->cur_cycle == 0)
+        gr_fb_blank(false);
+
+    /* draw the new frame (@ cur_frame) */
+    redraw_screen(charger);
+
+    /* if we don't have anim frames, we only have one image, so just bump
+     * the cycle counter and exit
+     */
+    if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
+        LOGV("[%lld] animation missing or unknown battery status\n", now);
+        charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
+        batt_anim->cur_cycle++;
+        return;
+    }
+
+    /* schedule next screen transition */
+    charger->next_screen_transition = now + disp_time;
+
+    /* advance frame cntr to the next valid frame
+     * if necessary, advance cycle cntr, and reset frame cntr
+     */
+    batt_anim->cur_frame++;
+
+    /* if the frame is used for level-only, that is only show it when it's
+     * the current level, skip it during the animation.
+     */
+    while (batt_anim->cur_frame < batt_anim->num_frames &&
+           batt_anim->frames[batt_anim->cur_frame].level_only)
+        batt_anim->cur_frame++;
+    if (batt_anim->cur_frame >= batt_anim->num_frames) {
+        batt_anim->cur_cycle++;
+        batt_anim->cur_frame = 0;
+
+        /* don't reset the cycle counter, since we use that as a signal
+         * in a test above to check if animation is over
+         */
+    }
+}
+
+static int set_key_callback(int code, int value, void *data)
+{
+    struct charger *charger = (struct charger *)data;
+    int64_t now = curr_time_ms();
+    int down = !!value;
+
+    if (code > KEY_MAX)
+        return -1;
+
+    /* ignore events that don't modify our state */
+    if (charger->keys[code].down == down)
+        return 0;
+
+    /* only record the down even timestamp, as the amount
+     * of time the key spent not being pressed is not useful */
+    if (down)
+        charger->keys[code].timestamp = now;
+    charger->keys[code].down = down;
+    charger->keys[code].pending = true;
+    if (down) {
+        LOGV("[%lld] key[%d] down\n", now, code);
+    } else {
+        int64_t duration = now - charger->keys[code].timestamp;
+        int64_t secs = duration / 1000;
+        int64_t msecs = duration - secs * 1000;
+        LOGV("[%lld] key[%d] up (was down for %lld.%lldsec)\n", now,
+            code, secs, msecs);
+    }
+
+    return 0;
+}
+
+static void update_input_state(struct charger *charger,
+                               struct input_event *ev)
+{
+    if (ev->type != EV_KEY)
+        return;
+    set_key_callback(ev->code, ev->value, charger);
+}
+
+static void set_next_key_check(struct charger *charger,
+                               struct key_state *key,
+                               int64_t timeout)
+{
+    int64_t then = key->timestamp + timeout;
+
+    if (charger->next_key_check == -1 || then < charger->next_key_check)
+        charger->next_key_check = then;
+}
+
+static void process_key(struct charger *charger, int code, int64_t now)
+{
+    struct key_state *key = &charger->keys[code];
+    int64_t next_key_check;
+
+    if (code == KEY_POWER) {
+        if (key->down) {
+            int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
+            if (now >= reboot_timeout) {
+                LOGI("[%lld] rebooting\n", now);
+                android_reboot(ANDROID_RB_RESTART, 0, 0);
+            } else {
+                /* if the key is pressed but timeout hasn't expired,
+                 * make sure we wake up at the right-ish time to check
+                 */
+                set_next_key_check(charger, key, POWER_ON_KEY_TIME);
+            }
+        } else {
+            /* if the power key got released, force screen state cycle */
+            if (key->pending) {
+                request_suspend(false);
+                kick_animation(charger->batt_anim);
+            }
+        }
+    }
+
+    key->pending = false;
+}
+
+static void handle_input_state(struct charger *charger, int64_t now)
+{
+    process_key(charger, KEY_POWER, now);
+
+    if (charger->next_key_check != -1 && now > charger->next_key_check)
+        charger->next_key_check = -1;
+}
+
+static void handle_power_supply_state(struct charger *charger, int64_t now)
+{
+    if (!charger->have_battery_state)
+        return;
+
+    if (!charger->charger_connected) {
+        request_suspend(false);
+        if (charger->next_pwr_check == -1) {
+            charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
+            LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)\n",
+                 now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
+        } else if (now >= charger->next_pwr_check) {
+            LOGI("[%lld] shutting down\n", now);
+            android_reboot(ANDROID_RB_POWEROFF, 0, 0);
+        } else {
+            /* otherwise we already have a shutdown timer scheduled */
+        }
+    } else {
+        /* online supply present, reset shutdown timer if set */
+        if (charger->next_pwr_check != -1) {
+            LOGI("[%lld] device plugged in: shutdown cancelled\n", now);
+            kick_animation(charger->batt_anim);
+        }
+        charger->next_pwr_check = -1;
+    }
+}
+
+void healthd_mode_charger_heartbeat()
+{
+    struct charger *charger = &charger_state;
+    int64_t now = curr_time_ms();
+    int ret;
+
+    handle_input_state(charger, now);
+    handle_power_supply_state(charger, now);
+
+    /* do screen update last in case any of the above want to start
+     * screen transitions (animations, etc)
+     */
+    update_screen_state(charger, now);
+}
+
+void healthd_mode_charger_battery_update(
+    struct android::BatteryProperties *props)
+{
+    struct charger *charger = &charger_state;
+
+    charger->charger_connected =
+        props->chargerAcOnline || props->chargerUsbOnline ||
+        props->chargerWirelessOnline;
+    charger->capacity = props->batteryLevel;
+
+    if (!charger->have_battery_state) {
+        charger->have_battery_state = true;
+        charger->next_screen_transition = curr_time_ms() - 1;
+        reset_animation(charger->batt_anim);
+        kick_animation(charger->batt_anim);
+    }
+}
+
+int healthd_mode_charger_preparetowait(void)
+{
+    struct charger *charger = &charger_state;
+    int64_t now = curr_time_ms();
+    int64_t next_event = INT64_MAX;
+    int64_t timeout;
+    struct input_event ev;
+    int ret;
+
+    LOGV("[%lld] next screen: %lld next key: %lld next pwr: %lld\n", now,
+         charger->next_screen_transition, charger->next_key_check,
+         charger->next_pwr_check);
+
+    if (charger->next_screen_transition != -1)
+        next_event = charger->next_screen_transition;
+    if (charger->next_key_check != -1 && charger->next_key_check < next_event)
+        next_event = charger->next_key_check;
+    if (charger->next_pwr_check != -1 && charger->next_pwr_check < next_event)
+        next_event = charger->next_pwr_check;
+
+    if (next_event != -1 && next_event != INT64_MAX)
+        timeout = max(0, next_event - now);
+    else
+        timeout = -1;
+
+   return (int)timeout;
+}
+
+static int input_callback(int fd, unsigned int epevents, void *data)
+{
+    struct charger *charger = (struct charger *)data;
+    struct input_event ev;
+    int ret;
+
+    ret = ev_get_input(fd, epevents, &ev);
+    if (ret)
+        return -1;
+    update_input_state(charger, &ev);
+    return 0;
+}
+
+static void charger_event_handler(uint32_t epevents)
+{
+    int ret;
+
+    ret = ev_wait(-1);
+    if (!ret)
+        ev_dispatch();
+}
+
+void healthd_mode_charger_init(struct healthd_config *config)
+{
+    int ret;
+    struct charger *charger = &charger_state;
+    int i;
+    int epollfd;
+
+    dump_last_kmsg();
+
+    LOGI("--------------- STARTING CHARGER MODE ---------------\n");
+
+    gr_init();
+    gr_font_size(&char_width, &char_height);
+
+    ret = ev_init(input_callback, charger);
+    if (!ret) {
+        epollfd = ev_get_epollfd();
+        healthd_register_event(epollfd, charger_event_handler);
+    }
+
+    ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
+    if (ret < 0) {
+        LOGE("Cannot load image\n");
+        charger->surf_unknown = NULL;
+    }
+
+    charger->batt_anim = &battery_animation;
+
+    for (i = 0; i < charger->batt_anim->num_frames; i++) {
+        struct frame *frame = &charger->batt_anim->frames[i];
+
+        ret = res_create_surface(frame->name, &frame->surface);
+        if (ret < 0) {
+            LOGE("Cannot load image %s\n", frame->name);
+            /* TODO: free the already allocated surfaces... */
+            charger->batt_anim->num_frames = 0;
+            charger->batt_anim->num_cycles = 1;
+            break;
+        }
+    }
+
+    ev_sync_key_state(set_key_callback, charger);
+
+#ifndef CHARGER_DISABLE_INIT_BLANK
+    gr_fb_blank(true);
+#endif
+
+    charger->next_screen_transition = -1;
+    charger->next_key_check = -1;
+    charger->next_pwr_check = -1;
+}
diff --git a/charger/images/battery_0.png b/healthd/images/battery_0.png
similarity index 100%
rename from charger/images/battery_0.png
rename to healthd/images/battery_0.png
Binary files differ
diff --git a/charger/images/battery_1.png b/healthd/images/battery_1.png
similarity index 100%
rename from charger/images/battery_1.png
rename to healthd/images/battery_1.png
Binary files differ
diff --git a/charger/images/battery_2.png b/healthd/images/battery_2.png
similarity index 100%
rename from charger/images/battery_2.png
rename to healthd/images/battery_2.png
Binary files differ
diff --git a/charger/images/battery_3.png b/healthd/images/battery_3.png
similarity index 100%
rename from charger/images/battery_3.png
rename to healthd/images/battery_3.png
Binary files differ
diff --git a/charger/images/battery_4.png b/healthd/images/battery_4.png
similarity index 100%
rename from charger/images/battery_4.png
rename to healthd/images/battery_4.png
Binary files differ
diff --git a/charger/images/battery_5.png b/healthd/images/battery_5.png
similarity index 100%
rename from charger/images/battery_5.png
rename to healthd/images/battery_5.png
Binary files differ
diff --git a/charger/images/battery_charge.png b/healthd/images/battery_charge.png
similarity index 100%
rename from charger/images/battery_charge.png
rename to healthd/images/battery_charge.png
Binary files differ
diff --git a/charger/images/battery_fail.png b/healthd/images/battery_fail.png
similarity index 100%
rename from charger/images/battery_fail.png
rename to healthd/images/battery_fail.png
Binary files differ
diff --git a/include/cutils/klog.h b/include/cutils/klog.h
index ba728ac..3953904 100644
--- a/include/cutils/klog.h
+++ b/include/cutils/klog.h
@@ -23,7 +23,7 @@
 
 void klog_init(void);
 void klog_set_level(int level);
-void klog_close(void);
+/* TODO: void klog_close(void); - and make klog_fd users thread safe. */
 void klog_write(int level, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
 
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 0ed0d78..53619ac 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -255,7 +255,6 @@
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
-    { 00750, AID_ROOT,      AID_SHELL,     0, "charger*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 053bfaf..fa8f03f 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -17,8 +17,8 @@
 #ifndef ANDROID_UTILS_LRU_CACHE_H
 #define ANDROID_UTILS_LRU_CACHE_H
 
+#include <UniquePtr.h>
 #include <utils/BasicHashtable.h>
-#include <utils/UniquePtr.h>
 
 namespace android {
 
diff --git a/include/utils/UniquePtr.h b/include/utils/UniquePtr.h
deleted file mode 100644
index bc62fe6..0000000
--- a/include/utils/UniquePtr.h
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
- *
- * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE
- * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC.
- *
- * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE ===
- */
-
-#ifndef UNIQUE_PTR_H_included
-#define UNIQUE_PTR_H_included
-
-#include <cstdlib> // For NULL.
-
-// Default deleter for pointer types.
-template <typename T>
-struct DefaultDelete {
-    enum { type_must_be_complete = sizeof(T) };
-    DefaultDelete() {}
-    void operator()(T* p) const {
-        delete p;
-    }
-};
-
-// Default deleter for array types.
-template <typename T>
-struct DefaultDelete<T[]> {
-    enum { type_must_be_complete = sizeof(T) };
-    void operator()(T* p) const {
-        delete[] p;
-    }
-};
-
-// A smart pointer that deletes the given pointer on destruction.
-// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
-// and boost::scoped_array).
-// Named to be in keeping with Android style but also to avoid
-// collision with any other implementation, until we can switch over
-// to unique_ptr.
-// Use thus:
-//   UniquePtr<C> c(new C);
-template <typename T, typename D = DefaultDelete<T> >
-class UniquePtr {
-public:
-    // Construct a new UniquePtr, taking ownership of the given raw pointer.
-    explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
-    }
-
-    ~UniquePtr() {
-        reset();
-    }
-
-    // Accessors.
-    T& operator*() const { return *mPtr; }
-    T* operator->() const { return mPtr; }
-    T* get() const { return mPtr; }
-
-    // Returns the raw pointer and hands over ownership to the caller.
-    // The pointer will not be deleted by UniquePtr.
-    T* release() __attribute__((warn_unused_result)) {
-        T* result = mPtr;
-        mPtr = NULL;
-        return result;
-    }
-
-    // Takes ownership of the given raw pointer.
-    // If this smart pointer previously owned a different raw pointer, that
-    // raw pointer will be freed.
-    void reset(T* ptr = NULL) {
-        if (ptr != mPtr) {
-            D()(mPtr);
-            mPtr = ptr;
-        }
-    }
-
-private:
-    // The raw pointer.
-    T* mPtr;
-
-    // Comparing unique pointers is probably a mistake, since they're unique.
-    template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
-    template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
-
-    // Disallow copy and assignment.
-    UniquePtr(const UniquePtr&);
-    void operator=(const UniquePtr&);
-};
-
-// Partial specialization for array types. Like std::unique_ptr, this removes
-// operator* and operator-> but adds operator[].
-template <typename T, typename D>
-class UniquePtr<T[], D> {
-public:
-    explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
-    }
-
-    ~UniquePtr() {
-        reset();
-    }
-
-    T& operator[](size_t i) const {
-        return mPtr[i];
-    }
-    T* get() const { return mPtr; }
-
-    T* release() __attribute__((warn_unused_result)) {
-        T* result = mPtr;
-        mPtr = NULL;
-        return result;
-    }
-
-    void reset(T* ptr = NULL) {
-        if (ptr != mPtr) {
-            D()(mPtr);
-            mPtr = ptr;
-        }
-    }
-
-private:
-    T* mPtr;
-
-    // Disallow copy and assignment.
-    UniquePtr(const UniquePtr&);
-    void operator=(const UniquePtr&);
-};
-
-#if UNIQUE_PTR_TESTS
-
-// Run these tests with:
-// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out
-
-#include <stdio.h>
-
-static void assert(bool b) {
-    if (!b) {
-        fprintf(stderr, "FAIL\n");
-        abort();
-    }
-    fprintf(stderr, "OK\n");
-}
-static int cCount = 0;
-struct C {
-    C() { ++cCount; }
-    ~C() { --cCount; }
-};
-static bool freed = false;
-struct Freer {
-    void operator()(int* p) {
-        assert(*p == 123);
-        free(p);
-        freed = true;
-    }
-};
-
-int main(int argc, char* argv[]) {
-    //
-    // UniquePtr<T> tests...
-    //
-
-    // Can we free a single object?
-    {
-        UniquePtr<C> c(new C);
-        assert(cCount == 1);
-    }
-    assert(cCount == 0);
-    // Does release work?
-    C* rawC;
-    {
-        UniquePtr<C> c(new C);
-        assert(cCount == 1);
-        rawC = c.release();
-    }
-    assert(cCount == 1);
-    delete rawC;
-    // Does reset work?
-    {
-        UniquePtr<C> c(new C);
-        assert(cCount == 1);
-        c.reset(new C);
-        assert(cCount == 1);
-    }
-    assert(cCount == 0);
-
-    //
-    // UniquePtr<T[]> tests...
-    //
-
-    // Can we free an array?
-    {
-        UniquePtr<C[]> cs(new C[4]);
-        assert(cCount == 4);
-    }
-    assert(cCount == 0);
-    // Does release work?
-    {
-        UniquePtr<C[]> c(new C[4]);
-        assert(cCount == 4);
-        rawC = c.release();
-    }
-    assert(cCount == 4);
-    delete[] rawC;
-    // Does reset work?
-    {
-        UniquePtr<C[]> c(new C[4]);
-        assert(cCount == 4);
-        c.reset(new C[2]);
-        assert(cCount == 2);
-    }
-    assert(cCount == 0);
-
-    //
-    // Custom deleter tests...
-    //
-    assert(!freed);
-    {
-        UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int))));
-        *i = 123;
-    }
-    assert(freed);
-    return 0;
-}
-#endif
-
-#endif  // UNIQUE_PTR_H_included
diff --git a/init/property_service.c b/init/property_service.c
index c370769..1b9327c 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -281,7 +281,6 @@
 static bool is_legal_property_name(const char* name, size_t namelen)
 {
     size_t i;
-    bool previous_was_dot = false;
     if (namelen >= PROP_NAME_MAX) return false;
     if (namelen < 1) return false;
     if (name[0] == '.') return false;
@@ -291,11 +290,10 @@
     /* Don't allow ".." to appear in a property name */
     for (i = 0; i < namelen; i++) {
         if (name[i] == '.') {
-            if (previous_was_dot == true) return false;
-            previous_was_dot = true;
+            // i=0 is guaranteed to never have a dot. See above.
+            if (name[i-1] == '.') return false;
             continue;
         }
-        previous_was_dot = false;
         if (name[i] == '_' || name[i] == '-') continue;
         if (name[i] >= 'a' && name[i] <= 'z') continue;
         if (name[i] >= 'A' && name[i] <= 'Z') continue;
diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c
index 9cc6e01..073b24a 100644
--- a/libbacktrace/map_info.c
+++ b/libbacktrace/map_info.c
@@ -21,7 +21,7 @@
 #include <limits.h>
 #include <pthread.h>
 #include <unistd.h>
-#include <cutils/log.h>
+#include <log/log.h>
 #include <sys/time.h>
 
 #include <backtrace/backtrace.h>
diff --git a/libnl_2/socket.c b/libnl_2/socket.c
index e94eb9e..f51cf56 100644
--- a/libnl_2/socket.c
+++ b/libnl_2/socket.c
@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <malloc.h>
 #include <sys/time.h>
 #include <sys/socket.h>
@@ -139,3 +140,14 @@
 {
 	return nl_cb_get(sk->s_cb);
 }
+
+int nl_socket_set_nonblocking(struct nl_sock *sk)
+{
+	if (sk->s_fd == -1)
+		return -NLE_BAD_SOCK;
+
+	if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
+		return -errno;
+
+	return 0;
+}
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
index 9025cc0..a21e090 100644
--- a/libsparse/Android.mk
+++ b/libsparse/Android.mk
@@ -82,8 +82,8 @@
 
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := simg2simg.c
-LOCAL_MODULE := simg2simg
+LOCAL_SRC_FILES := append2simg.c
+LOCAL_MODULE := append2simg
 LOCAL_STATIC_LIBRARIES := \
     libsparse_host \
     libz
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
new file mode 100644
index 0000000..180584f
--- /dev/null
+++ b/libsparse/append2simg.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _FILE_OFFSET_BITS 64
+#define _LARGEFILE64_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sparse/sparse.h>
+#include "sparse_file.h"
+#include "backed_block.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#endif
+#if defined(__APPLE__) && defined(__MACH__)
+#define lseek64 lseek
+#define off64_t off_t
+#endif
+
+void usage()
+{
+    fprintf(stderr, "Usage: append2simg <output> <input>\n");
+}
+
+int main(int argc, char *argv[])
+{
+    int output;
+    int output_block;
+    char *output_path;
+    struct sparse_file *sparse_output;
+
+    int input;
+    char *input_path;
+    off64_t input_len;
+
+    if (argc == 3) {
+        output_path = argv[1];
+        input_path = argv[2];
+    } else {
+        usage();
+        exit(-1);
+    }
+
+    output = open(output_path, O_RDWR | O_BINARY);
+    if (output < 0) {
+        fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
+        exit(-1);
+    }
+
+    sparse_output = sparse_file_import_auto(output, true);
+    if (!sparse_output) {
+        fprintf(stderr, "Couldn't import output file\n");
+        exit(-1);
+    }
+
+    input = open(input_path, O_RDONLY | O_BINARY);
+    if (input < 0) {
+        fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
+        exit(-1);
+    }
+
+    input_len = lseek64(input, 0, SEEK_END);
+    if (input_len < 0) {
+        fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
+        exit(-1);
+    } else if (input_len % sparse_output->block_size) {
+        fprintf(stderr, "Input file is not a multiple of the output file's block size");
+        exit(-1);
+    }
+    lseek64(input, 0, SEEK_SET);
+
+    output_block = sparse_output->len / sparse_output->block_size;
+    if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
+        fprintf(stderr, "Couldn't add input file\n");
+        exit(-1);
+    }
+    sparse_output->len += input_len;
+
+    lseek64(output, 0, SEEK_SET);
+    if (sparse_file_write(sparse_output, output, false, true, false) < 0) {
+        fprintf(stderr, "Failed to write sparse file\n");
+        exit(-1);
+    }
+
+    sparse_file_destroy(sparse_output);
+    close(output);
+    close(input);
+    exit(0);
+}
diff --git a/lmkd/Android.mk b/lmkd/Android.mk
new file mode 100644
index 0000000..5d6d1d2
--- /dev/null
+++ b/lmkd/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := lmkd.c
+LOCAL_STATIC_LIBRARIES := libcutils liblog libm libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+LOCAL_MODULE := lmkd
+
+include $(BUILD_EXECUTABLE)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
new file mode 100644
index 0000000..e489e81
--- /dev/null
+++ b/lmkd/lmkd.c
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "lowmemorykiller"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <cutils/sockets.h>
+
+#define MEMCG_SYSFS_PATH "/dev/memcg/"
+#define MEMPRESSURE_WATCH_LEVEL "medium"
+#define ZONEINFO_PATH "/proc/zoneinfo"
+#define LINE_MAX 128
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define INKERNEL_ADJ_PATH "/sys/module/lowmemorykiller/parameters/adj"
+
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
+
+enum lmk_cmd {
+    LMK_TARGET,
+    LMK_PROCPRIO,
+    LMK_PROCREMOVE,
+};
+
+#define MAX_TARGETS 6
+/*
+ * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
+ * values
+ */
+#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* default to old in-kernel interface if no memory pressure events */
+static int use_inkernel_interface = 1;
+
+/* memory pressure level medium event */
+static int mpevfd;
+
+/* control socket listen and data */
+static int ctrl_lfd;
+static int ctrl_dfd = -1;
+static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+
+/* 1 memory pressure level, 1 ctrl listen socket, 1 ctrl data socket */
+#define MAX_EPOLL_EVENTS 3
+static int epollfd;
+static int maxevents;
+
+#define OOM_DISABLE (-17)
+/* inclusive */
+#define OOM_ADJUST_MIN (-16)
+#define OOM_ADJUST_MAX 15
+
+/* kernel OOM score values */
+#define OOM_SCORE_ADJ_MIN       (-1000)
+#define OOM_SCORE_ADJ_MAX       1000
+
+static int lowmem_adj[MAX_TARGETS];
+static int lowmem_minfree[MAX_TARGETS];
+static int lowmem_targets_size;
+
+struct sysmeminfo {
+    int nr_free_pages;
+    int nr_file_pages;
+    int nr_shmem;
+    int totalreserve_pages;
+};
+
+struct adjslot_list {
+    struct adjslot_list *next;
+    struct adjslot_list *prev;
+};
+
+struct proc {
+    struct adjslot_list asl;
+    int pid;
+    int oomadj;
+    struct proc *pidhash_next;
+};
+
+#define PIDHASH_SZ 1024
+static struct proc *pidhash[PIDHASH_SZ];
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
+#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+
+/*
+ * Wait 1-2 seconds for the death report of a killed process prior to
+ * considering killing more processes.
+ */
+#define KILL_TIMEOUT 2
+/* Time of last process kill we initiated, stop me before I kill again */
+static time_t kill_lasttime;
+
+/* PAGE_SIZE / 1024 */
+static long page_k;
+
+static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
+{
+    if (oom_adj == OOM_ADJUST_MAX)
+        return OOM_SCORE_ADJ_MAX;
+    else
+        return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
+}
+
+static struct proc *pid_lookup(int pid) {
+    struct proc *procp;
+
+    for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid;
+         procp = procp->pidhash_next)
+            ;
+
+    return procp;
+}
+
+static void adjslot_insert(struct adjslot_list *head, struct adjslot_list *new)
+{
+    struct adjslot_list *next = head->next;
+    new->prev = head;
+    new->next = next;
+    next->prev = new;
+    head->next = new;
+}
+
+static void adjslot_remove(struct adjslot_list *old)
+{
+    struct adjslot_list *prev = old->prev;
+    struct adjslot_list *next = old->next;
+    next->prev = prev;
+    prev->next = next;
+}
+
+static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
+    struct adjslot_list *asl = head->prev;
+
+    return asl == head ? NULL : asl;
+}
+
+static void proc_slot(struct proc *procp) {
+    int adjslot = ADJTOSLOT(procp->oomadj);
+
+    adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
+}
+
+static void proc_unslot(struct proc *procp) {
+    adjslot_remove(&procp->asl);
+}
+
+static void proc_insert(struct proc *procp) {
+    int hval = pid_hashfn(procp->pid);
+
+    procp->pidhash_next = pidhash[hval];
+    pidhash[hval] = procp;
+    proc_slot(procp);
+}
+
+static int pid_remove(int pid) {
+    int hval = pid_hashfn(pid);
+    struct proc *procp;
+    struct proc *prevp;
+
+    for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
+         procp = procp->pidhash_next)
+            prevp = procp;
+
+    if (!procp)
+        return -1;
+
+    if (!prevp)
+        pidhash[hval] = procp->pidhash_next;
+    else
+        prevp->pidhash_next = procp->pidhash_next;
+
+    proc_unslot(procp);
+    free(procp);
+    return 0;
+}
+
+static void writefilestring(char *path, char *s) {
+    int fd = open(path, O_WRONLY);
+    int len = strlen(s);
+    int ret;
+
+    if (fd < 0) {
+        ALOGE("Error opening %s; errno=%d", path, errno);
+        return;
+    }
+
+    ret = write(fd, s, len);
+    if (ret < 0) {
+        ALOGE("Error writing %s; errno=%d", path, errno);
+    } else if (ret < len) {
+        ALOGE("Short write on %s; length=%d", path, ret);
+    }
+
+    close(fd);
+}
+
+static void cmd_procprio(int pid, int oomadj) {
+    struct proc *procp;
+    char path[80];
+    char val[20];
+
+    if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+        ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+        return;
+    }
+
+    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
+    snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
+    writefilestring(path, val);
+
+    if (use_inkernel_interface)
+        return;
+
+    procp = pid_lookup(pid);
+    if (!procp) {
+            procp = malloc(sizeof(struct proc));
+            if (!procp) {
+                // Oh, the irony.  May need to rebuild our state.
+                return;
+            }
+
+            procp->pid = pid;
+            procp->oomadj = oomadj;
+            proc_insert(procp);
+    } else {
+        proc_unslot(procp);
+        procp->oomadj = oomadj;
+        proc_slot(procp);
+    }
+}
+
+static void cmd_procremove(int pid) {
+    struct proc *procp;
+
+    if (use_inkernel_interface)
+        return;
+
+    pid_remove(pid);
+    kill_lasttime = 0;
+}
+
+static void cmd_target(int ntargets, int *params) {
+    int i;
+
+    if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
+        return;
+
+    for (i = 0; i < ntargets; i++) {
+        lowmem_minfree[i] = ntohl(*params++);
+        lowmem_adj[i] = ntohl(*params++);
+    }
+
+    lowmem_targets_size = ntargets;
+
+    if (use_inkernel_interface) {
+        char minfreestr[128];
+        char killpriostr[128];
+
+        minfreestr[0] = '\0';
+        killpriostr[0] = '\0';
+
+        for (i = 0; i < lowmem_targets_size; i++) {
+            char val[40];
+
+            if (i) {
+                strlcat(minfreestr, ",", sizeof(minfreestr));
+                strlcat(killpriostr, ",", sizeof(killpriostr));
+            }
+
+            snprintf(val, sizeof(val), "%d", lowmem_minfree[i]);
+            strlcat(minfreestr, val, sizeof(minfreestr));
+            snprintf(val, sizeof(val), "%d", lowmem_adj[i]);
+            strlcat(killpriostr, val, sizeof(killpriostr));
+        }
+
+        writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
+        writefilestring(INKERNEL_ADJ_PATH, killpriostr);
+    }
+}
+
+static void ctrl_data_close(void) {
+    ALOGI("Closing Activity Manager data connection");
+    close(ctrl_dfd);
+    ctrl_dfd = -1;
+    maxevents--;
+}
+
+static int ctrl_data_read(char *buf, size_t bufsz) {
+    int ret = 0;
+
+    ret = read(ctrl_dfd, buf, bufsz);
+
+    if (ret == -1) {
+        ALOGE("control data socket read failed; errno=%d", errno);
+    } else if (ret == 0) {
+        ALOGE("Got EOF on control data socket");
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static void ctrl_command_handler(void) {
+    int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+    int len;
+    int cmd = -1;
+    int nargs;
+    int targets;
+
+    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+    if (len <= 0)
+        return;
+
+    nargs = len / sizeof(int) - 1;
+    if (nargs < 0)
+        goto wronglen;
+
+    cmd = ntohl(ibuf[0]);
+
+    switch(cmd) {
+    case LMK_TARGET:
+        targets = nargs / 2;
+        if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
+            goto wronglen;
+        cmd_target(targets, &ibuf[1]);
+        break;
+    case LMK_PROCPRIO:
+        if (nargs != 2)
+            goto wronglen;
+        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]));
+        break;
+    case LMK_PROCREMOVE:
+        if (nargs != 1)
+            goto wronglen;
+        cmd_procremove(ntohl(ibuf[1]));
+        break;
+    default:
+        ALOGE("Received unknown command code %d", cmd);
+        return;
+    }
+
+    return;
+
+wronglen:
+    ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
+}
+
+static void ctrl_data_handler(uint32_t events) {
+    if (events & EPOLLHUP) {
+        ALOGI("ActivityManager disconnected");
+        if (!ctrl_dfd_reopened)
+            ctrl_data_close();
+    } else if (events & EPOLLIN) {
+        ctrl_command_handler();
+    }
+}
+
+static void ctrl_connect_handler(uint32_t events) {
+    struct sockaddr addr;
+    socklen_t alen;
+    struct epoll_event epev;
+
+    if (ctrl_dfd >= 0) {
+        ctrl_data_close();
+        ctrl_dfd_reopened = 1;
+    }
+
+    alen = sizeof(addr);
+    ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
+
+    if (ctrl_dfd < 0) {
+        ALOGE("lmkd control socket accept failed; errno=%d", errno);
+        return;
+    }
+
+    ALOGI("ActivityManager connected");
+    maxevents++;
+    epev.events = EPOLLIN;
+    epev.data.ptr = (void *)ctrl_data_handler;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+        ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
+        ctrl_data_close();
+        return;
+    }
+}
+
+static int zoneinfo_parse_protection(char *cp) {
+    int max = 0;
+    int zoneval;
+
+    if (*cp++ != '(')
+        return 0;
+
+    do {
+        zoneval = strtol(cp, &cp, 0);
+        if ((*cp != ',') && (*cp != ')'))
+            return 0;
+        if (zoneval > max)
+            max = zoneval;
+    } while (cp = strtok(NULL, " "));
+
+    return max;
+}
+
+static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
+    char *cp = line;
+    char *ap;
+
+    cp = strtok(line, " ");
+    if (!cp)
+        return;
+
+    ap = strtok(NULL, " ");
+    if (!ap)
+        return;
+
+    if (!strcmp(cp, "nr_free_pages"))
+        mip->nr_free_pages += strtol(ap, NULL, 0);
+    else if (!strcmp(cp, "nr_file_pages"))
+        mip->nr_file_pages += strtol(ap, NULL, 0);
+    else if (!strcmp(cp, "nr_shmem"))
+        mip->nr_shmem += strtol(ap, NULL, 0);
+    else if (!strcmp(cp, "high"))
+        mip->totalreserve_pages += strtol(ap, NULL, 0);
+    else if (!strcmp(cp, "protection:"))
+        mip->totalreserve_pages += zoneinfo_parse_protection(ap);
+}
+
+static int zoneinfo_parse(struct sysmeminfo *mip) {
+    FILE *f;
+    char *cp;
+    char line[LINE_MAX];
+
+    memset(mip, 0, sizeof(struct sysmeminfo));
+    f = fopen(ZONEINFO_PATH, "r");
+    if (!f) {
+        ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+        return -1;
+    }
+
+    while (fgets(line, LINE_MAX, f))
+            zoneinfo_parse_line(line, mip);
+
+    fclose(f);
+    return 0;
+}
+
+static int proc_get_size(int pid) {
+    char path[PATH_MAX];
+    char line[LINE_MAX];
+    FILE *f;
+    int rss = 0;
+    int total;
+
+    snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+    f = fopen(path, "r");
+    if (!f)
+        return -1;
+    if (!fgets(line, LINE_MAX, f)) {
+        fclose(f);
+        return -1;
+    }
+
+    sscanf(line, "%d %d ", &total, &rss);
+    fclose(f);
+    return rss;
+}
+
+static char *proc_get_name(int pid) {
+    char path[PATH_MAX];
+    static char line[LINE_MAX];
+    FILE *f;
+    char *cp;
+
+    snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+    f = fopen(path, "r");
+    if (!f)
+        return NULL;
+    if (!fgets(line, LINE_MAX, f)) {
+        fclose(f);
+        return NULL;
+    }
+
+    cp = strchr(line, ' ');
+    if (cp)
+        *cp = '\0';
+
+    return line;
+}
+
+static struct proc *proc_adj_lru(int oomadj) {
+    return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
+}
+
+static void mp_event(uint32_t events) {
+    int i;
+    int ret;
+    unsigned long long evcount;
+    struct sysmeminfo mi;
+    int other_free;
+    int other_file;
+    int minfree = 0;
+    int min_score_adj = OOM_ADJUST_MAX + 1;
+
+    ret = read(mpevfd, &evcount, sizeof(evcount));
+    if (ret < 0)
+        ALOGE("Error reading memory pressure event fd; errno=%d",
+              errno);
+
+    if (time(NULL) - kill_lasttime < KILL_TIMEOUT)
+        return;
+
+    if (zoneinfo_parse(&mi) < 0)
+        return;
+
+    other_free = mi.nr_free_pages - mi.totalreserve_pages;
+    other_file = mi.nr_file_pages - mi.nr_shmem;
+
+    for (i = 0; i < lowmem_targets_size; i++) {
+        minfree = lowmem_minfree[i];
+        if (other_free < minfree && other_file < minfree) {
+            min_score_adj = lowmem_adj[i];
+            break;
+        }
+    }
+
+    if (min_score_adj == OOM_ADJUST_MAX + 1)
+        return;
+
+    for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+        struct proc *procp;
+
+    retry:
+        procp = proc_adj_lru(i);
+
+        if (procp) {
+            int pid = procp->pid;
+            char *taskname;
+            int tasksize;
+            int r;
+
+            taskname = proc_get_name(pid);
+            if (!taskname) {
+                pid_remove(pid);
+                goto retry;
+            }
+
+            tasksize = proc_get_size(pid);
+            if (tasksize < 0) {
+                pid_remove(pid);
+                goto retry;
+            }
+
+            ALOGI("Killing '%s' (%d), adj %d\n"
+                  "   to free %ldkB because cache %ldkB is below limit %ldkB for oom_adj %d\n"
+                  "   Free memory is %ldkB %s reserved",
+                  taskname, pid, procp->oomadj, tasksize * page_k,
+                  other_file * page_k, minfree * page_k, min_score_adj,
+                  other_free * page_k, other_free >= 0 ? "above" : "below");
+            r = kill(pid, SIGKILL);
+            pid_remove(pid);
+
+            if (r) {
+                ALOGE("kill(%d): errno=%d", procp->pid, errno);
+                goto retry;
+            } else {
+                time(&kill_lasttime);
+                break;
+            }
+        }
+    }
+}
+
+static int init_mp(char *levelstr, void *event_handler)
+{
+    int mpfd;
+    int evfd;
+    int evctlfd;
+    char buf[256];
+    struct epoll_event epev;
+    int ret;
+
+    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+    if (mpfd < 0) {
+        ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
+        goto err_open_mpfd;
+    }
+
+    evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+    if (evctlfd < 0) {
+        ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
+        goto err_open_evctlfd;
+    }
+
+    evfd = eventfd(0, EFD_NONBLOCK);
+    if (evfd < 0) {
+        ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
+        goto err_eventfd;
+    }
+
+    ret = snprintf(buf, sizeof(buf), "%d %d %s", evfd, mpfd, levelstr);
+    if (ret >= (ssize_t)sizeof(buf)) {
+        ALOGE("cgroup.event_control line overflow for level %s", levelstr);
+        goto err;
+    }
+
+    ret = write(evctlfd, buf, strlen(buf) + 1);
+    if (ret == -1) {
+        ALOGE("cgroup.event_control write failed for level %s; errno=%d",
+              levelstr, errno);
+        goto err;
+    }
+
+    epev.events = EPOLLIN;
+    epev.data.ptr = event_handler;
+    ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
+    if (ret == -1) {
+        ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
+        goto err;
+    }
+    maxevents++;
+    mpevfd = evfd;
+    return 0;
+
+err:
+    close(evfd);
+err_eventfd:
+    close(evctlfd);
+err_open_evctlfd:
+    close(mpfd);
+err_open_mpfd:
+    return -1;
+}
+
+static int init(void) {
+    struct epoll_event epev;
+    int i;
+    int ret;
+
+    page_k = sysconf(_SC_PAGESIZE);
+    if (page_k == -1)
+        page_k = PAGE_SIZE;
+    page_k /= 1024;
+
+    epollfd = epoll_create(MAX_EPOLL_EVENTS);
+    if (epollfd == -1) {
+        ALOGE("epoll_create failed (errno=%d)", errno);
+        return -1;
+    }
+
+    ctrl_lfd = android_get_control_socket("lmkd");
+    if (ctrl_lfd < 0) {
+        ALOGE("get lmkd control socket failed");
+        return -1;
+    }
+
+    ret = listen(ctrl_lfd, 1);
+    if (ret < 0) {
+        ALOGE("lmkd control socket listen failed (errno=%d)", errno);
+        return -1;
+    }
+
+    epev.events = EPOLLIN;
+    epev.data.ptr = (void *)ctrl_connect_handler;
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+        ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
+        return -1;
+    }
+    maxevents++;
+
+    use_inkernel_interface = !access(INKERNEL_MINFREE_PATH, W_OK);
+
+    if (use_inkernel_interface) {
+        ALOGI("Using in-kernel low memory killer interface");
+    } else {
+        ret = init_mp(MEMPRESSURE_WATCH_LEVEL, (void *)&mp_event);
+        if (ret)
+            ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+    }
+
+    for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+        procadjslot_list[i].next = &procadjslot_list[i];
+        procadjslot_list[i].prev = &procadjslot_list[i];
+    }
+
+    return 0;
+}
+
+static void mainloop(void) {
+    while (1) {
+        struct epoll_event events[maxevents];
+        int nevents;
+        int i;
+
+        ctrl_dfd_reopened = 0;
+        nevents = epoll_wait(epollfd, events, maxevents, -1);
+
+        if (nevents == -1) {
+            if (errno == EINTR)
+                continue;
+            ALOGE("epoll_wait failed (errno=%d)", errno);
+            continue;
+        }
+
+        for (i = 0; i < nevents; ++i) {
+            if (events[i].events & EPOLLERR)
+                ALOGD("EPOLLERR on event #%d", i);
+            if (events[i].data.ptr)
+                (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+        }
+    }
+}
+
+int main(int argc, char **argv) {
+    if (!init())
+        mainloop();
+
+    ALOGI("exiting");
+    return 0;
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 400c09e..4bf69be 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -11,7 +11,7 @@
 
 on early-init
     # Set init and its forked children's oom_adj.
-    write /proc/1/oom_adj -16
+    write /proc/1/oom_score_adj -1000
 
     # Set the security context for the init process.
     # This should occur before anything else (e.g. ueventd) is started.
@@ -85,6 +85,10 @@
     mkdir /mnt/obb 0700 root system
     mount tmpfs tmpfs /mnt/obb mode=0755,gid=1000
 
+    # memory control cgroup
+    mkdir /dev/memcg 0700 root system
+    mount cgroup none /dev/memcg memory
+
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/kernel/hung_task_timeout_secs 0
     write /proc/cpu/alignment 4
@@ -421,11 +425,6 @@
     critical
     seclabel u:r:healthd:s0
 
-service healthd-charger /sbin/healthd -n
-    class charger
-    critical
-    seclabel u:r:healthd:s0
-
 service console /system/bin/sh
     class core
     console
@@ -447,6 +446,11 @@
 on property:ro.kernel.qemu=1
     start adbd
 
+service lmkd /system/bin/lmkd
+    class core
+    critical
+    socket lmkd seqpacket 0660 system system
+
 service servicemanager /system/bin/servicemanager
     class core
     user system
@@ -456,6 +460,7 @@
     onrestart restart zygote
     onrestart restart media
     onrestart restart surfaceflinger
+    onrestart restart inputflinger
     onrestart restart drm
 
 service vold /system/bin/vold
@@ -485,6 +490,12 @@
     group graphics drmrpc
     onrestart restart zygote
 
+service inputflinger /system/bin/inputflinger
+    class main
+    user system
+    group input
+    onrestart restart zygote
+
 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
     class main
     socket zygote stream 660 root system
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 75ce53f..c5890b2 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -69,7 +69,8 @@
 	swapon \
 	swapoff \
 	mkswap \
-	readlink
+	readlink \
+	nohup
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 TOOLS += r
diff --git a/toolbox/ioctl.c b/toolbox/ioctl.c
index fb555d2..fd24885 100644
--- a/toolbox/ioctl.c
+++ b/toolbox/ioctl.c
@@ -63,10 +63,14 @@
         exit(1);
     }
 
-    fd = open(argv[optind], O_RDWR | O_SYNC);
-    if (fd < 0) {
-        fprintf(stderr, "cannot open %s\n", argv[optind]);
-        return 1;
+    if (!strcmp(argv[optind], "-")) {
+        fd = STDIN_FILENO;
+    } else {
+        fd = open(argv[optind], read_only ? O_RDONLY : (O_RDWR | O_SYNC));
+        if (fd < 0) {
+            fprintf(stderr, "cannot open %s\n", argv[optind]);
+            return 1;
+        }
     }
     optind++;
     
diff --git a/toolbox/nohup.c b/toolbox/nohup.c
new file mode 100644
index 0000000..363999d
--- /dev/null
+++ b/toolbox/nohup.c
@@ -0,0 +1,26 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int nohup_main(int argc, char *argv[])
+{
+    if (argc < 2) {
+        fprintf(stderr, "Usage: %s [-n] program args...\n", argv[0]);
+        return EXIT_FAILURE;
+    }
+    signal(SIGHUP, SIG_IGN);
+    argv++;
+    if (strcmp(argv[0], "-n") == 0) {
+        argv++;
+        signal(SIGINT, SIG_IGN);
+        signal(SIGSTOP, SIG_IGN);
+        signal(SIGTTIN, SIG_IGN);
+        signal(SIGTTOU, SIG_IGN);
+        signal(SIGQUIT, SIG_IGN);
+        signal(SIGTERM, SIG_IGN);
+    }
+    execvp(argv[0], argv);
+    perror(argv[0]);
+    return EXIT_FAILURE;
+}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 7c35ccb..de141fc 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -28,6 +28,7 @@
 #define SHOW_POLICY 4
 #define SHOW_CPU  8
 #define SHOW_MACLABEL 16
+#define SHOW_NUMERIC_UID 32
 
 static int display_flags = 0;
 
@@ -45,7 +46,7 @@
     unsigned utime, stime;
     int prio, nice, rtprio, sched, psr;
     struct passwd *pw;
-    
+
     sprintf(statline, "/proc/%d", pid);
     stat(statline, &stats);
 
@@ -67,7 +68,7 @@
         }
         cmdline[r] = 0;
     }
-    
+
     fd = open(statline, O_RDONLY);
     if(fd == 0) return -1;
     r = read(fd, statline, 1023);
@@ -89,7 +90,7 @@
     nexttok(&ptr); // pgrp
     nexttok(&ptr); // sid
     tty = atoi(nexttok(&ptr));
-    
+
     nexttok(&ptr); // tpgid
     nexttok(&ptr); // flags
     nexttok(&ptr); // minflt
@@ -129,21 +130,21 @@
     psr = atoi(nexttok(&ptr)); // processor
     rtprio = atoi(nexttok(&ptr)); // rt_priority
     sched = atoi(nexttok(&ptr)); // scheduling policy
-    
+
     tty = atoi(nexttok(&ptr));
-    
+
     if(tid != 0) {
         ppid = pid;
         pid = tid;
     }
 
     pw = getpwuid(stats.st_uid);
-    if(pw == 0) {
+    if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
         sprintf(user,"%d",(int)stats.st_uid);
     } else {
         strcpy(user,pw->pw_name);
     }
-    
+
     if(!namefilter || !strncmp(name, namefilter, strlen(namefilter))) {
         if (display_flags & SHOW_MACLABEL) {
             fd = open(macline, O_RDONLY);
@@ -189,7 +190,7 @@
     sprintf(tmp,"/proc/%d/task",pid);
     d = opendir(tmp);
     if(d == 0) return;
-    
+
     while((de = readdir(d)) != 0){
         if(isdigit(de->d_name[0])){
             int tid = atoi(de->d_name);
@@ -197,7 +198,7 @@
             ps_line(pid, tid, namefilter);
         }
     }
-    closedir(d);    
+    closedir(d);
 }
 
 int ps_main(int argc, char **argv)
@@ -207,13 +208,15 @@
     char *namefilter = 0;
     int pidfilter = 0;
     int threads = 0;
-    
+
     d = opendir("/proc");
     if(d == 0) return -1;
 
     while(argc > 1){
         if(!strcmp(argv[1],"-t")) {
             threads = 1;
+        } else if(!strcmp(argv[1],"-n")) {
+            display_flags |= SHOW_NUMERIC_UID;
         } else if(!strcmp(argv[1],"-x")) {
             display_flags |= SHOW_TIME;
         } else if(!strcmp(argv[1], "-Z")) {