Merge "healthd: Fix negativity check after cast to unsigned enum" am: 3dad67af05 am: 4734981245
am: eb066ae098

Change-Id: I2327adfd50fceb232ef2a52e535914743c25cf3a
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index d126f52..6a80bcd 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1944,7 +1944,8 @@
     for (int i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
 
-        if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+        if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+            android::base::EndsWithIgnoreCase(file, ".dm")) {
             struct stat sb;
             if (stat(file, &sb) != -1) total_size += sb.st_size;
             first_apk = i;
@@ -2005,9 +2006,8 @@
         }
 
         std::string cmd = android::base::StringPrintf(
-                "%s install-write -S %" PRIu64 " %d %d_%s -",
-                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
-                android::base::Basename(file).c_str());
+            "%s install-write -S %" PRIu64 " %d %s -", install_cmd.c_str(),
+            static_cast<uint64_t>(sb.st_size), session_id, android::base::Basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
diff --git a/adf/libadf/adf.cpp b/adf/libadf/adf.cpp
index 60d8ef0..fd9c208 100644
--- a/adf/libadf/adf.cpp
+++ b/adf/libadf/adf.cpp
@@ -132,8 +132,11 @@
 void adf_free_device_data(struct adf_device_data *data)
 {
     delete [] data->attachments;
+    data->attachments = nullptr;
     delete [] data->allowed_attachments;
+    data->allowed_attachments = nullptr;
     delete [] static_cast<char *>(data->custom_data);
+    data->custom_data = nullptr;
 }
 
 int adf_device_post(struct adf_device *dev,
@@ -236,9 +239,10 @@
         return err;
 
     std::vector<adf_id_t> ids;
-    for (size_t i = 0; i < data.n_allowed_attachments; i++)
-        if (data.allowed_attachments[i].overlay_engine == overlay_engine)
-            ids.push_back(data.allowed_attachments[i].interface);
+    if (data.allowed_attachments != nullptr)
+        for (size_t i = 0; i < data.n_allowed_attachments; i++)
+            if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+              ids.push_back(data.allowed_attachments[i].interface);
 
     adf_free_device_data(&data);
     return adf_id_vector_to_array(ids, interfaces);
@@ -450,9 +454,10 @@
         return err;
 
     std::vector<adf_id_t> ids;
-    for (size_t i = 0; i < data.n_allowed_attachments; i++)
-        if (data.allowed_attachments[i].interface == interface)
-            ids.push_back(data.allowed_attachments[i].overlay_engine);
+    if (data.allowed_attachments != nullptr)
+        for (size_t i = 0; i < data.n_allowed_attachments; i++)
+            if (data.allowed_attachments[i].interface == interface)
+                ids.push_back(data.allowed_attachments[i].overlay_engine);
 
     return adf_id_vector_to_array(ids, overlay_engines);
 }
@@ -551,7 +556,9 @@
 void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
 {
     delete [] data->supported_formats;
+    data->supported_formats = nullptr;
     delete [] static_cast<char *>(data->custom_data);
+    data->custom_data = nullptr;
 }
 
 bool adf_overlay_engine_supports_format(int fd, __u32 format)
@@ -564,10 +571,12 @@
     if (err < 0)
         return false;
 
-    for (i = 0; i < data.n_supported_formats; i++) {
-        if (data.supported_formats[i] == format) {
-            ret = true;
-            break;
+    if (data.supported_formats != nullptr) {
+        for (i = 0; i < data.n_supported_formats; i++) {
+            if (data.supported_formats[i] == format) {
+                ret = true;
+                break;
+            }
         }
     }
 
@@ -638,18 +647,18 @@
         const __u32 *formats, size_t n_formats,
         adf_id_t interface, adf_id_t *overlay_engine)
 {
-    adf_id_t *engs;
+    adf_id_t *engs = nullptr;
     ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
 
-    if (n_engs <= 0)
+    if (engs == nullptr)
         return false;
 
-    adf_id_t *filtered_engs;
+    adf_id_t *filtered_engs = nullptr;
     ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
             formats, n_formats, engs, n_engs, &filtered_engs);
     free(engs);
 
-    if (n_filtered_engs <= 0)
+    if (filtered_engs == nullptr)
         return false;
 
     *overlay_engine = filtered_engs[0];
@@ -700,17 +709,17 @@
 
     if (n_intfs < 0)
         return n_intfs;
-    else if (!n_intfs)
+    else if (!intfs)
         return -ENODEV;
 
-    adf_id_t *primary_intfs;
+    adf_id_t *primary_intfs = nullptr;
     ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
             ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
     free(intfs);
 
     if (n_primary_intfs < 0)
         return n_primary_intfs;
-    else if (!n_primary_intfs)
+    else if (!primary_intfs)
         return -ENODEV;
 
     if (!formats) {
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd9ba88..3ddadbc 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,10 @@
     name: "bootstat",
     defaults: ["bootstat_defaults"],
     static_libs: ["libbootstat"],
-    shared_libs: ["liblogcat"],
+    shared_libs: [
+        "liblogcat",
+        "libstatslog"
+    ],
     init_rc: ["bootstat.rc"],
     product_variables: {
         pdk: {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a1fe6ed..d8ee899 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -42,6 +42,7 @@
 #include <cutils/properties.h>
 #include <log/logcat.h>
 #include <metricslogger/metrics_logger.h>
+#include <statslog.h>
 
 #include "boot_event_record_store.h"
 
@@ -881,6 +882,16 @@
   return timings;
 }
 
+// Returns the total bootloader boot time from the ro.boot.boottime system property.
+int32_t GetBootloaderTime(const BootloaderTimingMap& bootloader_timings) {
+  int32_t total_time = 0;
+  for (const auto& timing : bootloader_timings) {
+    total_time += timing.second;
+  }
+
+  return total_time;
+}
+
 // Parses and records the set of bootloader stages and associated boot times
 // from the ro.boot.boottime system property.
 void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
@@ -894,11 +905,10 @@
   boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
 }
 
-// Records the closest estimation to the absolute device boot time, i.e.,
+// Returns the closest estimation to the absolute device boot time, i.e.,
 // from power on to boot_complete, including bootloader times.
-void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
-                            const BootloaderTimingMap& bootloader_timings,
-                            std::chrono::milliseconds uptime) {
+std::chrono::milliseconds GetAbsoluteBootTime(const BootloaderTimingMap& bootloader_timings,
+                                              std::chrono::milliseconds uptime) {
   int32_t bootloader_time_ms = 0;
 
   for (const auto& timing : bootloader_timings) {
@@ -908,9 +918,43 @@
   }
 
   auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
-  auto absolute_total =
-      std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
-  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
+  return bootloader_duration + uptime;
+}
+
+// Records the closest estimation to the absolute device boot time in seconds.
+// i.e. from power on to boot_complete, including bootloader times.
+void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
+                            std::chrono::milliseconds absolute_total) {
+  auto absolute_total_sec = std::chrono::duration_cast<std::chrono::seconds>(absolute_total);
+  boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total_sec.count());
+}
+
+// Logs the total boot time and reason to statsd.
+void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
+                         std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
+                         double time_since_last_boot_sec) {
+  const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+  if (reason.empty()) {
+    android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, "<EMPTY>", "<EMPTY>",
+                               end_time.count(), total_duration.count(),
+                               (int64_t)bootloader_duration_ms,
+                               (int64_t)time_since_last_boot_sec * 1000);
+    return;
+  }
+
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
+  android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
+                             system_reason.c_str(), end_time.count(), total_duration.count(),
+                             (int64_t)bootloader_duration_ms,
+                             (int64_t)time_since_last_boot_sec * 1000);
+}
+
+void SetSystemBootReason() {
+  const std::string bootloader_boot_reason(GetProperty(bootloader_reboot_reason_property));
+  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));
+  // Record the scrubbed system_boot_reason to the property
+  SetProperty(system_reboot_reason_property, system_boot_reason);
 }
 
 // Records several metrics related to the time it takes to boot the device,
@@ -922,10 +966,11 @@
   auto time_since_epoch = android::base::boot_clock::now().time_since_epoch();
   auto uptime = std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch);
   time_t current_time_utc = time(nullptr);
+  time_t time_since_last_boot = 0;
 
   if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
     time_t last_boot_time_utc = record.second;
-    time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
+    time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
     boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
   }
 
@@ -964,10 +1009,18 @@
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
 
   const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
+  int32_t bootloader_boot_duration = GetBootloaderTime(bootloader_timings);
   RecordBootloaderTimings(&boot_event_store, bootloader_timings);
 
   auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch);
-  RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
+  auto absolute_boot_time = GetAbsoluteBootTime(bootloader_timings, uptime_ms);
+  RecordAbsoluteBootTime(&boot_event_store, absolute_boot_time);
+
+  auto boot_end_time_point = std::chrono::system_clock::now().time_since_epoch();
+  auto boot_end_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_end_time_point);
+
+  LogBootInfoToStatsd(boot_end_time, absolute_boot_time, bootloader_boot_duration,
+                      time_since_last_boot);
 }
 
 // Records the boot_reason metric by querying the ro.boot.bootreason system
@@ -991,12 +1044,10 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 
   // Log the scrubbed system_boot_reason.
-  const std::string system_reason(BootReasonStrToReason(reason));
+  const std::string system_reason(GetProperty(system_reboot_reason_property));
   int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
   boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
 
-  // Record the scrubbed system_boot_reason to the property
-  SetProperty(system_reboot_reason_property, system_reason);
   if (reason == "") {
     SetProperty(bootloader_reboot_reason_property, system_reason);
   }
@@ -1062,20 +1113,22 @@
 
   int option_index = 0;
   static const char value_str[] = "value";
+  static const char system_boot_reason_str[] = "set_system_boot_reason";
   static const char boot_complete_str[] = "record_boot_complete";
   static const char boot_reason_str[] = "record_boot_reason";
   static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
       // clang-format off
-      { "help",            no_argument,       NULL,   'h' },
-      { "log",             no_argument,       NULL,   'l' },
-      { "print",           no_argument,       NULL,   'p' },
-      { "record",          required_argument, NULL,   'r' },
-      { value_str,         required_argument, NULL,   0 },
-      { boot_complete_str, no_argument,       NULL,   0 },
-      { boot_reason_str,   no_argument,       NULL,   0 },
-      { factory_reset_str, no_argument,       NULL,   0 },
-      { NULL,              0,                 NULL,   0 }
+      { "help",                 no_argument,       NULL,   'h' },
+      { "log",                  no_argument,       NULL,   'l' },
+      { "print",                no_argument,       NULL,   'p' },
+      { "record",               required_argument, NULL,   'r' },
+      { value_str,              required_argument, NULL,   0 },
+      { system_boot_reason_str, no_argument,       NULL,   0 },
+      { boot_complete_str,      no_argument,       NULL,   0 },
+      { boot_reason_str,        no_argument,       NULL,   0 },
+      { factory_reset_str,      no_argument,       NULL,   0 },
+      { NULL,                   0,                 NULL,   0 }
       // clang-format on
   };
 
@@ -1091,6 +1144,8 @@
           // |optarg| is an external variable set by getopt representing
           // the option argument.
           value = optarg;
+        } else if (option_name == system_boot_reason_str) {
+          SetSystemBootReason();
         } else if (option_name == boot_complete_str) {
           RecordBootComplete();
         } else if (option_name == boot_reason_str) {
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index f06a38f..1300a27 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -68,8 +68,9 @@
 
 # Record boot complete metrics.
 on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
+    # Converts bootloader boot reason to system boot reason
     # Record boot_complete and related stats (decryption, etc).
     # Record the boot reason.
     # Record time since factory reset.
     # Log all boot events.
-    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
+    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 85a593f..cbd8ffa 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -115,7 +115,9 @@
 
     std::string size_str = std::to_string(dev_sz / 4096);
     const char* const args[] = {
-        "/system/bin/make_f2fs", "-f", "-O", "encrypt", fs_blkdev, size_str.c_str(), nullptr};
+        "/system/bin/make_f2fs", "-d1", "-f",
+        "-O", "encrypt", "-O", "quota",
+        fs_blkdev, size_str.c_str(), nullptr};
 
     return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
                                    LOG_KLOG, true, nullptr, nullptr, 0);
diff --git a/gatekeeperd/Android.mk b/gatekeeperd/Android.mk
index 28f0b07..6d5d1ea 100644
--- a/gatekeeperd/Android.mk
+++ b/gatekeeperd/Android.mk
@@ -32,6 +32,7 @@
 	libbase \
 	libutils \
 	libcrypto \
+	libkeystore_aidl \
 	libkeystore_binder \
 	libhidlbase \
 	libhidltransport \
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
new file mode 100644
index 0000000..9c99c6e
--- /dev/null
+++ b/gatekeeperd/OWNERS
@@ -0,0 +1,2 @@
+swillden@google.com
+jdanis@google.com
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 61c8804..5781765 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,14 +25,15 @@
 #include <unistd.h>
 #include <memory>
 
+#include <android/security/IKeystoreService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
 #include <gatekeeper/password_handle.h> // for password_handle_t
 #include <hardware/gatekeeper.h>
 #include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
 #include <keystore/keystore.h> // For error code
+#include <keystore/keystore_return_types.h>
 #include <log/log.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
@@ -315,11 +316,15 @@
             // TODO: cache service?
             sp<IServiceManager> sm = defaultServiceManager();
             sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
-            sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+            sp<security::IKeystoreService> service =
+                interface_cast<security::IKeystoreService>(binder);
             if (service != NULL) {
-                auto ret = service->addAuthToken(*auth_token, *auth_token_length);
-                if (!ret.isOk()) {
-                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
+                std::vector<uint8_t> auth_token_vector(*auth_token,
+                                                       (*auth_token) + *auth_token_length);
+                int result = 0;
+                auto binder_result = service->addAuthToken(auth_token_vector, &result);
+                if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+                    ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
                 }
             } else {
                 ALOGE("Unable to communicate with KeyStore");
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..c70278a 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,85 @@
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
 }
+
+cc_library_static {
+    name: "libbatterymonitor",
+    srcs: ["BatteryMonitor.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
+
+cc_binary {
+    name: "android.hardware.health@2.0-service",
+    init_rc: ["android.hardware.health@2.0-service.rc"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "HealthServiceDefault.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libhealthservice",
+        "libhealthstoragedefault",
+        "libbatterymonitor",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.health@2.0",
+    ],
+}
+
+cc_binary {
+    name: "healthd",
+    srcs: [
+        "HealthServiceHealthd.cpp",
+    ],
+    local_include_dirs: ["include"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libhealthservice",
+        "libbatterymonitor",
+        "libhealthstoragedefault",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+    ],
+
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..7792eaf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,35 +3,6 @@
 LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
-    healthd_mode_android.cpp \
-    BatteryPropertiesRegistrar.cpp
-
-LOCAL_MODULE := libhealthd_android
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
-    $(LOCAL_PATH) \
-    $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
-    libbatterymonitor \
-    libbatteryservice \
-    libutils \
-    libbase \
-    libcutils \
-    liblog \
-    libc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
 
 LOCAL_MODULE := libhealthd_draw
 
@@ -76,6 +47,11 @@
     $(LOCAL_PATH)/include
 
 LOCAL_STATIC_LIBRARIES := \
+    android.hardware.health@2.0 \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@1.0 \
+    android.hardware.health@1.0-convert \
+    libhealthstoragedefault \
     libminui \
     libpng \
     libz \
@@ -103,7 +79,6 @@
 endif
 
 LOCAL_SRC_FILES := \
-    healthd_common.cpp \
     charger.cpp \
 
 LOCAL_MODULE := charger
@@ -117,14 +92,17 @@
 ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 LOCAL_CFLAGS += -DCHARGER_NO_UI
 endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
 
-LOCAL_STATIC_LIBRARIES := \
+CHARGER_STATIC_LIBRARIES := \
+    android.hardware.health@2.0-impl \
+    android.hardware.health@2.0 \
+    android.hardware.health@1.0 \
+    android.hardware.health@1.0-convert \
+    libhidltransport \
+    libhidlbase \
+    libhwbinder \
+    libhealthstoragedefault \
+    libvndksupport \
     libhealthd_charger \
     libhealthd_draw \
     libbatterymonitor \
@@ -135,6 +113,8 @@
     libm \
     libc \
 
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 LOCAL_STATIC_LIBRARIES += \
     libminui \
@@ -155,6 +135,21 @@
 
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+LOCAL_MODULE := charger_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
+LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
+LOCAL_SRC_FILES := \
+    charger.cpp \
+    charger_test.cpp \
+
+include $(BUILD_EXECUTABLE)
+
+CHARGER_STATIC_LIBRARIES :=
+
 ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
 define _add-charger-image
 include $$(CLEAR_VARS)
@@ -182,41 +177,3 @@
 _add-charger-image :=
 _img_modules :=
 endif # LOCAL_CHARGER_NO_UI
-
-### healthd ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    healthd_common.cpp \
-    healthd.cpp \
-
-LOCAL_MODULE := healthd
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
-
-LOCAL_STATIC_LIBRARIES := \
-    libhealthd_android \
-    libbatterymonitor \
-    libbatteryservice \
-    android.hardware.health@1.0-convert \
-
-LOCAL_SHARED_LIBRARIES := \
-    libbinder \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libm \
-    libc \
-    libhidlbase \
-    libhidltransport \
-    android.hardware.health@1.0 \
-
-include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index fa79d0b..80c5afe 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -88,6 +88,10 @@
     initBatteryProperties(&props);
 }
 
+struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
+    return batteryMonitor->props;
+}
+
 int BatteryMonitor::getBatteryStatus(const char* status) {
     int ret;
     struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -531,12 +535,6 @@
                                       POWER_SUPPLY_SYSFS_PATH, name);
                     if (access(path, R_OK) == 0) {
                         mHealthdConfig->batteryVoltagePath = path;
-                    } else {
-                        path.clear();
-                        path.appendFormat("%s/%s/batt_vol",
-                                          POWER_SUPPLY_SYSFS_PATH, name);
-                        if (access(path, R_OK) == 0)
-                            mHealthdConfig->batteryVoltagePath = path;
                     }
                 }
 
@@ -586,12 +584,6 @@
                                       name);
                     if (access(path, R_OK) == 0) {
                         mHealthdConfig->batteryTemperaturePath = path;
-                    } else {
-                        path.clear();
-                        path.appendFormat("%s/%s/batt_temp",
-                                          POWER_SUPPLY_SYSFS_PATH, name);
-                        if (access(path, R_OK) == 0)
-                            mHealthdConfig->batteryTemperaturePath = path;
                     }
                 }
 
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
deleted file mode 100644
index e51a06d..0000000
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.
- */
-
-#include "BatteryPropertiesRegistrar.h"
-#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/healthd.h>
-
-namespace android {
-
-void BatteryPropertiesRegistrar::publish(
-    const sp<BatteryPropertiesRegistrar>& service) {
-    defaultServiceManager()->addService(String16("batteryproperties"), service);
-}
-
-void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
-    Vector<sp<IBatteryPropertiesListener> > listenersCopy;
-
-    // Binder currently may service an incoming oneway transaction whenever an
-    // outbound oneway call is made (if there is already a pending incoming
-    // oneway call waiting).  This is considered a bug and may change in the
-    // future.  For now, avoid recursive mutex lock while making outbound
-    // calls by making a local copy of the current list of listeners.
-    {
-        Mutex::Autolock _l(mRegistrationLock);
-        listenersCopy = mListeners;
-    }
-    for (size_t i = 0; i < listenersCopy.size(); i++) {
-        listenersCopy[i]->batteryPropertiesChanged(props);
-    }
-}
-
-void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
-    {
-        if (listener == NULL)
-            return;
-        Mutex::Autolock _l(mRegistrationLock);
-        // check whether this is a duplicate
-        for (size_t i = 0; i < mListeners.size(); i++) {
-            if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-                return;
-            }
-        }
-
-        mListeners.add(listener);
-        IInterface::asBinder(listener)->linkToDeath(this);
-    }
-    healthd_battery_update();
-}
-
-void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
-    if (listener == NULL)
-        return;
-    Mutex::Autolock _l(mRegistrationLock);
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
-            IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
-    return healthd_get_property(id, val);
-}
-
-void BatteryPropertiesRegistrar::scheduleUpdate() {
-    healthd_battery_update();
-}
-
-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);
-
-    for (size_t i = 0; i < mListeners.size(); i++) {
-        if (IInterface::asBinder(mListeners[i]) == who) {
-            mListeners.removeAt(i);
-            break;
-        }
-    }
-}
-
-}  // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
deleted file mode 100644
index 14e9145..0000000
--- a/healthd/BatteryPropertiesRegistrar.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_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>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-
-namespace android {
-
-class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
-                                   public IBinder::DeathRecipient {
-public:
-    void publish(const sp<BatteryPropertiesRegistrar>& service);
-    void notifyListeners(const struct BatteryProperties& props);
-    void scheduleUpdate();
-
-private:
-    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);
-};
-
-};  // namespace android
-
-#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
diff --git a/healthd/HealthServiceDefault.cpp b/healthd/HealthServiceDefault.cpp
new file mode 100644
index 0000000..89ecc2f
--- /dev/null
+++ b/healthd/HealthServiceDefault.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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 <health2/service.h>
+#include <healthd/healthd.h>
+
+void healthd_board_init(struct healthd_config*) {
+    // Implementation-defined init logic goes here.
+    // 1. config->periodic_chores_interval_* variables
+    // 2. config->battery*Path variables
+    // 3. config->energyCounter. In this implementation, energyCounter is not defined.
+
+    // use defaults
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    // Implementation-defined update logic goes here. An implementation
+    // can make modifications to prop before broadcasting it to all callbacks.
+
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+int main() {
+    return health_service_main();
+}
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
new file mode 100644
index 0000000..5fd2597
--- /dev/null
+++ b/healthd/HealthServiceHealthd.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/service.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::OK;
+using android::NAME_NOT_FOUND;
+using android::hardware::health::V1_0::HealthConfig;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::Result;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+
+using IHealthLegacy = android::hardware::health::V1_0::IHealth;
+
+static android::sp<IHealthLegacy> gHealth_1_0;
+
+static int healthd_board_get_energy_counter(int64_t* energy) {
+    if (gHealth_1_0 == nullptr) {
+        return NAME_NOT_FOUND;
+    }
+
+    Result result = Result::NOT_SUPPORTED;
+    gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
+        result = ret;
+        *energy = energyOut;
+    });
+
+    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+    gHealth_1_0 = IHealthLegacy::getService();
+
+    if (gHealth_1_0 == nullptr) {
+        return;
+    }
+
+    HealthConfig halConfig{};
+    convertToHealthConfig(config, halConfig);
+    gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
+        convertFromHealthConfig(halConfigOut, config);
+        // always redirect energy counter queries
+        config->energyCounter = healthd_board_get_energy_counter;
+    });
+    LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
+}
+
+// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
+// with logthis returned.
+int healthd_board_battery_update(struct android::BatteryProperties* props) {
+    int logthis = 0;
+
+    if (gHealth_1_0 == nullptr) {
+        return logthis;
+    }
+
+    HealthInfo info;
+    convertToHealthInfo(props, info);
+    gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
+        logthis = ret;
+        convertFromHealthInfo(infoOut, props);
+    });
+
+    return logthis;
+}
+
+int main() {
+    return health_service_main("backup");
+}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
new file mode 100644
index 0000000..8b86868
--- /dev/null
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -0,0 +1,4 @@
+service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
+    class hal
+    user system
+    group system
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 5a8fe1a..43e7fd5 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "charger"
 #define KLOG_LEVEL 6
 
+#include <health2/Health.h>
 #include <healthd/healthd.h>
 
 #include <stdlib.h>
@@ -62,7 +63,9 @@
 };
 #endif
 
-static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+static void healthd_mode_nop_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+    Health::initInstance(config);
 }
 
 static int healthd_mode_nop_preparetowait(void) {
@@ -76,7 +79,7 @@
     struct android::BatteryProperties* /*props*/) {
 }
 
-int main(int argc, char **argv) {
+int healthd_charger_main(int argc, char** argv) {
     int ch;
 
     healthd_mode_ops = &charger_ops;
@@ -100,3 +103,9 @@
 
     return healthd_main();
 }
+
+#ifndef CHARGER_TEST
+int main(int argc, char** argv) {
+    return healthd_charger_main(argc, argv);
+}
+#endif
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
new file mode 100644
index 0000000..a7e2161
--- /dev/null
+++ b/healthd/charger_test.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 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 "charger_test"
+#include <android/log.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <fstream>
+#include <iostream>
+#include <mutex>
+#include <streambuf>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <health2/Health.h>
+
+#define LOG_THIS(fmt, ...)     \
+    ALOGE(fmt, ##__VA_ARGS__); \
+    printf(fmt "\n", ##__VA_ARGS__);
+
+template <typename T>
+class Atomic {
+  public:
+    Atomic(T&& init) : mValue(std::move(init)) {}
+    void set(T&& newVal) {
+        {
+            std::lock_guard<std::mutex> lock(mMutex);
+            mValue = std::move(newVal);
+        }
+        mChanged.notify_all();
+    }
+    bool waitFor(long ms, const T& expectVal) {
+        std::unique_lock<std::mutex> lock(mMutex);
+        return mChanged.wait_for(lock, std::chrono::milliseconds(ms),
+                                 [this, &expectVal] { return mValue == expectVal; });
+    }
+  private:
+    std::mutex mMutex;
+    std::condition_variable mChanged;
+    T mValue;
+};
+
+Atomic<bool>& getUpdateNotifier() {
+    static Atomic<bool> val(false);
+    return val;
+}
+
+int energyCounter(int64_t* counter) {
+    *counter = 0xEC12345;
+    return 0;
+}
+
+const char* createFile(const char* path, const char* content) {
+    std::ofstream stream(path);
+    if (!stream.is_open()) {
+        LOG_THIS("Cannot create file %s", path);
+        return NULL;
+    }
+    stream << content << std::endl;
+    stream.close();
+    return path;
+}
+
+std::string openToString(const char* path) {
+    std::ifstream stream(path);
+    if (!stream.is_open()) {
+        LOG_THIS("Cannot open file %s", path);
+        return "";
+    }
+    return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
+}
+
+int expectContains(const std::string& content, const std::vector<std::string>& fields) {
+    int status = 0;
+    for (const auto& field : fields) {
+        auto pos = content.find(field);
+        if (pos == std::string::npos) {
+            LOG_THIS("Cannot find substr '%s'", field.c_str());
+            status = 1;
+        }
+    }
+    return status;
+}
+
+::android::hardware::hidl_handle createHidlHandle(const char* filepath) {
+    int fd = creat(filepath, S_IRUSR | S_IWUSR);
+    if (fd < 0) return {};
+    native_handle_t* nativeHandle = native_handle_create(1, 0);
+    nativeHandle->data[0] = fd;
+    ::android::hardware::hidl_handle handle;
+    handle.setTo(nativeHandle, true /* shouldOwn */);
+    return handle;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+    config->periodic_chores_interval_fast = 60;
+    config->periodic_chores_interval_slow = 600;
+
+    config->batteryStatusPath = createFile("/data/local/tmp/batteryStatus", "Not charging");
+    config->batteryHealthPath = createFile("/data/local/tmp/batteryHealth", "Unspecified failure");
+    config->batteryPresentPath = createFile("/data/local/tmp/batteryPresent", "1");
+    config->batteryCapacityPath = createFile("/data/local/tmp/batteryCapacity", "47");
+    config->batteryVoltagePath = createFile("/data/local/tmp/batteryVoltage", "45000");
+    config->batteryTemperaturePath = createFile("/data/local/tmp/batteryTemperature", "987");
+    config->batteryTechnologyPath = createFile("/data/local/tmp/batteryTechnology", "NiCd");
+    config->batteryCurrentNowPath = createFile("/data/local/tmp/batteryCurrentNow", "99000");
+    config->batteryCurrentAvgPath = createFile("/data/local/tmp/batteryCurrentAvg", "98000");
+    config->batteryChargeCounterPath = createFile("/data/local/tmp/batteryChargeCounter", "600");
+    config->batteryFullChargePath = createFile("/data/local/tmp/batteryFullCharge", "3515547");
+    config->batteryCycleCountPath = createFile("/data/local/tmp/batteryCycleCount", "77");
+
+    config->energyCounter = energyCounter;
+    config->boot_min_cap = 50;
+    config->screen_on = NULL;
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+    getUpdateNotifier().set(true /* updated */);
+
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+extern int healthd_charger_main(int argc, char** argv);
+
+int main(int argc, char** argv) {
+    using android::hardware::health::V2_0::implementation::Health;
+
+    const char* dumpFile = "/data/local/tmp/dump.txt";
+
+    std::thread bgThread([=] {
+        healthd_charger_main(argc, argv);
+    });
+
+    // wait for healthd_init to finish
+    if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {
+        LOG_THIS("Time out.");
+        exit(1);
+    }
+
+    Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
+
+    std::string content = openToString(dumpFile);
+    int status = expectContains(content, {
+        "status: 4",
+        "health: 6",
+        "present: 1",
+        "level: 47",
+        "voltage: 45",
+        "temp: 987",
+        "current now: 99000",
+        "current avg: 98000",
+        "charge counter: 600",
+        "current now: 99",
+        "cycle count: 77",
+        "Full charge: 3515547"
+    });
+
+    if (status == 0) {
+        LOG_THIS("Test success.");
+    } else {
+        LOG_THIS("Actual dump:\n%s", content.c_str());
+    }
+
+    exit(status);  // force bgThread to exit
+}
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
deleted file mode 100644
index ed1971a..0000000
--- a/healthd/healthd.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2016 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"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <cutils/klog.h>
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <hal_conversion.h>
-
-using namespace android;
-
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// device specific hal interface;
-static sp<IHealth> gHealth;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Android mode
-extern void healthd_mode_android_init(struct healthd_config *config);
-extern int healthd_mode_android_preparetowait(void);
-extern void healthd_mode_android_heartbeat(void);
-extern void healthd_mode_android_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_android_heartbeat,
-    .battery_update = healthd_mode_android_battery_update,
-};
-
-// default energy counter property redirect to talk to device
-// HAL
-static int healthd_board_get_energy_counter(int64_t *energy) {
-
-    if (gHealth == nullptr) {
-        return NAME_NOT_FOUND;
-    }
-
-    Result result = Result::NOT_SUPPORTED;
-    gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
-                result = ret;
-                *energy = energyOut;
-            });
-
-    return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
-}
-
-void healthd_board_init(struct healthd_config *config) {
-
-    // Initialize the board HAL - Equivalent of healthd_board_init(config)
-    // in charger/recovery mode.
-
-    gHealth = IHealth::getService();
-    if (gHealth == nullptr) {
-        KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
-        return;
-    }
-
-    HealthConfig halConfig;
-    convertToHealthConfig(config, halConfig);
-    gHealth->init(halConfig, [=] (const auto &halConfigOut) {
-            convertFromHealthConfig(halConfigOut, config);
-            // always redirect energy counter queries
-            config->energyCounter = healthd_board_get_energy_counter;
-            });
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties *props) {
-    int logthis = 0;
-
-    if (gHealth == nullptr) {
-        return logthis;
-    }
-
-    HealthInfo info;
-    convertToHealthInfo(props, info);
-    gHealth->update(info,
-            [=, &logthis] (int32_t ret, const auto &infoOut) {
-                logthis = ret;
-                convertFromHealthInfo(infoOut, props);
-            });
-
-    return logthis;
-}
-
-int main(int /*argc*/, char ** /*argv*/) {
-
-    healthd_mode_ops = &android_ops;
-
-    return healthd_main();
-}
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
deleted file mode 100644
index 6599919..0000000
--- a/healthd/healthd_common.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * 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-common"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-#include <healthd/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 <cutils/klog.h>
-#include <cutils/uevent.h>
-#include <sys/epoll.h>
-#include <sys/timerfd.h>
-#include <utils/Errors.h>
-
-using namespace android;
-
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-#endif
-
-#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
-  // Periodic chores fast interval in seconds
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
-#else
-  #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-#endif
-
-static struct healthd_config healthd_config = {
-    .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
-    .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-    .batteryStatusPath = String8(String8::kEmptyString),
-    .batteryHealthPath = String8(String8::kEmptyString),
-    .batteryPresentPath = String8(String8::kEmptyString),
-    .batteryCapacityPath = String8(String8::kEmptyString),
-    .batteryVoltagePath = String8(String8::kEmptyString),
-    .batteryTemperaturePath = String8(String8::kEmptyString),
-    .batteryTechnologyPath = String8(String8::kEmptyString),
-    .batteryCurrentNowPath = String8(String8::kEmptyString),
-    .batteryCurrentAvgPath = String8(String8::kEmptyString),
-    .batteryChargeCounterPath = String8(String8::kEmptyString),
-    .batteryFullChargePath = String8(String8::kEmptyString),
-    .batteryCycleCountPath = String8(String8::kEmptyString),
-    .energyCounter = NULL,
-    .boot_min_cap = 0,
-    .screen_on = NULL,
-};
-
-static int eventct;
-static int epollfd;
-
-#define POWER_SUPPLY_SUBSYSTEM "power_supply"
-
-// epoll_create() parameter is actually unused
-#define MAX_EPOLL_EVENTS 40
-static int uevent_fd;
-static int wakealarm_fd;
-
-// -1 for no epoll timeout
-static int awake_poll_interval = -1;
-
-static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-
-static BatteryMonitor* gBatteryMonitor;
-
-struct healthd_mode_ops *healthd_mode_ops;
-
-int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
-    struct epoll_event ev;
-
-    ev.events = EPOLLIN;
-
-    if (wakeup == EVENT_WAKEUP_FD)
-        ev.events |= 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;
-
-    if (wakealarm_fd == -1)
-            return;
-
-    wakealarm_wake_interval = interval;
-
-    if (interval == -1)
-        interval = 0;
-
-    itval.it_interval.tv_sec = interval;
-    itval.it_interval.tv_nsec = 0;
-    itval.it_value.tv_sec = interval;
-    itval.it_value.tv_nsec = 0;
-
-    if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
-        KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
-}
-
-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).
-
-   int new_wake_interval = gBatteryMonitor->update() ?
-       healthd_config.periodic_chores_interval_fast :
-           healthd_config.periodic_chores_interval_slow;
-
-    if (new_wake_interval != wakealarm_wake_interval)
-            wakealarm_set_interval(new_wake_interval);
-
-    // During awake periods poll at fast rate.  If wake alarm is set at fast
-    // rate then just use the alarm; if wake alarm is set at slow rate then
-    // poll at fast rate while awake and let alarm wake up at slow rate when
-    // asleep.
-
-    if (healthd_config.periodic_chores_interval_fast == -1)
-        awake_poll_interval = -1;
-    else
-        awake_poll_interval =
-            new_wake_interval == healthd_config.periodic_chores_interval_fast ?
-                -1 : healthd_config.periodic_chores_interval_fast * 1000;
-}
-
-void healthd_dump_battery_state(int fd) {
-    gBatteryMonitor->dumpState(fd);
-    fsync(fd);
-}
-
-static void periodic_chores() {
-    healthd_battery_update();
-}
-
-#define UEVENT_MSG_LEN 2048
-static void uevent_event(uint32_t /*epevents*/) {
-    char msg[UEVENT_MSG_LEN+2];
-    char *cp;
-    int n;
-
-    n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
-    if (n <= 0)
-        return;
-    if (n >= UEVENT_MSG_LEN)   /* overflow -- discard */
-        return;
-
-    msg[n] = '\0';
-    msg[n+1] = '\0';
-    cp = msg;
-
-    while (*cp) {
-        if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
-            healthd_battery_update();
-            break;
-        }
-
-        /* advance to after the next \0 */
-        while (*cp++)
-            ;
-    }
-}
-
-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, EVENT_WAKEUP_FD))
-        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) {
-        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
-        return;
-    }
-
-    if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
-        KLOG_ERROR(LOG_TAG,
-                   "Registration of wakealarm event failed\n");
-
-    wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
-}
-
-static void healthd_mainloop(void) {
-    int nevents = 0;
-    while (1) {
-        struct epoll_event events[eventct];
-        int timeout = awake_poll_interval;
-        int mode_timeout;
-
-        /* Don't wait for first timer timeout to run periodic chores */
-        if (!nevents)
-            periodic_chores();
-
-        healthd_mode_ops->heartbeat();
-
-        mode_timeout = healthd_mode_ops->preparetowait();
-        if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
-            timeout = mode_timeout;
-        nevents = epoll_wait(epollfd, events, eventct, timeout);
-        if (nevents == -1) {
-            if (errno == EINTR)
-                continue;
-            KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
-            break;
-        }
-
-        for (int n = 0; n < nevents; ++n) {
-            if (events[n].data.ptr)
-                (*(void (*)(int))events[n].data.ptr)(events[n].events);
-        }
-    }
-
-    return;
-}
-
-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_board_init(&healthd_config);
-    healthd_mode_ops->init(&healthd_config);
-    wakealarm_init();
-    uevent_init();
-    gBatteryMonitor = new BatteryMonitor();
-    gBatteryMonitor->init(&healthd_config);
-    return 0;
-}
-
-int healthd_main() {
-    int ret;
-
-    klog_set_level(KLOG_LEVEL);
-
-    if (!healthd_mode_ops) {
-        KLOG_ERROR("healthd ops not set, exiting\n");
-        exit(1);
-    }
-
-    ret = healthd_init();
-    if (ret) {
-        KLOG_ERROR("Initialization failed, exiting\n");
-        exit(2);
-    }
-
-    healthd_mainloop();
-    KLOG_ERROR("Main loop terminated, exiting\n");
-    return 3;
-}
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
deleted file mode 100644
index c612313..0000000
--- a/healthd/healthd_mode_android.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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/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;
-}
-
-void healthd_mode_android_heartbeat(void) {
-}
-
-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(gBatteryPropertiesRegistrar);
-}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 4f77e7a..61e7465 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -49,6 +49,7 @@
 #include "AnimationParser.h"
 #include "healthd_draw.h"
 
+#include <health2/Health.h>
 #include <healthd/healthd.h>
 
 using namespace android;
@@ -612,6 +613,8 @@
 }
 
 void healthd_mode_charger_init(struct healthd_config* config) {
+    using android::hardware::health::V2_0::implementation::Health;
+
     int ret;
     charger* charger = &charger_state;
     int i;
@@ -666,6 +669,10 @@
     charger->next_screen_transition = -1;
     charger->next_key_check = -1;
     charger->next_pwr_check = -1;
+
+    // Initialize Health implementation (which initializes the internal BatteryMonitor).
+    Health::initInstance(config);
+
     healthd_config = config;
     charger->boot_min_cap = config->boot_min_cap;
 }
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 194e667..4d1d53f 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
 #define HEALTHD_BATTERYMONITOR_H
 
 #include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -43,6 +42,7 @@
     int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
+    friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
 
   private:
     struct healthd_config *mHealthdConfig;
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 17efbd6..c01e8d7 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -81,10 +81,6 @@
 // Global helper functions
 
 int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
-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);
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5ea981f..10050eb 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -186,11 +186,16 @@
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
                                               "system/bin/surfaceflinger" },
 
-    // Support hostapd administering a network interface.
+    // Support hostapd administering a network interface (Old Path: <= O-MR1 release).
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
                                            CAP_MASK_LONG(CAP_NET_RAW),
                                               "vendor/bin/hostapd" },
 
+    // Support hostapd administering a network interface (New Path: >= P release).
+    { 00750, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
+                                           CAP_MASK_LONG(CAP_NET_RAW),
+                                              "vendor/bin/hw/hostapd" },
+
     // Support Bluetooth legacy hal accessing /sys/class/rfkill
     // Support RT scheduling in Bluetooth
     { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
@@ -199,8 +204,9 @@
 
     // Support wifi_hal_legacy administering a network interface.
     { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                           CAP_MASK_LONG(CAP_NET_RAW),
-                                              "vendor/bin/hw/android.hardware.wifi@1.0-service" },
+                                           CAP_MASK_LONG(CAP_NET_RAW) |
+                                           CAP_MASK_LONG(CAP_SYS_MODULE),
+                                           "vendor/bin/hw/android.hardware.wifi@1.0-service" },
 
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
diff --git a/libsystem/include/system/graphics-base-v1.0.h b/libsystem/include/system/graphics-base-v1.0.h
new file mode 100644
index 0000000..987a39d
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.0.h
@@ -0,0 +1,140 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
+// Root: android.hardware:hardware/interfaces
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_PIXEL_FORMAT_RGBA_8888 = 1,
+    HAL_PIXEL_FORMAT_RGBX_8888 = 2,
+    HAL_PIXEL_FORMAT_RGB_888 = 3,
+    HAL_PIXEL_FORMAT_RGB_565 = 4,
+    HAL_PIXEL_FORMAT_BGRA_8888 = 5,
+    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16,
+    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17,
+    HAL_PIXEL_FORMAT_YCBCR_422_I = 20,
+    HAL_PIXEL_FORMAT_RGBA_FP16 = 22,
+    HAL_PIXEL_FORMAT_RAW16 = 32,
+    HAL_PIXEL_FORMAT_BLOB = 33,
+    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34,
+    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35,
+    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36,
+    HAL_PIXEL_FORMAT_RAW10 = 37,
+    HAL_PIXEL_FORMAT_RAW12 = 38,
+    HAL_PIXEL_FORMAT_RGBA_1010102 = 43,
+    HAL_PIXEL_FORMAT_Y8 = 538982489,
+    HAL_PIXEL_FORMAT_Y16 = 540422489,
+    HAL_PIXEL_FORMAT_YV12 = 842094169,
+} android_pixel_format_t;
+
+typedef enum {
+    HAL_TRANSFORM_FLIP_H = 1,   // (1 << 0)
+    HAL_TRANSFORM_FLIP_V = 2,   // (1 << 1)
+    HAL_TRANSFORM_ROT_90 = 4,   // (1 << 2)
+    HAL_TRANSFORM_ROT_180 = 3,  // (FLIP_H | FLIP_V)
+    HAL_TRANSFORM_ROT_270 = 7,  // ((FLIP_H | FLIP_V) | ROT_90)
+} android_transform_t;
+
+typedef enum {
+    HAL_DATASPACE_UNKNOWN = 0,
+    HAL_DATASPACE_ARBITRARY = 1,
+    HAL_DATASPACE_STANDARD_SHIFT = 16,
+    HAL_DATASPACE_STANDARD_MASK = 4128768,                      // (63 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0,                     // (0 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT709 = 65536,                       // (1 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625 = 131072,                  // (2 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608,       // (3 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525 = 262144,                  // (4 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680,       // (5 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020 = 393216,                     // (6 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752,  // (7 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_BT470M = 524288,                     // (8 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_FILM = 589824,                       // (9 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_DCI_P3 = 655360,                     // (10 << STANDARD_SHIFT)
+    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896,                  // (11 << STANDARD_SHIFT)
+    HAL_DATASPACE_TRANSFER_SHIFT = 22,
+    HAL_DATASPACE_TRANSFER_MASK = 130023424,       // (31 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0,        // (0 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_LINEAR = 4194304,       // (1 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SRGB = 8388608,         // (2 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912,  // (3 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216,    // (4 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520,    // (5 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824,    // (6 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_ST2084 = 29360128,      // (7 << TRANSFER_SHIFT)
+    HAL_DATASPACE_TRANSFER_HLG = 33554432,         // (8 << TRANSFER_SHIFT)
+    HAL_DATASPACE_RANGE_SHIFT = 27,
+    HAL_DATASPACE_RANGE_MASK = 939524096,      // (7 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_UNSPECIFIED = 0,       // (0 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_FULL = 134217728,      // (1 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_LIMITED = 268435456,   // (2 << RANGE_SHIFT)
+    HAL_DATASPACE_RANGE_EXTENDED = 402653184,  // (3 << RANGE_SHIFT)
+    HAL_DATASPACE_SRGB_LINEAR = 512,
+    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB_LINEAR =
+        406913024,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
+    HAL_DATASPACE_SRGB = 513,
+    HAL_DATASPACE_V0_SRGB = 142671872,   // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_V0_SCRGB = 411107328,  // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
+    HAL_DATASPACE_JFIF = 257,
+    HAL_DATASPACE_V0_JFIF = 146931712,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT601_625 = 258,
+    HAL_DATASPACE_V0_BT601_625 =
+        281149440,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT601_525 = 259,
+    HAL_DATASPACE_V0_BT601_525 =
+        281280512,  // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT709 = 260,
+    HAL_DATASPACE_V0_BT709 = 281083904,  // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_DCI_P3_LINEAR = 139067392,  // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DCI_P3 = 155844608,  // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3_LINEAR =
+        139067392,                         // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_DISPLAY_P3 = 143261696,  // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
+    HAL_DATASPACE_ADOBE_RGB = 151715840,  // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_LINEAR = 138805248,  // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
+    HAL_DATASPACE_BT2020 = 147193856,     // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
+    HAL_DATASPACE_BT2020_PQ = 163971072,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
+    HAL_DATASPACE_DEPTH = 4096,
+    HAL_DATASPACE_SENSOR = 4097,
+} android_dataspace_t;
+
+typedef enum {
+    HAL_COLOR_MODE_NATIVE = 0,
+    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
+    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
+    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
+    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
+    HAL_COLOR_MODE_STANDARD_BT709 = 5,
+    HAL_COLOR_MODE_DCI_P3 = 6,
+    HAL_COLOR_MODE_SRGB = 7,
+    HAL_COLOR_MODE_ADOBE_RGB = 8,
+    HAL_COLOR_MODE_DISPLAY_P3 = 9,
+} android_color_mode_t;
+
+typedef enum {
+    HAL_COLOR_TRANSFORM_IDENTITY = 0,
+    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
+    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
+    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
+    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
+    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
+    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
+} android_color_transform_t;
+
+typedef enum {
+    HAL_HDR_DOLBY_VISION = 1,
+    HAL_HDR_HDR10 = 2,
+    HAL_HDR_HLG = 3,
+} android_hdr_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base-v1.1.h b/libsystem/include/system/graphics-base-v1.1.h
new file mode 100644
index 0000000..12d01c1
--- /dev/null
+++ b/libsystem/include/system/graphics-base-v1.1.h
@@ -0,0 +1,33 @@
+// This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.1
+// Root: android.hardware:hardware/interfaces
+
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    HAL_PIXEL_FORMAT_DEPTH_16 = 48,
+    HAL_PIXEL_FORMAT_DEPTH_24 = 49,
+    HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = 50,
+    HAL_PIXEL_FORMAT_DEPTH_32F = 51,
+    HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 = 52,
+    HAL_PIXEL_FORMAT_STENCIL_8 = 53,
+    HAL_PIXEL_FORMAT_YCBCR_P010 = 54,
+} android_pixel_format_v1_1_t;
+
+typedef enum {
+    HAL_DATASPACE_BT2020_ITU =
+        281411584,  // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
+    HAL_DATASPACE_BT2020_ITU_PQ =
+        298188800,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_LIMITED)
+} android_dataspace_v1_1_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
diff --git a/libsystem/include/system/graphics-base.h b/libsystem/include/system/graphics-base.h
index 2a44faf..663b8b0 100644
--- a/libsystem/include/system/graphics-base.h
+++ b/libsystem/include/system/graphics-base.h
@@ -1,141 +1,15 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-// Source: android.hardware.graphics.common@1.0
-// Root: android.hardware:hardware/interfaces
+#ifndef SYSTEM_CORE_GRAPHICS_BASE_H_
+#define SYSTEM_CORE_GRAPHICS_BASE_H_
 
-#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#include "graphics-base-v1.0.h"
+#include "graphics-base-v1.1.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+/* Legacy formats not in the HAL definitions. */
 typedef enum {
-    HAL_PIXEL_FORMAT_RGBA_8888 = 1,
-    HAL_PIXEL_FORMAT_RGBX_8888 = 2,
-    HAL_PIXEL_FORMAT_RGB_888 = 3,
-    HAL_PIXEL_FORMAT_RGB_565 = 4,
-    HAL_PIXEL_FORMAT_BGRA_8888 = 5,
-    HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
-    HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
-    HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
-    HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
-    HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
-    HAL_PIXEL_FORMAT_RAW16 = 32, // 0x20
-    HAL_PIXEL_FORMAT_RAW10 = 37, // 0x25
-    HAL_PIXEL_FORMAT_RAW12 = 38, // 0x26
-    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36, // 0x24
-    HAL_PIXEL_FORMAT_BLOB = 33, // 0x21
-    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34, // 0x22
-    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35, // 0x23
-    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
-    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
-    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
-    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
-    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
-    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
-    HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
-    HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
-} android_pixel_format_t;
+    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39,   // 0x27
+    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40,   // 0x28
+    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41,    // 0x29
+    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42,  // 0x2A
+} android_pixel_format_legacy_t;
 
-typedef enum {
-    HAL_TRANSFORM_FLIP_H = 1, // 0x01
-    HAL_TRANSFORM_FLIP_V = 2, // 0x02
-    HAL_TRANSFORM_ROT_90 = 4, // 0x04
-    HAL_TRANSFORM_ROT_180 = 3, // 0x03
-    HAL_TRANSFORM_ROT_270 = 7, // 0x07
-} android_transform_t;
-
-typedef enum {
-    HAL_DATASPACE_UNKNOWN = 0, // 0x0
-    HAL_DATASPACE_ARBITRARY = 1, // 0x1
-    HAL_DATASPACE_STANDARD_SHIFT = 16,
-    HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
-    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
-    HAL_DATASPACE_TRANSFER_SHIFT = 22,
-    HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
-    HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
-    HAL_DATASPACE_RANGE_SHIFT = 27,
-    HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
-    HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
-    HAL_DATASPACE_SRGB_LINEAR = 512, // 0x200
-    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_V0_SCRGB_LINEAR = 406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
-    HAL_DATASPACE_SRGB = 513, // 0x201
-    HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
-    HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
-    HAL_DATASPACE_JFIF = 257, // 0x101
-    HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
-    HAL_DATASPACE_BT601_625 = 258, // 0x102
-    HAL_DATASPACE_V0_BT601_625 = 281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_BT601_525 = 259, // 0x103
-    HAL_DATASPACE_V0_BT601_525 = 281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_BT709 = 260, // 0x104
-    HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
-    HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
-    HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
-    HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
-    HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
-    HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
-    HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
-    HAL_DATASPACE_DEPTH = 4096, // 0x1000
-    HAL_DATASPACE_SENSOR = 4097, // 0x1001
-} android_dataspace_t;
-
-typedef enum {
-    HAL_COLOR_MODE_NATIVE = 0,
-    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
-    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
-    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
-    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
-    HAL_COLOR_MODE_STANDARD_BT709 = 5,
-    HAL_COLOR_MODE_DCI_P3 = 6,
-    HAL_COLOR_MODE_SRGB = 7,
-    HAL_COLOR_MODE_ADOBE_RGB = 8,
-    HAL_COLOR_MODE_DISPLAY_P3 = 9,
-} android_color_mode_t;
-
-typedef enum {
-    HAL_COLOR_TRANSFORM_IDENTITY = 0,
-    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
-    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
-    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
-    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
-    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
-    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
-} android_color_transform_t;
-
-typedef enum {
-    HAL_HDR_DOLBY_VISION = 1,
-    HAL_HDR_HDR10 = 2,
-    HAL_HDR_HLG = 3,
-} android_hdr_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // SYSTEM_CORE_GRAPHICS_BASE_H_
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index 1a99187..657370b 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -257,6 +257,11 @@
     float minLuminance;
 };
 
+struct android_cta861_3_metadata {
+    float maxContentLightLevel;
+    float maxFrameAverageLightLevel;
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 9758b18..7e62542 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -137,6 +137,7 @@
 /* Returns the USB product ID from the device descriptor for the USB device */
 uint16_t usb_device_get_product_id(struct usb_device *device);
 
+/* Returns a pointer to device descriptor */
 const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
 
 /* Returns a USB descriptor string for the given string ID.
@@ -156,6 +157,12 @@
 int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
                                size_t* response_size);
 
+/* Returns the length in bytes read into the raw descriptors array */
+size_t usb_device_get_descriptors_length(const struct usb_device* device);
+
+/* Returns a pointer to the raw descriptors array */
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device);
+
 /* Returns a USB descriptor string for the given string ID.
  * Used to implement usb_device_get_manufacturer_name,
  * usb_device_get_product_name and usb_device_get_serial.
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index cb8d430..415488f 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -76,9 +76,11 @@
     int                         wddbus;
 };
 
+#define MAX_DESCRIPTORS_LENGTH 4096
+
 struct usb_device {
     char dev_name[64];
-    unsigned char desc[4096];
+    unsigned char desc[MAX_DESCRIPTORS_LENGTH];
     int desc_length;
     int fd;
     int writeable;
@@ -387,6 +389,8 @@
     return device;
 
 failed:
+    // TODO It would be more appropriate to have callers do this
+    // since this function doesn't "own" this file descriptor.
     close(fd);
     free(device);
     return NULL;
@@ -455,11 +459,18 @@
     return __le16_to_cpu(desc->idProduct);
 }
 
-const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
-{
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device* device) {
     return (struct usb_device_descriptor*)device->desc;
 }
 
+size_t usb_device_get_descriptors_length(const struct usb_device* device) {
+    return device->desc_length;
+}
+
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device) {
+    return device->desc;
+}
+
 /* Returns a USB descriptor string for the given string ID.
  * Return value: < 0 on error.  0 on success.
  * The string is returned in ucs2_out in USB-native UCS-2 encoding.
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index ad335c3..e8f1c51 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -79,6 +79,23 @@
     return getEmptyString();
 }
 
+static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
+    if (u16len >= SIZE_MAX / sizeof(char16_t)) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
+    SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
+    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
+    if (buf) {
+        char16_t* str = (char16_t*)buf->data();
+        memcpy(str, u16str, u16len * sizeof(char16_t));
+        str[u16len] = 0;
+        return str;
+    }
+    return getEmptyString();
+}
+
 // ---------------------------------------------------------------------------
 
 String16::String16()
@@ -111,35 +128,9 @@
     setTo(o, len, begin);
 }
 
-String16::String16(const char16_t* o)
-{
-    size_t len = strlen16(o);
-    SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
-    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
-    if (buf) {
-        char16_t* str = (char16_t*)buf->data();
-        strcpy16(str, o);
-        mString = str;
-        return;
-    }
+String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {}
 
-    mString = getEmptyString();
-}
-
-String16::String16(const char16_t* o, size_t len)
-{
-    SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
-    ALOG_ASSERT(buf, "Unable to allocate shared buffer");
-    if (buf) {
-        char16_t* str = (char16_t*)buf->data();
-        memcpy(str, o, len*sizeof(char16_t));
-        str[len] = 0;
-        mString = str;
-        return;
-    }
-
-    mString = getEmptyString();
-}
+String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
 
 String16::String16(const String8& o)
     : mString(allocFromUTF8(o.string(), o.size()))
@@ -201,6 +192,11 @@
 
 status_t String16::setTo(const char16_t* other, size_t len)
 {
+    if (len >= SIZE_MAX / sizeof(char16_t)) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((len+1)*sizeof(char16_t));
     if (buf) {
@@ -224,6 +220,11 @@
         return NO_ERROR;
     }
 
+    if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
@@ -245,6 +246,11 @@
         return NO_ERROR;
     }
 
+    if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
+        android_errorWriteLog(0x534e4554, "73826242");
+        abort();
+    }
+
     SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
         ->editResize((myLen+otherLen+1)*sizeof(char16_t));
     if (buf) {
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..8e65162 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -4,10 +4,38 @@
     srcs: ["lmkd.c"],
     shared_libs: [
         "liblog",
-        "libprocessgroup",
         "libcutils",
     ],
-    cflags: ["-Werror"],
-
+    static_libs: [
+        "libstatslogc",
+    ],
+    local_include_dirs: ["include"],
+    cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
     init_rc: ["lmkd.rc"],
 }
+
+cc_library_static {
+    name: "libstatslogc",
+    srcs: ["statslog.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "liblog",
+    ],
+}
+
+cc_library_static {
+    name: "liblmkd_utils",
+    srcs: ["liblmkd_utils.c"],
+    shared_libs: [
+        "libcutils",
+    ],
+    export_include_dirs: ["include"],
+    cppflags: [
+        "-g",
+        "-Wall",
+        "-Werror",
+    ]
+}
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 0000000..72e3f4a
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _LIBLMKD_UTILS_H_
+#define _LIBLMKD_UTILS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <lmkd.h>
+
+__BEGIN_DECLS
+
+/*
+ * Connects to lmkd process and returns socket handle.
+ * On success returns socket handle.
+ * On error, -1 is returned, and errno is set appropriately.
+ */
+int lmkd_connect();
+
+/*
+ * Registers a process with lmkd and sets its oomadj score.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_register_proc(int sock, struct lmk_procprio *params);
+
+/*
+ * Creates memcg directory for given process.
+ * On success returns 0.
+ * -1 is returned if path creation failed.
+ * -2 is returned if tasks file open operation failed.
+ * -3 is returned if tasks file write operation failed.
+ * In the case of error errno is set appropriately.
+ */
+int create_memcg(uid_t uid, pid_t pid);
+
+__END_DECLS
+
+#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 0000000..fe6364d
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _LMKD_H_
+#define _LMKD_H_
+
+#include <arpa/inet.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Supported LMKD commands
+ */
+enum lmk_cmd {
+    LMK_TARGET = 0,  /* Associate minfree with oom_adj_score */
+    LMK_PROCPRIO,    /* Register a process and set its oom_adj_score */
+    LMK_PROCREMOVE,  /* Unregister a process */
+};
+
+/*
+ * Max number of targets in LMK_TARGET command.
+ */
+#define MAX_TARGETS 6
+
+/*
+ * Max packet length in bytes.
+ * Longest packet is LMK_TARGET followed by MAX_TARGETS
+ * of minfree and oom_adj_score values
+ */
+#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* LMKD packet - first int is lmk_cmd followed by payload */
+typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
+
+/* Get LMKD packet command */
+inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+    return (enum lmk_cmd)ntohl(pack[0]);
+}
+
+/* LMK_TARGET packet payload */
+struct lmk_target {
+    int minfree;
+    int oom_adj_score;
+};
+
+/*
+ * For LMK_TARGET packet get target_idx-th payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
+                                 int target_idx, struct lmk_target *target) {
+    target->minfree = ntohl(packet[target_idx * 2 + 1]);
+    target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
+}
+
+/*
+ * Prepare LMK_TARGET packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
+                                   struct lmk_target *targets,
+                                   size_t target_cnt) {
+    int idx = 0;
+    packet[idx++] = htonl(LMK_TARGET);
+    while (target_cnt) {
+        packet[idx++] = htonl(targets->minfree);
+        packet[idx++] = htonl(targets->oom_adj_score);
+        targets++;
+        target_cnt--;
+    }
+    return idx * sizeof(int);
+}
+
+/* LMK_PROCPRIO packet payload */
+struct lmk_procprio {
+    pid_t pid;
+    uid_t uid;
+    int oomadj;
+};
+
+/*
+ * For LMK_PROCPRIO packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+    params->uid = (uid_t)ntohl(packet[2]);
+    params->oomadj = ntohl(packet[3]);
+}
+
+/*
+ * Prepare LMK_PROCPRIO packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCPRIO);
+    packet[1] = htonl(params->pid);
+    packet[2] = htonl(params->uid);
+    packet[3] = htonl(params->oomadj);
+    return 4 * sizeof(int);
+}
+
+/* LMK_PROCREMOVE packet payload */
+struct lmk_procremove {
+    pid_t pid;
+};
+
+/*
+ * For LMK_PROCREMOVE packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procremove *params) {
+    params->pid = (pid_t)ntohl(packet[1]);
+}
+
+/*
+ * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+                                   struct lmk_procprio *params) {
+    packet[0] = htonl(LMK_PROCREMOVE);
+    packet[1] = htonl(params->pid);
+    return 2 * sizeof(int);
+}
+
+__END_DECLS
+
+#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 0000000..fa3b7a9
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 <errno.h>
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <liblmkd_utils.h>
+#include <cutils/sockets.h>
+
+int lmkd_connect() {
+    return socket_local_client("lmkd",
+                               ANDROID_SOCKET_NAMESPACE_RESERVED,
+                               SOCK_SEQPACKET);
+}
+
+int lmkd_register_proc(int sock, struct lmk_procprio *params) {
+    LMKD_CTRL_PACKET packet;
+    size_t size;
+    int ret;
+
+    size = lmkd_pack_set_procprio(packet, params);
+    ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
+
+    return (ret < 0) ? -1 : 0;
+}
+
+int create_memcg(uid_t uid, pid_t pid) {
+    char buf[256];
+    int tasks_file;
+    int written;
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+    if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+        errno != EEXIST) {
+        return -1;
+    }
+
+    snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+    tasks_file = open(buf, O_WRONLY);
+    if (tasks_file < 0) {
+        return -2;
+    }
+    written = snprintf(buf, sizeof(buf), "%u", pid);
+    if (__predict_false(written >= (int)sizeof(buf))) {
+        written = sizeof(buf) - 1;
+    }
+    written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+    close(tasks_file);
+
+    return (written < 0) ? -3 : 0;
+}
+
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 15471e0..1d8afb4 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "lowmemorykiller"
 
-#include <arpa/inet.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <sched.h>
@@ -29,13 +28,36 @@
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <time.h>
+#include <sys/sysinfo.h>
 #include <unistd.h>
 
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
+#include <lmkd.h>
 #include <log/log.h>
-#include <processgroup/processgroup.h>
+
+#ifdef LMKD_LOG_STATS
+#include <statslog.h>
+#endif
+
+/*
+ * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
+ * to profile and correlate with OOM kills
+ */
+#ifdef LMKD_TRACE_KILLS
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <cutils/trace.h>
+
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END()      ATRACE_INT(__FUNCTION__, 0);
+
+#else /* LMKD_TRACE_KILLS */
+
+#define TRACE_KILL_START(pid) ((void)(pid))
+#define TRACE_KILL_END() ((void)0)
+
+#endif /* LMKD_TRACE_KILLS */
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -44,9 +66,7 @@
 #define MEMCG_SYSFS_PATH "/dev/memcg/"
 #define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
 #define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
-#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
-#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
-#define ZONEINFO_PATH "/proc/zoneinfo"
+
 #define LINE_MAX 128
 
 #define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
@@ -55,43 +75,68 @@
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
 #define EIGHT_MEGA (1 << 23)
 
-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;
 static bool has_inkernel_module;
 
-/* memory pressure level medium event */
-static int mpevfd[2];
-#define CRITICAL_INDEX 1
-#define MEDIUM_INDEX 0
+/* memory pressure levels */
+enum vmpressure_level {
+    VMPRESS_LEVEL_LOW = 0,
+    VMPRESS_LEVEL_MEDIUM,
+    VMPRESS_LEVEL_CRITICAL,
+    VMPRESS_LEVEL_COUNT
+};
 
-static int medium_oomadj;
-static int critical_oomadj;
+static const char *level_name[] = {
+    "low",
+    "medium",
+    "critical"
+};
+
+struct mem_size {
+    int free_mem;
+    int free_swap;
+};
+
+struct {
+    int min_free; /* recorded but not used yet */
+    int max_free;
+} low_pressure_mem = { -1, -1 };
+
+static int level_oomadj[VMPRESS_LEVEL_COUNT];
+static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
 static bool debug_process_killing;
 static bool enable_pressure_upgrade;
 static int64_t upgrade_pressure;
 static int64_t downgrade_pressure;
 static bool is_go_device;
+static bool kill_heaviest_task;
+static unsigned long kill_timeout_ms;
 
-/* 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? */
+/* data required to handle events */
+struct event_handler_info {
+    int data;
+    void (*handler)(int data, uint32_t events);
+};
 
-/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 4
+/* data required to handle socket events */
+struct sock_event_handler_info {
+    int sock;
+    struct event_handler_info handler_info;
+};
+
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
+
+/* socket event handler data */
+static struct sock_event_handler_info ctrl_sock;
+static struct sock_event_handler_info data_sock[MAX_DATA_CONN];
+
+/* vmpressure event handler data */
+static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
+
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
 static int epollfd;
 static int maxevents;
 
@@ -123,6 +168,11 @@
     struct proc *pidhash_next;
 };
 
+#ifdef LMKD_LOG_STATS
+static bool enable_stats_log;
+static android_log_context log_ctx;
+#endif
+
 #define PIDHASH_SZ 1024
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -226,7 +276,7 @@
     return 0;
 }
 
-static void writefilestring(char *path, char *s) {
+static void writefilestring(const char *path, char *s) {
     int fd = open(path, O_WRONLY | O_CLOEXEC);
     int len = strlen(s);
     int ret;
@@ -246,45 +296,49 @@
     close(fd);
 }
 
-static void cmd_procprio(int pid, int uid, int oomadj) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
     struct proc *procp;
     char path[80];
     char val[20];
     int soft_limit_mult;
+    struct lmk_procprio params;
 
-    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
-        ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+    lmkd_pack_get_procprio(packet, &params);
+
+    if (params.oomadj < OOM_SCORE_ADJ_MIN ||
+        params.oomadj > OOM_SCORE_ADJ_MAX) {
+        ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
         return;
     }
 
-    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
-    snprintf(val, sizeof(val), "%d", oomadj);
+    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
+    snprintf(val, sizeof(val), "%d", params.oomadj);
     writefilestring(path, val);
 
     if (use_inkernel_interface)
         return;
 
-    if (oomadj >= 900) {
+    if (params.oomadj >= 900) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 800) {
+    } else if (params.oomadj >= 800) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 700) {
+    } else if (params.oomadj >= 700) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 600) {
+    } else if (params.oomadj >= 600) {
         // Launcher should be perceptible, don't kill it.
-        oomadj = 200;
+        params.oomadj = 200;
         soft_limit_mult = 1;
-    } else if (oomadj >= 500) {
+    } else if (params.oomadj >= 500) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 400) {
+    } else if (params.oomadj >= 400) {
         soft_limit_mult = 0;
-    } else if (oomadj >= 300) {
+    } else if (params.oomadj >= 300) {
         soft_limit_mult = 1;
-    } else if (oomadj >= 200) {
+    } else if (params.oomadj >= 200) {
         soft_limit_mult = 2;
-    } else if (oomadj >= 100) {
+    } else if (params.oomadj >= 100) {
         soft_limit_mult = 10;
-    } else if (oomadj >=   0) {
+    } else if (params.oomadj >=   0) {
         soft_limit_mult = 20;
     } else {
         // Persistent processes will have a large
@@ -292,11 +346,13 @@
         soft_limit_mult = 64;
     }
 
-    snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+    snprintf(path, sizeof(path),
+             "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+             params.uid, params.pid);
     snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
     writefilestring(path, val);
 
-    procp = pid_lookup(pid);
+    procp = pid_lookup(params.pid);
     if (!procp) {
             procp = malloc(sizeof(struct proc));
             if (!procp) {
@@ -304,33 +360,38 @@
                 return;
             }
 
-            procp->pid = pid;
-            procp->uid = uid;
-            procp->oomadj = oomadj;
+            procp->pid = params.pid;
+            procp->uid = params.uid;
+            procp->oomadj = params.oomadj;
             proc_insert(procp);
     } else {
         proc_unslot(procp);
-        procp->oomadj = oomadj;
+        procp->oomadj = params.oomadj;
         proc_slot(procp);
     }
 }
 
-static void cmd_procremove(int pid) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+    struct lmk_procremove params;
+
     if (use_inkernel_interface)
         return;
 
-    pid_remove(pid);
+    lmkd_pack_get_procremove(packet, &params);
+    pid_remove(params.pid);
 }
 
-static void cmd_target(int ntargets, int *params) {
+static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
     int i;
+    struct lmk_target target;
 
     if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
         return;
 
     for (i = 0; i < ntargets; i++) {
-        lowmem_minfree[i] = ntohl(*params++);
-        lowmem_adj[i] = ntohl(*params++);
+        lmkd_pack_get_target(packet, i, &target);
+        lowmem_minfree[i] = target.minfree;
+        lowmem_adj[i] = target.oom_adj_score;
     }
 
     lowmem_targets_size = ntargets;
@@ -361,17 +422,24 @@
     }
 }
 
-static void ctrl_data_close(void) {
-    ALOGI("Closing Activity Manager data connection");
-    close(ctrl_dfd);
-    ctrl_dfd = -1;
+static void ctrl_data_close(int dsock_idx) {
+    struct epoll_event epev;
+
+    ALOGI("closing lmkd data connection");
+    if (epoll_ctl(epollfd, EPOLL_CTL_DEL, data_sock[dsock_idx].sock, &epev) == -1) {
+        // Log a warning and keep going
+        ALOGW("epoll_ctl for data connection socket failed; errno=%d", errno);
+    }
     maxevents--;
+
+    close(data_sock[dsock_idx].sock);
+    data_sock[dsock_idx].sock = -1;
 }
 
-static int ctrl_data_read(char *buf, size_t bufsz) {
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
     int ret = 0;
 
-    ret = read(ctrl_dfd, buf, bufsz);
+    ret = read(data_sock[dsock_idx].sock, buf, bufsz);
 
     if (ret == -1) {
         ALOGE("control data socket read failed; errno=%d", errno);
@@ -383,39 +451,43 @@
     return ret;
 }
 
-static void ctrl_command_handler(void) {
-    int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+static void ctrl_command_handler(int dsock_idx) {
+    LMKD_CTRL_PACKET packet;
     int len;
-    int cmd = -1;
+    enum lmk_cmd cmd;
     int nargs;
     int targets;
 
-    len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+    len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
     if (len <= 0)
         return;
 
+    if (len < (int)sizeof(int)) {
+        ALOGE("Wrong control socket read length len=%d", len);
+        return;
+    }
+
+    cmd = lmkd_pack_get_cmd(packet);
     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]);
+        cmd_target(targets, packet);
         break;
     case LMK_PROCPRIO:
         if (nargs != 3)
             goto wronglen;
-        cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+        cmd_procprio(packet);
         break;
     case LMK_PROCREMOVE:
         if (nargs != 1)
             goto wronglen;
-        cmd_procremove(ntohl(ibuf[1]));
+        cmd_procremove(packet);
         break;
     default:
         ALOGE("Received unknown command code %d", cmd);
@@ -428,109 +500,113 @@
     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_data_handler(int data, uint32_t events) {
+    if (events & EPOLLIN) {
+        ctrl_command_handler(data);
     }
 }
 
-static void ctrl_connect_handler(uint32_t events __unused) {
-    struct epoll_event epev;
+static int get_free_dsock() {
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock < 0) {
+            return i;
+        }
+    }
+    return -1;
+}
 
-    if (ctrl_dfd >= 0) {
-        ctrl_data_close();
-        ctrl_dfd_reopened = 1;
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+    struct epoll_event epev;
+    int free_dscock_idx = get_free_dsock();
+
+    if (free_dscock_idx < 0) {
+        /*
+         * Number of data connections exceeded max supported. This should not
+         * happen but if it does we drop all existing connections and accept
+         * the new one. This prevents inactive connections from monopolizing
+         * data socket and if we drop ActivityManager connection it will
+         * immediately reconnect.
+         */
+        for (int i = 0; i < MAX_DATA_CONN; i++) {
+            ctrl_data_close(i);
+        }
+        free_dscock_idx = 0;
     }
 
-    ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
-
-    if (ctrl_dfd < 0) {
+    data_sock[free_dscock_idx].sock = accept(ctrl_sock.sock, NULL, NULL);
+    if (data_sock[free_dscock_idx].sock < 0) {
         ALOGE("lmkd control socket accept failed; errno=%d", errno);
         return;
     }
 
-    ALOGI("ActivityManager connected");
-    maxevents++;
+    ALOGI("lmkd data connection established");
+    /* use data to store data connection idx */
+    data_sock[free_dscock_idx].handler_info.data = free_dscock_idx;
+    data_sock[free_dscock_idx].handler_info.handler = ctrl_data_handler;
     epev.events = EPOLLIN;
-    epev.data.ptr = (void *)ctrl_data_handler;
-    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+    epev.data.ptr = (void *)&(data_sock[free_dscock_idx].handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, data_sock[free_dscock_idx].sock, &epev) == -1) {
         ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
-        ctrl_data_close();
+        ctrl_data_close(free_dscock_idx);
         return;
     }
+    maxevents++;
 }
 
-static int zoneinfo_parse_protection(char *cp) {
-    int max = 0;
-    int zoneval;
-    char *save_ptr;
+#ifdef LMKD_LOG_STATS
+static void memory_stat_parse_line(char *line, struct memory_stat *mem_st) {
+    char key[LINE_MAX];
+    int64_t value;
 
-    for (cp = strtok_r(cp, "(), ", &save_ptr); cp; cp = strtok_r(NULL, "), ", &save_ptr)) {
-        zoneval = strtol(cp, &cp, 0);
-        if (zoneval > max)
-            max = zoneval;
+    sscanf(line,"%s  %" SCNd64 "", key, &value);
+
+    if (strcmp(key, "total_") < 0) {
+        return;
     }
 
-    return max;
+    if (!strcmp(key, "total_pgfault"))
+        mem_st->pgfault = value;
+    else if (!strcmp(key, "total_pgmajfault"))
+        mem_st->pgmajfault = value;
+    else if (!strcmp(key, "total_rss"))
+        mem_st->rss_in_bytes = value;
+    else if (!strcmp(key, "total_cache"))
+        mem_st->cache_in_bytes = value;
+    else if (!strcmp(key, "total_swap"))
+        mem_st->swap_in_bytes = value;
 }
 
-static void zoneinfo_parse_line(char *line, struct sysmeminfo *mip) {
-    char *cp = line;
-    char *ap;
-    char *save_ptr;
+static int memory_stat_parse(struct memory_stat *mem_st,  int pid, uid_t uid) {
+   FILE *fp;
+   char buf[PATH_MAX];
 
-    cp = strtok_r(line, " ", &save_ptr);
-    if (!cp)
-        return;
+   snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
 
-    ap = strtok_r(NULL, " ", &save_ptr);
-    if (!ap)
-        return;
+   fp = fopen(buf, "r");
 
-    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);
+   if (fp == NULL) {
+       ALOGE("%s open failed: %s", buf, strerror(errno));
+       return -1;
+   }
+
+   while (fgets(buf, PAGE_SIZE, fp) != NULL ) {
+       memory_stat_parse_line(buf, mem_st);
+   }
+   fclose(fp);
+
+   return 0;
 }
+#endif
 
-static int zoneinfo_parse(struct sysmeminfo *mip) {
-    int fd;
-    ssize_t size;
-    char buf[PAGE_SIZE];
-    char *save_ptr;
-    char *line;
+static int get_free_memory(struct mem_size *ms) {
+    struct sysinfo si;
 
-    memset(mip, 0, sizeof(struct sysmeminfo));
-
-    fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
-    if (fd == -1) {
-        ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
+    if (sysinfo(&si) < 0)
         return -1;
-    }
 
-    size = read_all(fd, buf, sizeof(buf) - 1);
-    if (size < 0) {
-        ALOGE("%s read: errno=%d", ZONEINFO_PATH, errno);
-        close(fd);
-        return -1;
-    }
-    ALOG_ASSERT((size_t)size < sizeof(buf) - 1, "/proc/zoneinfo too large");
-    buf[size] = 0;
+    ms->free_mem = (int)(si.freeram * si.mem_unit / PAGE_SIZE);
+    ms->free_swap = (int)(si.freeswap * si.mem_unit / PAGE_SIZE);
 
-    for (line = strtok_r(buf, "\n", &save_ptr); line; line = strtok_r(NULL, "\n", &save_ptr))
-            zoneinfo_parse_line(line, mip);
-
-    close(fd);
     return 0;
 }
 
@@ -586,14 +662,43 @@
     return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
 }
 
+static struct proc *proc_get_heaviest(int oomadj) {
+    struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
+    struct adjslot_list *curr = head->next;
+    struct proc *maxprocp = NULL;
+    int maxsize = 0;
+    while (curr != head) {
+        int pid = ((struct proc *)curr)->pid;
+        int tasksize = proc_get_size(pid);
+        if (tasksize <= 0) {
+            struct adjslot_list *next = curr->next;
+            pid_remove(pid);
+            curr = next;
+        } else {
+            if (tasksize > maxsize) {
+                maxsize = tasksize;
+                maxprocp = (struct proc *)curr;
+            }
+            curr = curr->next;
+        }
+    }
+    return maxprocp;
+}
+
 /* Kill one process specified by procp.  Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) {
+static int kill_one_process(struct proc* procp, int min_score_adj,
+                            enum vmpressure_level level) {
     int pid = procp->pid;
     uid_t uid = procp->uid;
     char *taskname;
     int tasksize;
     int r;
 
+#ifdef LMKD_LOG_STATS
+    struct memory_stat mem_st = {};
+    int memory_stat_parse_result = -1;
+#endif
+
     taskname = proc_get_name(pid);
     if (!taskname) {
         pid_remove(pid);
@@ -606,48 +711,95 @@
         return -1;
     }
 
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log) {
+        memory_stat_parse_result = memory_stat_parse(&mem_st, pid, uid);
+    }
+#endif
+
+    TRACE_KILL_START(pid);
+
+    r = kill(pid, SIGKILL);
     ALOGI(
         "Killing '%s' (%d), uid %d, adj %d\n"
         "   to free %ldkB because system is under %s memory pressure oom_adj %d\n",
-        taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium",
-        min_score_adj);
-    r = kill(pid, SIGKILL);
+        taskname, pid, uid, procp->oomadj, tasksize * page_k,
+        level_name[level], min_score_adj);
     pid_remove(pid);
 
+    TRACE_KILL_END();
+
     if (r) {
         ALOGE("kill(%d): errno=%d", pid, errno);
         return -1;
+    } else {
+#ifdef LMKD_LOG_STATS
+        if (memory_stat_parse_result == 0) {
+            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
+                    procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
+                    mem_st.cache_in_bytes, mem_st.swap_in_bytes);
+        }
+#endif
+        return tasksize;
     }
 
     return tasksize;
 }
 
 /*
- * Find a process to kill based on the current (possibly estimated) free memory
- * and cached memory sizes.  Returns the size of the killed processes.
+ * Find processes to kill to free required number of pages.
+ * If pages_to_free is set to 0 only one process will be killed.
+ * Returns the size of the killed processes.
  */
-static int find_and_kill_process(bool is_critical) {
+static int find_and_kill_processes(enum vmpressure_level level,
+                                   int pages_to_free) {
     int i;
-    int killed_size = 0;
-    int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;
+    int killed_size;
+    int pages_freed = 0;
+    int min_score_adj = level_oomadj[level];
+
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log) {
+        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_START);
+    }
+#endif
 
     for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
-retry:
-        procp = proc_adj_lru(i);
+        while (true) {
+            if (is_go_device)
+                procp = proc_adj_lru(i);
+            else
+                procp = proc_get_heaviest(i);
 
-        if (procp) {
-            killed_size = kill_one_process(procp, min_score_adj, is_critical);
-            if (killed_size < 0) {
-                goto retry;
-            } else {
-                return killed_size;
+            if (!procp)
+                break;
+
+            killed_size = kill_one_process(procp, min_score_adj, level);
+            if (killed_size >= 0) {
+                pages_freed += killed_size;
+                if (pages_freed >= pages_to_free) {
+
+#ifdef LMKD_LOG_STATS
+                    if (enable_stats_log) {
+                        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+                                LMK_STATE_CHANGE_STOP);
+                    }
+#endif
+                    return pages_freed;
+                }
             }
         }
     }
 
-    return 0;
+#ifdef LMKD_LOG_STATS
+    if (enable_stats_log) {
+        stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
+    }
+#endif
+
+    return pages_freed;
 }
 
 static int64_t get_memory_usage(const char* path) {
@@ -674,33 +826,119 @@
     return mem_usage;
 }
 
-static void mp_event_common(bool is_critical) {
+void record_low_pressure_levels(struct mem_size *free_mem) {
+    if (low_pressure_mem.min_free == -1 ||
+        low_pressure_mem.min_free > free_mem->free_mem) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure min memory update from %d to %d",
+                low_pressure_mem.min_free, free_mem->free_mem);
+        }
+        low_pressure_mem.min_free = free_mem->free_mem;
+    }
+    /*
+     * Free memory at low vmpressure events occasionally gets spikes,
+     * possibly a stale low vmpressure event with memory already
+     * freed up (no memory pressure should have been reported).
+     * Ignore large jumps in max_free that would mess up our stats.
+     */
+    if (low_pressure_mem.max_free == -1 ||
+        (low_pressure_mem.max_free < free_mem->free_mem &&
+         free_mem->free_mem - low_pressure_mem.max_free < low_pressure_mem.max_free * 0.1)) {
+        if (debug_process_killing) {
+            ALOGI("Low pressure max memory update from %d to %d",
+                low_pressure_mem.max_free, free_mem->free_mem);
+        }
+        low_pressure_mem.max_free = free_mem->free_mem;
+    }
+}
+
+enum vmpressure_level upgrade_level(enum vmpressure_level level) {
+    return (enum vmpressure_level)((level < VMPRESS_LEVEL_CRITICAL) ?
+        level + 1 : level);
+}
+
+enum vmpressure_level downgrade_level(enum vmpressure_level level) {
+    return (enum vmpressure_level)((level > VMPRESS_LEVEL_LOW) ?
+        level - 1 : level);
+}
+
+static inline unsigned long get_time_diff_ms(struct timeval *from,
+                                             struct timeval *to) {
+    return (to->tv_sec - from->tv_sec) * 1000 +
+           (to->tv_usec - from->tv_usec) / 1000;
+}
+
+static void mp_event_common(int data, uint32_t events __unused) {
     int ret;
     unsigned long long evcount;
-    int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
     int64_t mem_usage, memsw_usage;
     int64_t mem_pressure;
+    enum vmpressure_level lvl;
+    struct mem_size free_mem;
+    static struct timeval last_report_tm;
+    static unsigned long skip_count = 0;
+    enum vmpressure_level level = (enum vmpressure_level)data;
 
-    ret = read(mpevfd[index], &evcount, sizeof(evcount));
-    if (ret < 0)
-        ALOGE("Error reading memory pressure event fd; errno=%d",
-              errno);
+    /*
+     * Check all event counters from low to critical
+     * and upgrade to the highest priority one. By reading
+     * eventfd we also reset the event counters.
+     */
+    for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+        if (mpevfd[lvl] != -1 &&
+            read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
+            evcount > 0 && lvl > level) {
+            level = lvl;
+        }
+    }
+
+    if (kill_timeout_ms) {
+        struct timeval curr_tm;
+        gettimeofday(&curr_tm, NULL);
+        if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
+            skip_count++;
+            return;
+        }
+    }
+
+    if (skip_count > 0) {
+        if (debug_process_killing) {
+            ALOGI("%lu memory pressure events were skipped after a kill!",
+                skip_count);
+        }
+        skip_count = 0;
+    }
+
+    if (get_free_memory(&free_mem) == 0) {
+        if (level == VMPRESS_LEVEL_LOW) {
+            record_low_pressure_levels(&free_mem);
+        }
+    } else {
+        ALOGE("Failed to get free memory!");
+        return;
+    }
+
+    if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
+        /* Do not monitor this pressure level */
+        return;
+    }
 
     mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
     memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
     if (memsw_usage < 0 || mem_usage < 0) {
-        find_and_kill_process(is_critical);
-        return;
+        goto do_kill;
     }
 
     // Calculate percent for swappinness.
     mem_pressure = (mem_usage * 100) / memsw_usage;
 
-    if (enable_pressure_upgrade && !is_critical) {
+    if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
         // We are swapping too much.
         if (mem_pressure < upgrade_pressure) {
-            ALOGI("Event upgraded to critical.");
-            is_critical = true;
+            level = upgrade_level(level);
+            if (debug_process_killing) {
+                ALOGI("Event upgraded to %s", level_name[level]);
+            }
         }
     }
 
@@ -708,41 +946,63 @@
     // kill any process, since enough memory is available.
     if (mem_pressure > downgrade_pressure) {
         if (debug_process_killing) {
-            ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium");
+            ALOGI("Ignore %s memory pressure", level_name[level]);
         }
         return;
-    } else if (is_critical && mem_pressure > upgrade_pressure) {
+    } else if (level == VMPRESS_LEVEL_CRITICAL &&
+               mem_pressure > upgrade_pressure) {
         if (debug_process_killing) {
             ALOGI("Downgrade critical memory pressure");
         }
-        // Downgrade event to medium, since enough memory available.
-        is_critical = false;
+        // Downgrade event, since enough memory available.
+        level = downgrade_level(level);
     }
 
-    if (find_and_kill_process(is_critical) == 0) {
-        if (debug_process_killing) {
-            ALOGI("Nothing to kill");
+do_kill:
+    if (is_go_device) {
+        /* For Go devices kill only one task */
+        if (find_and_kill_processes(level, 0) == 0) {
+            if (debug_process_killing) {
+                ALOGI("Nothing to kill");
+            }
+        }
+    } else {
+        /* If pressure level is less than critical and enough free swap then ignore */
+        if (level < VMPRESS_LEVEL_CRITICAL && free_mem.free_swap > low_pressure_mem.max_free) {
+            if (debug_process_killing) {
+                ALOGI("Ignoring pressure since %d swap pages are available ", free_mem.free_swap);
+            }
+            return;
+        }
+
+        /* Free up enough memory to downgrate the memory pressure to low level */
+        if (free_mem.free_mem < low_pressure_mem.max_free) {
+            int pages_to_free = low_pressure_mem.max_free - free_mem.free_mem;
+            if (debug_process_killing) {
+                ALOGI("Trying to free %d pages", pages_to_free);
+            }
+            int pages_freed = find_and_kill_processes(level, pages_to_free);
+            if (pages_freed < pages_to_free) {
+                if (debug_process_killing) {
+                    ALOGI("Unable to free enough memory (pages freed=%d)",
+                        pages_freed);
+                }
+            } else {
+                gettimeofday(&last_report_tm, NULL);
+            }
         }
     }
 }
 
-static void mp_event(uint32_t events __unused) {
-    mp_event_common(false);
-}
-
-static void mp_event_critical(uint32_t events __unused) {
-    mp_event_common(true);
-}
-
-static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
-{
+static bool init_mp_common(enum vmpressure_level level) {
     int mpfd;
     int evfd;
     int evctlfd;
     char buf[256];
     struct epoll_event epev;
     int ret;
-    int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
+    int level_idx = (int)level;
+    const char *levelstr = level_name[level_idx];
 
     mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
@@ -768,7 +1028,7 @@
         goto err;
     }
 
-    ret = write(evctlfd, buf, strlen(buf) + 1);
+    ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
     if (ret == -1) {
         ALOGE("cgroup.event_control write failed for level %s; errno=%d",
               levelstr, errno);
@@ -776,15 +1036,19 @@
     }
 
     epev.events = EPOLLIN;
-    epev.data.ptr = event_handler;
+    /* use data to store event level */
+    vmpressure_hinfo[level_idx].data = level_idx;
+    vmpressure_hinfo[level_idx].handler = mp_event_common;
+    epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
     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[mpevfd_index] = evfd;
-    return 0;
+    mpevfd[level] = evfd;
+    close(evctlfd);
+    return true;
 
 err:
     close(evfd);
@@ -793,17 +1057,7 @@
 err_open_evctlfd:
     close(mpfd);
 err_open_mpfd:
-    return -1;
-}
-
-static int init_mp_medium()
-{
-    return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
-}
-
-static int init_mp_critical()
-{
-    return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
+    return false;
 }
 
 static int init(void) {
@@ -822,36 +1076,44 @@
         return -1;
     }
 
-    ctrl_lfd = android_get_control_socket("lmkd");
-    if (ctrl_lfd < 0) {
+    // mark data connections as not connected
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        data_sock[i].sock = -1;
+    }
+
+    ctrl_sock.sock = android_get_control_socket("lmkd");
+    if (ctrl_sock.sock < 0) {
         ALOGE("get lmkd control socket failed");
         return -1;
     }
 
-    ret = listen(ctrl_lfd, 1);
+    ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
     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) {
+    ctrl_sock.handler_info.handler = ctrl_connect_handler;
+    epev.data.ptr = (void *)&(ctrl_sock.handler_info);
+    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
         ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
         return -1;
     }
     maxevents++;
 
     has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
-    use_inkernel_interface = has_inkernel_module && !is_go_device;
+    use_inkernel_interface = has_inkernel_module;
 
     if (use_inkernel_interface) {
         ALOGI("Using in-kernel low memory killer interface");
     } else {
-        ret = init_mp_medium();
-        ret |= init_mp_critical();
-        if (ret)
+        if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+            !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+            !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+            return -1;
+        }
     }
 
     for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -863,12 +1125,14 @@
 }
 
 static void mainloop(void) {
+    struct event_handler_info* handler_info;
+    struct epoll_event *evt;
+
     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) {
@@ -878,11 +1142,33 @@
             continue;
         }
 
-        for (i = 0; i < nevents; ++i) {
-            if (events[i].events & EPOLLERR)
+        /*
+         * First pass to see if any data socket connections were dropped.
+         * Dropped connection should be handled before any other events
+         * to deallocate data connection and correctly handle cases when
+         * connection gets dropped and reestablished in the same epoll cycle.
+         * In such cases it's essential to handle connection closures first.
+         */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if ((evt->events & EPOLLHUP) && evt->data.ptr) {
+                ALOGI("lmkd data connection dropped");
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                ctrl_data_close(handler_info->data);
+            }
+        }
+
+        /* Second pass to handle all other events */
+        for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+            if (evt->events & EPOLLERR)
                 ALOGD("EPOLLERR on event #%d", i);
-            if (events[i].data.ptr)
-                (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+            if (evt->events & EPOLLHUP) {
+                /* This case was handled in the first pass */
+                continue;
+            }
+            if (evt->data.ptr) {
+                handler_info = (struct event_handler_info*)evt->data.ptr;
+                handler_info->handler(handler_info->data, evt->events);
+            }
         }
     }
 }
@@ -892,13 +1178,31 @@
             .sched_priority = 1,
     };
 
-    medium_oomadj = property_get_int32("ro.lmk.medium", 800);
-    critical_oomadj = property_get_int32("ro.lmk.critical", 0);
+    /* By default disable low level vmpressure events */
+    level_oomadj[VMPRESS_LEVEL_LOW] =
+        property_get_int32("ro.lmk.low", OOM_SCORE_ADJ_MAX + 1);
+    level_oomadj[VMPRESS_LEVEL_MEDIUM] =
+        property_get_int32("ro.lmk.medium", 800);
+    level_oomadj[VMPRESS_LEVEL_CRITICAL] =
+        property_get_int32("ro.lmk.critical", 0);
     debug_process_killing = property_get_bool("ro.lmk.debug", false);
-    enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
-    upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
-    downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+
+    /* By default disable upgrade/downgrade logic */
+    enable_pressure_upgrade =
+        property_get_bool("ro.lmk.critical_upgrade", false);
+    upgrade_pressure =
+        (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 100);
+    downgrade_pressure =
+        (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
+    kill_heaviest_task =
+        property_get_bool("ro.lmk.kill_heaviest_task", true);
     is_go_device = property_get_bool("ro.config.low_ram", false);
+    kill_timeout_ms =
+        (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
+
+#ifdef LMKD_LOG_STATS
+    statlog_init();
+#endif
 
     // MCL_ONFAULT pins pages as they fault instead of loading
     // everything immediately all at once. (Which would be bad,
@@ -916,6 +1220,10 @@
     if (!init())
         mainloop();
 
+#ifdef LMKD_LOG_STATS
+    statslog_destroy();
+#endif
+
     ALOGI("exiting");
     return 0;
 }
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
new file mode 100644
index 0000000..db7a76a
--- /dev/null
+++ b/lmkd/statslog.c
@@ -0,0 +1,100 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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 <assert.h>
+#include <errno.h>
+#include <log/log_event_list.h>
+#include <log/log_id.h>
+
+/**
+ * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * LMK_KILL_OCCURRED event.
+ * Code: LMK_STATE_CHANGED = 54
+ */
+int
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
+    assert(ctx != NULL);
+    int ret = -EINVAL;
+    if (!ctx) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, state)) < 0) {
+        return ret;
+    }
+    return ret;
+}
+
+/**
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+int
+stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
+                              char const* process_name, int32_t oom_score, int64_t pgfault,
+                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
+                              int64_t swap_in_bytes) {
+    assert(ctx != NULL);
+    int ret = -EINVAL;
+    if (!ctx) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, code)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, uid)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
+        return ret;
+    }
+
+    if ((ret = android_log_write_list(ctx, LOG_ID_STATS)) < 0) {
+        return ret;
+    }
+    return ret;
+}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
new file mode 100644
index 0000000..b567fbf
--- /dev/null
+++ b/lmkd/statslog.h
@@ -0,0 +1,87 @@
+/*
+ *  Copyright 2018 Google, Inc
+ *
+ *  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.
+ */
+
+#ifndef _STATSLOG_H_
+#define _STATSLOG_H_
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/properties.h>
+#include <log/log_event_list.h>
+
+__BEGIN_DECLS
+
+/*
+ * These are defined in
+ * http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
+ */
+#define LMK_KILL_OCCURRED 51
+#define LMK_STATE_CHANGED 54
+#define LMK_STATE_CHANGE_START 1
+#define LMK_STATE_CHANGE_STOP 2
+
+static inline void statslog_init() {
+    enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+
+    if (enable_stats_log) {
+        log_ctx = create_android_logger(kStatsEventTag);
+    }
+}
+
+static inline void statslog_destroy() {
+    if (log_ctx) {
+        android_log_destroy(&log_ctx);
+    }
+}
+
+struct memory_stat {
+    int64_t pgfault;
+    int64_t pgmajfault;
+    int64_t rss_in_bytes;
+    int64_t cache_in_bytes;
+    int64_t swap_in_bytes;
+};
+
+#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
+
+/*
+ * The single event tag id for all stats logs.
+ * Keep this in sync with system/core/logcat/event.logtags
+ */
+const static int kStatsEventTag = 1937006964;
+
+/**
+ * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * LMK_KILL_OCCURRED event.
+ * Code: LMK_STATE_CHANGED = 54
+ */
+int
+stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
+
+/**
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+int
+stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
+                              char const* process_name, int32_t oom_score, int64_t pgfault,
+                              int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
+                              int64_t swap_in_bytes);
+
+__END_DECLS
+
+#endif /* _STATSLOG_H_ */
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
new file mode 100644
index 0000000..cbf44e9
--- /dev/null
+++ b/lmkd/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 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.
+
+cc_test {
+    name: "lmkd_unit_test",
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    static_libs: [
+        "liblmkd_utils",
+    ],
+
+    target: {
+        android: {
+            srcs: ["lmkd_test.cpp"],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    compile_multilib: "first",
+}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
new file mode 100644
index 0000000..4afaeb8
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2018 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 <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <lmkd.h>
+#include <liblmkd_utils.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+using namespace android::base;
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
+
+#define LMKD_LOGCAT_MARKER "lowmemorykiller"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define OOM_MARKER "Out of memory"
+#define OOM_KILL_MARKER "Killed process"
+#define MIN_LOG_SIZE 100
+
+#define ONE_MB (1 << 20)
+
+/* Test constant parameters */
+#define OOM_ADJ_MAX 1000
+#define OOM_ADJ_MIN 0
+#define OOM_ADJ_STEP 100
+#define STEP_COUNT ((OOM_ADJ_MAX - OOM_ADJ_MIN) / OOM_ADJ_STEP + 1)
+
+#define ALLOC_STEP (ONE_MB)
+#define ALLOC_DELAY 1000
+
+/* Utility functions */
+std::string readCommand(const std::string& command) {
+    FILE* fp = popen(command.c_str(), "r");
+    std::string content;
+    ReadFdToString(fileno(fp), &content);
+    pclose(fp);
+    return content;
+}
+
+std::string readLogcat(const std::string& marker) {
+    std::string content = readCommand("logcat -d -b all");
+    size_t pos = content.find(marker);
+    if (pos == std::string::npos) return "";
+    content.erase(0, pos);
+    return content;
+}
+
+bool writeFile(const std::string& file, const std::string& string) {
+    if (getuid() == static_cast<unsigned>(AID_ROOT)) {
+        return WriteStringToFile(string, file);
+    }
+    return string == readCommand(
+        "echo -n '" + string + "' | su root tee " + file + " 2>&1");
+}
+
+bool writeKmsg(const std::string& marker) {
+    return writeFile("/dev/kmsg", marker);
+}
+
+std::string getTextAround(const std::string& text, size_t pos,
+                          size_t lines_before, size_t lines_after) {
+    size_t start_pos = pos;
+
+    // find start position
+    // move up lines_before number of lines
+    while (lines_before > 0 &&
+           (start_pos = text.rfind('\n', start_pos)) != std::string::npos) {
+        lines_before--;
+    }
+    // move to the beginning of the line
+    start_pos = text.rfind('\n', start_pos);
+    start_pos = (start_pos == std::string::npos) ? 0 : start_pos + 1;
+
+    // find end position
+    // move down lines_after number of lines
+    while (lines_after > 0 &&
+           (pos = text.find('\n', pos)) != std::string::npos) {
+        pos++;
+        lines_after--;
+    }
+    return text.substr(start_pos, (pos == std::string::npos) ?
+                       std::string::npos : pos - start_pos);
+}
+
+bool getExecPath(std::string &path) {
+    char buf[PATH_MAX + 1];
+    int ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+    if (ret < 0) {
+        return false;
+    }
+    buf[ret] = '\0';
+    path = buf;
+    return true;
+}
+
+/* Child synchronization primitives */
+#define STATE_INIT 0
+#define STATE_CHILD_READY 1
+#define STATE_PARENT_READY 2
+
+struct state_sync {
+    pthread_mutex_t mutex;
+    pthread_cond_t condition;
+    int state;
+};
+
+struct state_sync * init_state_sync_obj() {
+    struct state_sync *ssync;
+
+    ssync = (struct state_sync*)mmap(NULL, sizeof(struct state_sync),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    if (ssync == MAP_FAILED) {
+        return NULL;
+    }
+
+    pthread_mutexattr_t mattr;
+    pthread_mutexattr_init(&mattr);
+    pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+    pthread_mutex_init(&ssync->mutex, &mattr);
+
+    pthread_condattr_t cattr;
+    pthread_condattr_init(&cattr);
+    pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+    pthread_cond_init(&ssync->condition, &cattr);
+
+    ssync->state = STATE_INIT;
+    return ssync;
+}
+
+void destroy_state_sync_obj(struct state_sync *ssync) {
+    pthread_cond_destroy(&ssync->condition);
+    pthread_mutex_destroy(&ssync->mutex);
+    munmap(ssync, sizeof(struct state_sync));
+}
+
+void signal_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    ssync->state = state;
+    pthread_cond_signal(&ssync->condition);
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+void wait_for_state(struct state_sync *ssync, int state) {
+    pthread_mutex_lock(&ssync->mutex);
+    while (ssync->state != state) {
+        pthread_cond_wait(&ssync->condition, &ssync->mutex);
+    }
+    pthread_mutex_unlock(&ssync->mutex);
+}
+
+/* Memory allocation and data sharing */
+struct shared_data {
+    size_t allocated;
+    bool finished;
+    size_t total_size;
+    size_t step_size;
+    size_t step_delay;
+    int oomadj;
+};
+
+volatile void *gptr;
+void add_pressure(struct shared_data *data) {
+    volatile void *ptr;
+    size_t allocated_size = 0;
+
+    data->finished = false;
+    while (allocated_size < data->total_size) {
+        ptr = mmap(NULL, data->step_size, PROT_READ | PROT_WRITE,
+                MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+        if (ptr != MAP_FAILED) {
+            /* create ptr aliasing to prevent compiler optimizing the access */
+            gptr = ptr;
+            /* make data non-zero */
+            memset((void*)ptr, (int)(allocated_size + 1), data->step_size);
+            allocated_size += data->step_size;
+            data->allocated = allocated_size;
+        }
+        usleep(data->step_delay);
+    }
+    data->finished = (allocated_size >= data->total_size);
+}
+
+/* Memory stress test main body */
+void runMemStressTest() {
+    struct shared_data *data;
+    struct state_sync *ssync;
+    int sock;
+    pid_t pid;
+    uid_t uid = getuid();
+
+    ASSERT_FALSE((sock = lmkd_connect()) < 0)
+        << "Failed to connect to lmkd process, err=" << strerror(errno);
+
+    /* allocate shared memory to communicate params with a child */
+    data = (struct shared_data*)mmap(NULL, sizeof(struct shared_data),
+                PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+    ASSERT_FALSE(data == MAP_FAILED) << "Memory allocation failure";
+    data->total_size = (size_t)-1; /* allocate until killed */
+    data->step_size = ALLOC_STEP;
+    data->step_delay = ALLOC_DELAY;
+
+    /* allocate state sync object */
+    ASSERT_FALSE((ssync = init_state_sync_obj()) == NULL)
+        << "Memory allocation failure";
+
+    /* run the test gradually decreasing oomadj */
+    data->oomadj = OOM_ADJ_MAX;
+    while (data->oomadj >= OOM_ADJ_MIN) {
+        ASSERT_FALSE((pid = fork()) < 0)
+            << "Failed to spawn a child process, err=" << strerror(errno);
+        if (pid != 0) {
+            /* Parent */
+            struct lmk_procprio params;
+            /* wait for child to start and get ready */
+            wait_for_state(ssync, STATE_CHILD_READY);
+            params.pid = pid;
+            params.uid = uid;
+            params.oomadj = data->oomadj;
+            ASSERT_FALSE(lmkd_register_proc(sock, &params) < 0)
+                << "Failed to communicate with lmkd, err=" << strerror(errno);
+            // signal the child it can proceed
+            signal_state(ssync, STATE_PARENT_READY);
+            waitpid(pid, NULL, 0);
+            if (data->finished) {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB << "MB";
+            } else {
+                GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+                                 << data->allocated / ONE_MB
+                                 << "MB before being killed";
+            }
+            data->oomadj -= OOM_ADJ_STEP;
+        } else {
+            /* Child */
+            pid = getpid();
+            GTEST_LOG_(INFO) << "Child [pid=" << pid
+                             << "] is running at oomadj="
+                             << data->oomadj;
+            data->allocated = 0;
+            data->finished = false;
+            ASSERT_FALSE(create_memcg(uid, pid) != 0)
+                << "Child [pid=" << pid << "] failed to create a cgroup";
+            signal_state(ssync, STATE_CHILD_READY);
+            wait_for_state(ssync, STATE_PARENT_READY);
+            add_pressure(data);
+            /* should not reach here, child should be killed by OOM/LMK */
+            FAIL() << "Child [pid=" << pid << "] was not killed";
+            break;
+        }
+    }
+    destroy_state_sync_obj(ssync);
+    munmap(data, sizeof(struct shared_data));
+    close(sock);
+}
+
+TEST(lmkd, check_for_oom) {
+    // test requirements
+    //   userdebug build
+    if (!__android_log_is_debuggable()) {
+        GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
+        return;
+    }
+    // check if in-kernel LMK driver is present
+    if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
+        GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
+                         << " terminating test";
+        return;
+    }
+
+    // if respawned test process then run the test and exit (no analysis)
+    if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
+        runMemStressTest();
+        return;
+    }
+
+    // Main test process
+    // mark the beginning of the test
+    std::string marker = StringPrintf(
+        "LMKD test start %lu\n", static_cast<unsigned long>(time(nullptr)));
+    ASSERT_TRUE(writeKmsg(marker));
+
+    // get executable complete path
+    std::string test_path;
+    ASSERT_TRUE(getExecPath(test_path));
+
+    std::string test_output;
+    if (getuid() != static_cast<unsigned>(AID_ROOT)) {
+        // if not root respawn itself as root and capture output
+        std::string command = StringPrintf(
+            "%s=true su root %s 2>&1", LMKDTEST_RESPAWN_FLAG,
+            test_path.c_str());
+        std::string test_output = readCommand(command);
+        GTEST_LOG_(INFO) << test_output;
+    } else {
+        // main test process is root, run the test
+        runMemStressTest();
+    }
+
+    // Analyze results
+    // capture logcat containind kernel logs
+    std::string logcat_out = readLogcat(marker);
+
+    // 1. extract LMKD kills from logcat output, count kills
+    std::stringstream kill_logs;
+    int hit_count = 0;
+    size_t pos = 0;
+    marker = StringPrintf(LMKD_KILL_MARKER_TEMPLATE, test_path.c_str());
+
+    while (true) {
+        if ((pos = logcat_out.find(marker, pos)) != std::string::npos) {
+            kill_logs << getTextAround(logcat_out, pos, 0, 1);
+            pos += marker.length();
+            hit_count++;
+        } else {
+            break;
+        }
+    }
+    GTEST_LOG_(INFO) << "====Logged kills====" << std::endl
+                     << kill_logs.str();
+    EXPECT_TRUE(hit_count == STEP_COUNT) << "Number of kills " << hit_count
+                                         << " is less than expected "
+                                         << STEP_COUNT;
+
+    // 2. check kernel logs for OOM kills
+    pos = logcat_out.find(OOM_MARKER);
+    bool oom_detected = (pos != std::string::npos);
+    bool oom_kill_detected = (oom_detected &&
+        logcat_out.find(OOM_KILL_MARKER, pos) != std::string::npos);
+
+    EXPECT_FALSE(oom_kill_detected) << "OOM kill is detected!";
+    if (oom_detected || oom_kill_detected) {
+        // capture logcat with logs around all OOMs
+        pos = 0;
+        while ((pos = logcat_out.find(OOM_MARKER, pos)) != std::string::npos) {
+            GTEST_LOG_(INFO) << "====Logs around OOM====" << std::endl
+                             << getTextAround(logcat_out, pos,
+                                    MIN_LOG_SIZE / 2, MIN_LOG_SIZE / 2);
+            pos += strlen(OOM_MARKER);
+        }
+    }
+
+    // output complete logcat with kernel (might get truncated)
+    GTEST_LOG_(INFO) << "====Complete logcat output====" << std::endl
+                     << logcat_out;
+}
+
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index b76160d..269db2f 100755
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -331,7 +331,7 @@
             reinterpret_cast<android_log_event_string_t*>(buffer);
         event->header.tag = htole32(AUDITD_LOG_TAG);
         event->type = EVENT_TYPE_STRING;
-        event->length = htole32(message_len);
+        event->length = htole32(str_len);
         memcpy(event->data, str, str_len - bug_metadata.length());
         memcpy(event->data + str_len - bug_metadata.length(),
                bug_metadata.c_str(), bug_metadata.length());
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a213ffb..4d823ea 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,7 +86,7 @@
     restorecon_recursive /mnt
 
     mount configfs none /config nodev noexec nosuid
-    chmod 0775 /config/sdcardfs
+    chmod 0770 /config/sdcardfs
     chown system package_info /config/sdcardfs
 
     mkdir /mnt/secure 0700 root root
@@ -423,6 +423,7 @@
     mkdir /data/misc/sms 0770 system radio
     mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/zoneinfo 0775 system system
+    mkdir /data/misc/network_watchlist 0774 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn
     mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
@@ -455,6 +456,8 @@
     mkdir /data/misc/gcov 0770 root root
 
     mkdir /data/vendor 0771 root root
+    mkdir /data/vendor_ce 0771 root root
+    mkdir /data/vendor_de 0771 root root
     mkdir /data/vendor/hardware 0771 root root
 
     # For security reasons, /data/local/tmp should always be empty.
@@ -731,7 +734,7 @@
     shutdown critical
 
 service healthd /system/bin/healthd
-    class core
+    class hal
     critical
     group root system wakelock
 
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 574bbfe..dc36596 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -43,6 +43,44 @@
 
 #include <private/android_filesystem_config.h>
 
+#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
+#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+
+static bool supports_esdfs(void) {
+    std::string filesystems;
+    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
+        PLOG(ERROR) << "Could not read /proc/filesystems";
+        return false;
+    }
+    for (const auto& fs : android::base::Split(filesystems, "\n")) {
+        if (fs.find("esdfs") != std::string::npos) return true;
+    }
+    return false;
+}
+
+static bool should_use_sdcardfs(void) {
+    char property[PROPERTY_VALUE_MAX];
+
+    // Allow user to have a strong opinion about state
+    property_get(PROP_SDCARDFS_USER, property, "");
+    if (!strcmp(property, "force_on")) {
+        LOG(WARNING) << "User explicitly enabled sdcardfs";
+        return true;
+    } else if (!strcmp(property, "force_off")) {
+        LOG(WARNING) << "User explicitly disabled sdcardfs";
+        return !supports_esdfs();
+    }
+
+    // Fall back to device opinion about state
+    if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {
+        LOG(WARNING) << "Device explicitly enabled sdcardfs";
+        return true;
+    } else {
+        LOG(WARNING) << "Device explicitly disabled sdcardfs";
+        return !supports_esdfs();
+    }
+}
+
 // NOTE: This is a vestigial program that simply exists to mount the in-kernel
 // sdcardfs filesystem.  The older FUSE-based design that used to live here has
 // been completely removed to avoid confusion.
@@ -61,7 +99,7 @@
 
 static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
                            uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
-                           mode_t mask, bool derive_gid, bool default_normal) {
+                           mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
     // Try several attempts, each time with one less option, to gracefully
     // handle older kernels that aren't updated yet.
     for (int i = 0; i < 4; i++) {
@@ -72,7 +110,7 @@
 
         auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
                                                 fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
-        if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+        if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? "esdfs" : "sdcardfs",
                   MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
             PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
         } else {
@@ -104,9 +142,21 @@
     return true;
 }
 
+static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
+                                     const std::string& dest_path, uid_t fsuid, gid_t fsgid,
+                                     bool multi_user, userid_t userid, gid_t gid, mode_t mask,
+                                     bool derive_gid, bool default_normal, bool use_esdfs) {
+    if (use_esdfs) {
+        return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
+                              derive_gid, default_normal, use_esdfs);
+    } else {
+        return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
+    }
+}
+
 static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
                          gid_t gid, userid_t userid, bool multi_user, bool full_write,
-                         bool derive_gid, bool default_normal) {
+                         bool derive_gid, bool default_normal, bool use_esdfs) {
     std::string dest_path_default = "/mnt/runtime/default/" + label;
     std::string dest_path_read = "/mnt/runtime/read/" + label;
     std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -116,10 +166,13 @@
         // Multi-user storage is fully isolated per user, so "other"
         // permissions are completely masked off.
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
-                                         full_write ? 0007 : 0027)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
+                                      default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
+                                      derive_gid, default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     } else {
@@ -127,11 +180,13 @@
         // the Android directories are masked off to a single user
         // deep inside attr_from_stat().
         if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
-                            AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
-                                         full_write ? 0027 : 0022) ||
-            !sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
-                                         full_write ? 0007 : 0022)) {
+                            AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
+                                      derive_gid, default_normal, use_esdfs) ||
+            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
+                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
+                                      derive_gid, default_normal, use_esdfs)) {
             LOG(FATAL) << "failed to sdcardfs_setup";
         }
     }
@@ -242,6 +297,6 @@
     }
 
     run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
-                 default_normal);
+                 default_normal, !should_use_sdcardfs());
     return 1;
 }
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..b478f4a
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+cc_defaults {
+    name: "storaged_defaults",
+
+    shared_libs: [
+        "android.hardware.health@1.0",
+        "android.hardware.health@2.0",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libsysutils",
+        "libutils",
+        "libz",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter"
+    ],
+}
+
+cc_library_static {
+    name: "libstoraged",
+
+    defaults: ["storaged_defaults"],
+
+    aidl: {
+        export_aidl_headers: true,
+        local_include_dirs: ["binder"],
+        include_dirs: ["frameworks/native/aidl/binder"],
+    },
+
+    srcs: [
+        "storaged.cpp",
+        "storaged_diskstats.cpp",
+        "storaged_info.cpp",
+        "storaged_service.cpp",
+        "storaged_utils.cpp",
+        "storaged_uid_monitor.cpp",
+        "uid_info.cpp",
+        "storaged.proto",
+        ":storaged_aidl",
+        "binder/android/os/storaged/IStoragedPrivate.aidl",
+    ],
+
+    static_libs: ["libhealthhalutils"],
+
+    logtags: ["EventLogTags.logtags"],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    },
+
+    export_include_dirs: ["include"],
+}
+
+cc_binary {
+    name: "storaged",
+
+    defaults: ["storaged_defaults"],
+
+    init_rc: ["storaged.rc"],
+
+    srcs: ["main.cpp"],
+
+    static_libs: [
+        "libhealthhalutils",
+        "libstoraged",
+    ],
+}
+
+/*
+ * Run with:
+ *  adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+ */
+cc_test {
+    name: "storaged-unit-tests",
+
+    defaults: ["storaged_defaults"],
+
+    srcs: ["tests/storaged_test.cpp"],
+
+    static_libs: [
+        "libhealthhalutils",
+        "libstoraged",
+    ],
+}
+
+// AIDL interface between storaged and framework.jar
+filegroup {
+    name: "storaged_aidl",
+    srcs: [
+        "binder/android/os/IStoraged.aidl",
+    ],
+}
diff --git a/storaged/Android.mk b/storaged/Android.mk
deleted file mode 100644
index a1abe0f..0000000
--- a/storaged/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-LIBSTORAGED_SHARED_LIBRARIES := \
-    libbinder \
-    libbase \
-    libutils \
-    libcutils \
-    liblog \
-    libsysutils \
-    libbatteryservice \
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    storaged.cpp \
-    storaged_info.cpp \
-    storaged_service.cpp \
-    storaged_utils.cpp \
-    storaged_uid_monitor.cpp \
-    EventLogTags.logtags
-
-LOCAL_MODULE := libstoraged
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := storaged
-LOCAL_INIT_RC := storaged.rc
-LOCAL_SRC_FILES := main.cpp
-# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
-LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
-LOCAL_C_INCLUDES := external/googletest/googletest/include
-
-include $(BUILD_EXECUTABLE)
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/binder/android/os/IStoraged.aidl b/storaged/binder/android/os/IStoraged.aidl
new file mode 100644
index 0000000..0bcc70c
--- /dev/null
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IStoraged {
+    void onUserStarted(int userId);
+    void onUserStopped(int userId);
+    int getRecentPerf();
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/IStoragedPrivate.aidl b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
new file mode 100644
index 0000000..9c888e3
--- /dev/null
+++ b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.os.storaged;
+
+import android.os.storaged.UidInfo;
+
+/** {@hide} */
+interface IStoragedPrivate {
+    UidInfo[] dumpUids();
+    int[] dumpPerfHistory();
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/UidInfo.aidl b/storaged/binder/android/os/storaged/UidInfo.aidl
new file mode 100644
index 0000000..440f386
--- /dev/null
+++ b/storaged/binder/android/os/storaged/UidInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.os.storaged;
+
+parcelable UidInfo cpp_header "include/uid_info.h";
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index fa68406..400e734 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -27,27 +27,18 @@
 #include <vector>
 
 #include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <utils/Mutex.h>
 
-#include "storaged_info.h"
-#include "storaged_uid_monitor.h"
-
-using namespace android;
+#include <android/hardware/health/2.0/IHealth.h>
 
 #define FRIEND_TEST(test_case_name, test_name) \
 friend class test_case_name##_##test_name##_Test
 
-/* For debug */
-#ifdef DEBUG
-#define debuginfo(fmt, ...) \
- do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
- while(0)
-#else
-#define debuginfo(...)
-#endif
-
 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))
 
+#define IS_ALIGNED(x, align)   (!((x) & ((align) - 1)))
+#define ROUND_UP(x, align)     (((x) + ((align) - 1)) & ~((align) - 1))
+
 #define SECTOR_SIZE ( 512 )
 #define SEC_TO_MSEC ( 1000 )
 #define MSEC_TO_USEC ( 1000 )
@@ -55,184 +46,24 @@
 #define SEC_TO_USEC ( 1000000 )
 #define HOUR_TO_SEC ( 3600 )
 #define DAY_TO_SEC ( 3600 * 24 )
+#define WEEK_TO_DAYS ( 7 )
+#define YEAR_TO_WEEKS ( 52 )
 
-// number of attributes diskstats has
-#define DISK_STATS_SIZE ( 11 )
-// maximum size limit of a stats file
-#define DISK_STATS_FILE_MAX_SIZE ( 256 )
-#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
-struct disk_stats {
-    /* It will be extremely unlikely for any of the following entries to overflow.
-     * For read_bytes(which will be greater than any of the following entries), it
-     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
-     * is the peak memory transfer rate for current memory.
-     * The diskstats entries (first 11) need to be at top in this structure _after_
-     * compiler's optimization.
-     */
-    uint64_t read_ios;       // number of read I/Os processed
-    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
-    uint64_t read_sectors;   // number of sectors read
-    uint64_t read_ticks;     // total wait time for read requests
-    uint64_t write_ios;      // number of write I/Os processed
-    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
-    uint64_t write_sectors;  // number of sectors written
-    uint64_t write_ticks;    // total wait time for write requests
-    uint64_t io_in_flight;   // number of I/Os currently in flight
-    uint64_t io_ticks;       // total time this block device has been active
-    uint64_t io_in_queue;    // total wait time for all requests
+#include "storaged_diskstats.h"
+#include "storaged_info.h"
+#include "storaged_uid_monitor.h"
+#include "storaged.pb.h"
+#include "uid_info.h"
 
-    uint64_t start_time;     // monotonic time accounting starts
-    uint64_t end_time;       // monotonic time accounting ends
-    uint32_t counter;        // private counter for accumulate calculations
-    double   io_avg;         // average io_in_flight for accumulate calculations
-};
-
-
-
-struct disk_perf {
-    uint32_t read_perf;         // read speed (kbytes/s)
-    uint32_t read_ios;          // read I/Os per second
-    uint32_t write_perf;        // write speed (kbytes/s)
-    uint32_t write_ios;         // write I/Os per second
-    uint32_t queue;             // I/Os in queue
-};
-
-#define CMD_MAX_LEN ( 64 )
-struct task_info {
-    uint32_t pid;                   // task id
-    uint64_t rchar;                 // characters read
-    uint64_t wchar;                 // characters written
-    uint64_t syscr;                 // read syscalls
-    uint64_t syscw;                 // write syscalls
-    uint64_t read_bytes;            // bytes read (from storage layer)
-    uint64_t write_bytes;           // bytes written (to storage layer)
-    uint64_t cancelled_write_bytes; // cancelled write byte by truncate
-
-    uint64_t starttime;             // start time of task
-
-    char cmd[CMD_MAX_LEN];          // filename of the executable
-};
-
-class lock_t {
-    sem_t* mSem;
-public:
-    lock_t(sem_t* sem) {
-        mSem = sem;
-        sem_wait(mSem);
-    }
-    ~lock_t() {
-        sem_post(mSem);
-    }
-};
-
-class stream_stats {
-private:
-    double mSum;
-    double mSquareSum;
-    uint32_t mCnt;
-public:
-    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
-    ~stream_stats() {};
-    double get_mean() {
-        return mSum / mCnt;
-    }
-    double get_std() {
-        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
-    }
-    void add(uint32_t num) {
-        mSum += (double)num;
-        mSquareSum += (double)num * (double)num;
-        mCnt++;
-    }
-    void evict(uint32_t num) {
-        if (mSum < num || mSquareSum < (double)num * (double)num) return;
-        mSum -= (double)num;
-        mSquareSum -= (double)num * (double)num;
-        mCnt--;
-    }
-};
-
-#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
-#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
-#define UID_IO_STATS_PATH "/proc/uid_io/stats"
-
-class disk_stats_monitor {
-private:
-    FRIEND_TEST(storaged_test, disk_stats_monitor);
-    const char* DISK_STATS_PATH;
-    struct disk_stats mPrevious;
-    struct disk_stats mAccumulate;
-    bool mStall;
-    std::queue<struct disk_perf> mBuffer;
-    struct {
-        stream_stats read_perf;           // read speed (bytes/s)
-        stream_stats read_ios;            // read I/Os per second
-        stream_stats write_perf;          // write speed (bytes/s)
-        stream_stats write_ios;           // write I/O per second
-        stream_stats queue;               // I/Os in queue
-    } mStats;
-    bool mValid;
-    const uint32_t mWindow;
-    const double mSigma;
-    struct disk_perf mMean;
-    struct disk_perf mStd;
-
-    void update_mean();
-    void update_std();
-    void add(struct disk_perf* perf);
-    void evict(struct disk_perf* perf);
-    bool detect(struct disk_perf* perf);
-
-    void update(struct disk_stats* stats);
-
-public:
-    disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
-            mStall(false),
-            mValid(false),
-            mWindow(window_size),
-            mSigma(sigma) {
-        memset(&mPrevious, 0, sizeof(mPrevious));
-        memset(&mMean, 0, sizeof(mMean));
-        memset(&mStd, 0, sizeof(mStd));
-
-        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
-            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
-        } else {
-            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
-        }
-    }
-    void update(void);
-};
-
-class disk_stats_publisher {
-private:
-    FRIEND_TEST(storaged_test, disk_stats_publisher);
-    const char* DISK_STATS_PATH;
-    struct disk_stats mAccumulate;
-    struct disk_stats mPrevious;
-public:
-    disk_stats_publisher(void) {
-        memset(&mAccumulate, 0, sizeof(struct disk_stats));
-        memset(&mPrevious, 0, sizeof(struct disk_stats));
-
-        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
-            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
-        } else {
-            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
-        }
-    }
-
-    ~disk_stats_publisher(void) {}
-    void publish(void);
-    void update(void);
-};
+using namespace std;
+using namespace android;
 
 // Periodic chores intervals in seconds
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT ( 300 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO ( 3600 )
 
 // UID IO threshold in bytes
 #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
@@ -241,25 +72,35 @@
     int periodic_chores_interval_unit;
     int periodic_chores_interval_disk_stats_publish;
     int periodic_chores_interval_uid_io;
-    bool proc_uid_io_available;      // whether uid_io is accessible
-    bool diskstats_available;   // whether diskstats is accessible
+    int periodic_chores_interval_flush_proto;
     int event_time_check_usec;  // check how much cputime spent in event loop
 };
 
-class storaged_t : public BnBatteryPropertiesListener,
-                   public IBinder::DeathRecipient {
-private:
+class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
+                   public android::hardware::hidl_death_recipient {
+  private:
     time_t mTimer;
     storaged_config mConfig;
-    disk_stats_publisher mDiskStats;
-    disk_stats_monitor mDsm;
+    unique_ptr<disk_stats_monitor> mDsm;
     uid_monitor mUidm;
     time_t mStarttime;
-    sp<IBatteryPropertiesRegistrar> battery_properties;
-    std::unique_ptr<storage_info_t> storage_info;
-public:
+    sp<android::hardware::health::V2_0::IHealth> health;
+    unique_ptr<storage_info_t> storage_info;
+    static const uint32_t current_version;
+    unordered_map<userid_t, bool> proto_loaded;
+    void load_proto(userid_t user_id);
+    char* prepare_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto(userid_t user_id, StoragedProto* proto);
+    void flush_proto_data(userid_t user_id, const char* data, ssize_t size);
+    string proto_path(userid_t user_id) {
+        return string("/data/misc_ce/") + to_string(user_id) +
+               "/storaged/storaged.proto";
+    }
+    void init_health_service();
+
+  public:
     storaged_t(void);
-    ~storaged_t() {}
+    void init(void);
     void event(void);
     void event_checked(void);
     void pause(void) {
@@ -270,24 +111,37 @@
         return mStarttime;
     }
 
-    std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+    unordered_map<uint32_t, uid_info> get_uids(void) {
         return mUidm.get_uid_io_stats();
     }
-    std::map<uint64_t, struct uid_records> get_uid_records(
+
+    vector<int> get_perf_history(void) {
+        return storage_info->get_perf_history();
+    }
+
+    uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }
+
+    map<uint64_t, struct uid_records> get_uid_records(
             double hours, uint64_t threshold, bool force_report) {
         return mUidm.dump(hours, threshold, force_report);
     }
+
     void update_uid_io_interval(int interval) {
         if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
             mConfig.periodic_chores_interval_uid_io = interval;
         }
     }
 
-    void init_battery_service();
-    virtual void batteryPropertiesChanged(struct BatteryProperties props);
-    void binderDied(const wp<IBinder>& who);
+    void add_user_ce(userid_t user_id);
+    void remove_user_ce(userid_t user_id);
+
+    virtual ::android::hardware::Return<void> healthInfoChanged(
+        const ::android::hardware::health::V2_0::HealthInfo& info);
+    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
 
     void report_storage_info();
+
+    void flush_protos(unordered_map<int, StoragedProto>* protos);
 };
 
 // Eventlog tag
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
new file mode 100644
index 0000000..0b93ba6
--- /dev/null
+++ b/storaged/include/storaged_diskstats.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef _STORAGED_DISKSTATS_H_
+#define _STORAGED_DISKSTATS_H_
+
+#include <stdint.h>
+
+#include <android/hardware/health/2.0/IHealth.h>
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+
+struct disk_stats {
+    /* It will be extremely unlikely for any of the following entries to overflow.
+     * For read_bytes(which will be greater than any of the following entries), it
+     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+     * is the peak memory transfer rate for current memory.
+     * The diskstats entries (first 11) need to be at top in this structure _after_
+     * compiler's optimization.
+     */
+    uint64_t read_ios;       // number of read I/Os processed
+    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
+    uint64_t read_sectors;   // number of sectors read
+    uint64_t read_ticks;     // total wait time for read requests
+    uint64_t write_ios;      // number of write I/Os processed
+    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
+    uint64_t write_sectors;  // number of sectors written
+    uint64_t write_ticks;    // total wait time for write requests
+    uint64_t io_in_flight;   // number of I/Os currently in flight
+    uint64_t io_ticks;       // total time this block device has been active
+    uint64_t io_in_queue;    // total wait time for all requests
+
+    uint64_t start_time;     // monotonic time accounting starts
+    uint64_t end_time;       // monotonic time accounting ends
+    uint32_t counter;        // private counter for accumulate calculations
+    double   io_avg;         // average io_in_flight for accumulate calculations
+
+    bool is_zero() {
+        return read_ios == 0 && write_ios == 0 &&
+               io_in_flight == 0 && io_ticks == 0 && io_in_queue == 0;
+    }
+
+    friend disk_stats operator- (disk_stats curr, const disk_stats& prev) {
+        curr.read_ios -= prev.read_ios;
+        curr.read_merges -= prev.read_merges;
+        curr.read_sectors -= prev.read_sectors;
+        curr.read_ticks -= prev.read_ticks;
+        curr.write_ios -= prev.write_ios;
+        curr.write_merges -= prev.write_merges;
+        curr.write_sectors -= prev.write_sectors;
+        curr.write_ticks -= prev.write_ticks;
+        /* skips io_in_flight, use current value */
+        curr.io_ticks -= prev.io_ticks;
+        curr.io_in_queue -= prev.io_in_queue;
+        return curr;
+    }
+
+    friend bool operator== (const disk_stats& a, const disk_stats& b) {
+        return a.read_ios == b.read_ios &&
+               a.read_merges == b.read_merges &&
+               a.read_sectors == b.read_sectors &&
+               a.read_ticks == b.read_ticks &&
+               a.write_ios == b.write_ios &&
+               a.write_merges == b.write_merges &&
+               a.write_sectors == b.write_sectors &&
+               a.write_ticks == b.write_ticks &&
+               /* skips io_in_flight */
+               a.io_ticks == b.io_ticks &&
+               a.io_in_queue == b.io_in_queue;
+    }
+
+    disk_stats& operator+= (const disk_stats& stats) {
+        read_ios += stats.read_ios;
+        read_merges += stats.read_merges;
+        read_sectors += stats.read_sectors;
+        read_ticks += stats.read_ticks;
+        write_ios += stats.write_ios;
+        write_merges += stats.write_merges;
+        write_sectors += stats.write_sectors;
+        write_ticks += stats.write_ticks;
+        /* skips io_in_flight, use current value */
+        io_ticks += stats.io_ticks;
+        io_in_queue += stats.io_in_queue;
+        return *this;
+    }
+};
+
+struct disk_perf {
+    uint32_t read_perf;         // read speed (kbytes/s)
+    uint32_t read_ios;          // read I/Os per second
+    uint32_t write_perf;        // write speed (kbytes/s)
+    uint32_t write_ios;         // write I/Os per second
+    uint32_t queue;             // I/Os in queue
+    bool is_zero() {
+        return read_perf == 0 && read_ios == 0 &&
+               write_perf == 0 && write_ios == 0 && queue == 0;
+    }
+};
+
+class stream_stats {
+private:
+    double mSum;
+    double mSquareSum;
+    uint32_t mCnt;
+public:
+    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+    ~stream_stats() {};
+    double get_mean() {
+        return mSum / mCnt;
+    }
+    double get_std() {
+        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+    }
+    void add(uint32_t num) {
+        mSum += (double)num;
+        mSquareSum += (double)num * (double)num;
+        mCnt++;
+    }
+    void evict(uint32_t num) {
+        if (mSum < num || mSquareSum < (double)num * (double)num) return;
+        mSum -= (double)num;
+        mSquareSum -= (double)num * (double)num;
+        mCnt--;
+    }
+};
+
+class disk_stats_monitor {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_monitor);
+    const char* const DISK_STATS_PATH;
+    struct disk_stats mPrevious;
+    struct disk_stats mAccumulate;      /* reset after stall */
+    struct disk_stats mAccumulate_pub;  /* reset after publish */
+    bool mStall;
+    std::queue<struct disk_perf> mBuffer;
+    struct {
+        stream_stats read_perf;           // read speed (bytes/s)
+        stream_stats read_ios;            // read I/Os per second
+        stream_stats write_perf;          // write speed (bytes/s)
+        stream_stats write_ios;           // write I/O per second
+        stream_stats queue;               // I/Os in queue
+    } mStats;
+    bool mValid;
+    const uint32_t mWindow;
+    const double mSigma;
+    struct disk_perf mMean;
+    struct disk_perf mStd;
+    android::sp<android::hardware::health::V2_0::IHealth> mHealth;
+
+    void update_mean();
+    void update_std();
+    void add(struct disk_perf* perf);
+    void evict(struct disk_perf* perf);
+    bool detect(struct disk_perf* perf);
+
+    void update(struct disk_stats* stats);
+
+public:
+  disk_stats_monitor(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
+                     uint32_t window_size = 5, double sigma = 1.0)
+      : DISK_STATS_PATH(
+            healthService != nullptr
+                ? nullptr
+                : (access(MMC_DISK_STATS_PATH, R_OK) == 0
+                       ? MMC_DISK_STATS_PATH
+                       : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH : nullptr))),
+        mPrevious(),
+        mAccumulate(),
+        mAccumulate_pub(),
+        mStall(false),
+        mValid(false),
+        mWindow(window_size),
+        mSigma(sigma),
+        mMean(),
+        mStd(),
+        mHealth(healthService) {}
+  bool enabled() { return mHealth != nullptr || DISK_STATS_PATH != nullptr; }
+  void update(void);
+  void publish(void);
+};
+
+#endif /* _STORAGED_DISKSTATS_H_ */
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 7d04c7a..88a53de 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -19,13 +19,24 @@
 
 #include <string.h>
 
+#include <chrono>
+
+#include <android/hardware/health/2.0/IHealth.h>
+#include <utils/Mutex.h>
+
+#include "storaged.h"
+#include "storaged.pb.h"
+
 #define FRIEND_TEST(test_case_name, test_name) \
 friend class test_case_name##_##test_name##_Test
 
 using namespace std;
+using namespace android;
+using namespace chrono;
+using namespace storaged_proto;
 
 class storage_info_t {
-protected:
+  protected:
     FRIEND_TEST(storaged_test, storage_info_t);
     // emmc lifetime
     uint16_t eol;                   // pre-eol (end of life) information
@@ -36,16 +47,38 @@
     const string userdata_path = "/data";
     uint64_t userdata_total_kb;
     uint64_t userdata_free_kb;
+    // io perf history
+    time_point<system_clock> day_start_tp;
+    vector<uint32_t> recent_perf;
+    uint32_t nr_samples;
+    vector<uint32_t> daily_perf;
+    uint32_t nr_days;
+    vector<uint32_t> weekly_perf;
+    uint32_t nr_weeks;
+    Mutex si_mutex;
 
     storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
-        userdata_total_kb(0), userdata_free_kb(0) {}
+        userdata_total_kb(0), userdata_free_kb(0), nr_samples(0),
+        daily_perf(WEEK_TO_DAYS, 0), nr_days(0),
+        weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) {
+            day_start_tp = system_clock::now();
+            day_start_tp -= chrono::seconds(duration_cast<chrono::seconds>(
+                day_start_tp.time_since_epoch()).count() % DAY_TO_SEC);
+    }
     void publish();
     storage_info_t* s_info;
-public:
-    static storage_info_t* get_storage_info();
-    virtual ~storage_info_t() {}
+
+  public:
+    static storage_info_t* get_storage_info(
+        const sp<android::hardware::health::V2_0::IHealth>& healthService);
+    virtual ~storage_info_t() {};
     virtual void report() {};
-    void refresh();
+    void load_perf_history_proto(const IOPerfHistory& perf_history);
+    void refresh(IOPerfHistory* perf_history);
+    void update_perf_history(uint32_t bw,
+                             const time_point<system_clock>& tp);
+    vector<int> get_perf_history();
+    uint32_t get_recent_perf();
 };
 
 class emmc_info_t : public storage_info_t {
@@ -69,4 +102,18 @@
     virtual void report();
 };
 
+class health_storage_info_t : public storage_info_t {
+  private:
+    using IHealth = hardware::health::V2_0::IHealth;
+    using StorageInfo = hardware::health::V2_0::StorageInfo;
+
+    sp<IHealth> mHealth;
+    void set_values_from_hal_storage_info(const StorageInfo& halInfo);
+
+  public:
+    health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
+    virtual ~health_storage_info_t() {}
+    virtual void report();
+};
+
 #endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index a8ddf4c..7ec6864 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -19,42 +19,38 @@
 
 #include <vector>
 
-#include <binder/IInterface.h>
-#include <binder/IBinder.h>
+#include <binder/BinderService.h>
 
-#include "storaged.h"
+#include "android/os/BnStoraged.h"
+#include "android/os/storaged/BnStoragedPrivate.h"
 
-using namespace android;
+using namespace std;
+using namespace android::os;
+using namespace android::os::storaged;
 
-// Interface
-class IStoraged : public IInterface {
+class StoragedService : public BinderService<StoragedService>, public BnStoraged {
+private:
+    void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
+    void dumpUidRecords(int fd, const vector<struct uid_record>& entries);
 public:
-    enum {
-        DUMPUIDS  = IBinder::FIRST_CALL_TRANSACTION,
-    };
-    // Request the service to run the test function
-    virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+    static status_t start();
+    static char const* getServiceName() { return "storaged"; }
+    virtual status_t dump(int fd, const Vector<String16> &args) override;
 
-    DECLARE_META_INTERFACE(Storaged);
+    binder::Status onUserStarted(int32_t userId);
+    binder::Status onUserStopped(int32_t userId);
+    binder::Status getRecentPerf(int32_t* _aidl_return);
 };
 
-// Client
-class BpStoraged : public BpInterface<IStoraged> {
+class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
 public:
-    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    static status_t start();
+    static char const* getServiceName() { return "storaged_pri"; }
+
+    binder::Status dumpUids(vector<UidInfo>* _aidl_return);
+    binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);
 };
 
-// Server
-class BnStoraged : public BnInterface<IStoraged> {
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-class Storaged : public BnStoraged {
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
-    virtual status_t dump(int fd, const Vector<String16>& args);
-};
-
-sp<IStoraged> get_storaged_service();
+sp<IStoragedPrivate> get_storaged_pri_service();
 
 #endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..3a718fa 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -23,88 +23,103 @@
 #include <unordered_map>
 #include <vector>
 
-enum uid_stat_t {
-    FOREGROUND = 0,
-    BACKGROUND = 1,
-    UID_STATS = 2
+#include <cutils/multiuser.h>
+#include <utils/Mutex.h>
+
+#include "storaged.pb.h"
+#include "uid_info.h"
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
+using namespace storaged_proto;
+using namespace android;
+using namespace android::os::storaged;
+
+class uid_info : public UidInfo {
+public:
+    bool parse_uid_io_stats(string&& s);
 };
 
-enum charger_stat_t {
-    CHARGER_OFF = 0,
-    CHARGER_ON = 1,
-    CHARGER_STATS = 2
-};
-
-enum io_type_t {
-    READ = 0,
-    WRITE = 1,
-    IO_TYPES = 2
-};
-
-struct uid_io_stats {
-    uint64_t rchar;                 // characters read
-    uint64_t wchar;                 // characters written
-    uint64_t read_bytes;            // bytes read (from storage layer)
-    uint64_t write_bytes;           // bytes written (to storage layer)
-    uint64_t fsync;                 // number of fsync syscalls
-};
-
-struct uid_info {
-    uint32_t uid;                   // user id
-    std::string name;               // package name
-    struct uid_io_stats io[UID_STATS];    // [0]:foreground [1]:background
+class io_usage {
+public:
+    io_usage() : bytes{{{0}}} {};
+    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    bool is_zero() const;
+    io_usage& operator+= (const io_usage& stats) {
+        for (int i = 0; i < IO_TYPES; i++) {
+            for (int j = 0; j < UID_STATS; j++) {
+                for (int k = 0; k < CHARGER_STATS; k++) {
+                    bytes[i][j][k] += stats.bytes[i][j][k];
+                }
+            }
+        }
+        return *this;
+    }
 };
 
 struct uid_io_usage {
-    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    userid_t user_id;
+    io_usage uid_ios;
+    // mapped from task comm to task io usage
+    map<string, io_usage> task_ios;
 };
 
 struct uid_record {
-    std::string name;
+    string name;
     struct uid_io_usage ios;
 };
 
 struct uid_records {
     uint64_t start_ts;
-    std::vector<struct uid_record> entries;
+    vector<struct uid_record> entries;
 };
 
 class uid_monitor {
 private:
+    FRIEND_TEST(storaged_test, uid_monitor);
     // last dump from /proc/uid_io/stats, uid -> uid_info
-    std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+    unordered_map<uint32_t, uid_info> last_uid_io_stats;
     // current io usage for next report, app name -> uid_io_usage
-    std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+    unordered_map<string, struct uid_io_usage> curr_io_stats;
     // io usage records, end timestamp -> {start timestamp, vector of records}
-    std::map<uint64_t, struct uid_records> records;
+    map<uint64_t, struct uid_records> io_history;
     // charger ON/OFF
     charger_stat_t charger_stat;
     // protects curr_io_stats, last_uid_io_stats, records and charger_stat
-    sem_t um_lock;
+    Mutex uidm_mutex;
     // start time for IO records
     uint64_t start_ts;
+    // true if UID_IO_STATS_PATH is accessible
+    const bool enable;
 
     // reads from /proc/uid_io/stats
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
     // flushes curr_io_stats to records
     void add_records_locked(uint64_t curr_ts);
     // updates curr_io_stats and set last_uid_io_stats
     void update_curr_io_stats_locked();
+    // writes io_history to protobuf
+    void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
 
 public:
     uid_monitor();
-    ~uid_monitor();
     // called by storaged main thread
     void init(charger_stat_t stat);
     // called by storaged -u
-    std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+    unordered_map<uint32_t, uid_info> get_uid_io_stats();
     // called by dumpsys
-    std::map<uint64_t, struct uid_records> dump(
+    map<uint64_t, struct uid_records> dump(
         double hours, uint64_t threshold, bool force_report);
     // called by battery properties listener
     void set_charger_state(charger_stat_t stat);
     // called by storaged periodic_chore or dump with force_report
-    void report();
+    bool enabled() { return enable; };
+    void report(unordered_map<int, StoragedProto>* protos);
+    // restores io_history from protobuf
+    void load_uid_io_proto(const UidIOUsage& proto);
+    void clear_user_history(userid_t user_id);
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..62cb12d 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -24,21 +24,20 @@
 
 #include "storaged.h"
 
+using namespace android::os::storaged;
+
 // Diskstats
 bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
 struct disk_perf get_disk_perf(struct disk_stats* stats);
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, struct disk_stats* inc);
 void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
-bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
 
 // UID I/O
-void sort_running_uids_info(std::vector<struct uid_info> &uids);
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries);
+void sort_running_uids_info(std::vector<UidInfo> &uids);
 
 // Logging
-void log_console_running_uids_info(std::vector<struct uid_info> uids);
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task);
+void log_console_perf_history(const vector<int>& perf_history);
 
-void log_debug_disk_perf(struct disk_perf* perf, const char* type);
-
-void log_event_disk_stats(struct disk_stats* stats, const char* type);
-void log_event_emmc_info(struct emmc_info* info_);
 #endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/include/uid_info.h b/storaged/include/uid_info.h
new file mode 100644
index 0000000..4398a0d
--- /dev/null
+++ b/storaged/include/uid_info.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef _UID_INFO_H_
+#define _UID_INFO_H_
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace storaged {
+
+enum uid_stat_t {
+    FOREGROUND = 0,
+    BACKGROUND = 1,
+    UID_STATS = 2
+};
+
+enum charger_stat_t {
+    CHARGER_OFF = 0,
+    CHARGER_ON = 1,
+    CHARGER_STATS = 2
+};
+
+enum io_type_t {
+    READ = 0,
+    WRITE = 1,
+    IO_TYPES = 2
+};
+
+struct io_stats {
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t fsync;                 // number of fsync syscalls
+};
+
+class task_info {
+public:
+    std::string comm;
+    pid_t pid;
+    io_stats io[UID_STATS];
+    bool parse_task_io_stats(std::string&& s);
+};
+
+class UidInfo : public Parcelable {
+public:
+    uint32_t uid;                     // user id
+    std::string name;                 // package name
+    io_stats io[UID_STATS];           // [0]:foreground [1]:background
+    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+
+    status_t writeToParcel(Parcel* parcel) const override;
+    status_t readFromParcel(const Parcel* parcel) override;
+};
+
+} // namespace storaged
+} // namespace os
+} // namespace android
+
+#endif /*  _UID_INFO_H_ */
\ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 6b82904..b3f1281 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -42,72 +42,81 @@
 #include <storaged_service.h>
 #include <storaged_utils.h>
 
-sp<storaged_t> storaged;
+using namespace std;
+using namespace android;
+
+sp<storaged_t> storaged_sp;
 
 // Function of storaged's main thread
 void* storaged_main(void* /* unused */) {
-    storaged = new storaged_t();
+    storaged_sp = new storaged_t();
 
-    storaged->init_battery_service();
-    storaged->report_storage_info();
+    storaged_sp->init();
+    storaged_sp->report_storage_info();
 
     LOG_TO(SYSTEM, INFO) << "storaged: Start";
 
     for (;;) {
-        storaged->event_checked();
-        storaged->pause();
+        storaged_sp->event_checked();
+        storaged_sp->pause();
     }
     return NULL;
 }
 
-static void help_message(void) {
+void help_message(void) {
     printf("usage: storaged [OPTION]\n");
     printf("  -u    --uid                   Dump uid I/O usage to stdout\n");
+    printf("  -t    --task                  Dump task I/O usage to stdout\n");
+    printf("  -p    --perf                  Dump I/O perf history to stdout\n");
     printf("  -s    --start                 Start storaged (default)\n");
     fflush(stdout);
 }
 
 int main(int argc, char** argv) {
-    int flag_main_service = 0;
-    int flag_dump_uid = 0;
+    bool flag_main_service = false;
+    bool flag_dump_uid = false;
+    bool flag_dump_task = false;
+    bool flag_dump_perf = false;
     int opt;
 
     for (;;) {
         int opt_idx = 0;
         static struct option long_options[] = {
-            {"start",       no_argument,        0, 's'},
-            {"kill",        no_argument,        0, 'k'},
-            {"uid",         no_argument,        0, 'u'},
-            {"help",        no_argument,        0, 'h'}
+            {"perf",        no_argument,    nullptr, 'p'},
+            {"start",       no_argument,    nullptr, 's'},
+            {"task",        no_argument,    nullptr, 't'},
+            {"uid",         no_argument,    nullptr, 'u'},
+            {nullptr,       0,              nullptr,  0}
         };
-        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        opt = getopt_long(argc, argv, ":pstu", long_options, &opt_idx);
         if (opt == -1) {
             break;
         }
 
         switch (opt) {
+        case 'p':
+            flag_dump_perf = true;
+            break;
         case 's':
-            flag_main_service = 1;
+            flag_main_service = true;
+            break;
+        case 't':
+            flag_dump_task = true;
             break;
         case 'u':
-            flag_dump_uid = 1;
+            flag_dump_uid = true;
             break;
-        case 'h':
+        default:
             help_message();
             return 0;
-        case '?':
-        default:
-            fprintf(stderr, "no supported option\n");
-            help_message();
-            return -1;
         }
     }
 
     if (argc == 1) {
-        flag_main_service = 1;
+        flag_main_service = true;
     }
 
-    if (flag_main_service && flag_dump_uid) {
+    if (flag_main_service && (flag_dump_uid || flag_dump_task)) {
         fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
         help_message();
         return -1;
@@ -122,7 +131,12 @@
             return -1;
         }
 
-        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        if (StoragedService::start() != android::OK ||
+            StoragedPrivateService::start() != android::OK) {
+            PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+            return -1;
+        }
+
         android::ProcessState::self()->startThreadPool();
         IPCThreadState::self()->joinThreadPool();
         pthread_join(storaged_main_thread, NULL);
@@ -130,23 +144,33 @@
         return 0;
     }
 
-    if (flag_dump_uid) {
-        sp<IStoraged> storaged_service = get_storaged_service();
-        if (storaged_service == NULL) {
-            fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
-            return -1;
-        }
-        std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+    sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();
+    if (storaged_service == NULL) {
+        fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+        return -1;
+    }
 
-        if (res.size() == 0) {
-            fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+    if (flag_dump_uid || flag_dump_task) {
+        vector<UidInfo> uid_io;
+        binder::Status status = storaged_service->dumpUids(&uid_io);
+        if (!status.isOk() || uid_io.size() == 0) {
+            fprintf(stderr, "UID I/O info is not available.\n");
             return 0;
         }
 
-        sort_running_uids_info(res);
-        log_console_running_uids_info(res);
+        sort_running_uids_info(uid_io);
+        log_console_running_uids_info(uid_io, flag_dump_task);
+    }
 
-        return 0;
+    if (flag_dump_perf) {
+        vector<int> perf_history;
+        binder::Status status = storaged_service->dumpPerfHistory(&perf_history);
+        if (!status.isOk() || perf_history.size() == 0) {
+            fprintf(stderr, "I/O perf history is not available.\n");
+            return 0;
+        }
+
+        log_console_perf_history(perf_history);
     }
 
     return 0;
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..bf8b448 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -16,184 +16,117 @@
 
 #define LOG_TAG "storaged"
 
+#include <dirent.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <time.h>
 #include <unistd.h>
+#include <zlib.h>
 
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
 #include <batteryservice/BatteryServiceConstants.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
+#include <healthhalutils/HealthHalUtils.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
 #include <log/log.h>
 
 #include <storaged.h>
 #include <storaged_utils.h>
 
-/* disk_stats_publisher */
-void disk_stats_publisher::publish(void) {
-    // Logging
-    struct disk_perf perf = get_disk_perf(&mAccumulate);
-    log_debug_disk_perf(&perf, "regular");
-    log_event_disk_stats(&mAccumulate, "regular");
-    // Reset global structures
-    memset(&mAccumulate, 0, sizeof(struct disk_stats));
-}
+using namespace android::base;
+using namespace chrono;
+using namespace google::protobuf::io;
+using namespace storaged_proto;
 
-void disk_stats_publisher::update(void) {
-    struct disk_stats curr;
-    if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
-        struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
-        add_disk_stats(&inc, &mAccumulate);
-#ifdef DEBUG
-//            log_kernel_disk_stats(&mPrevious, "prev stats");
-//            log_kernel_disk_stats(&curr, "curr stats");
-//            log_kernel_disk_stats(&inc, "inc stats");
-//            log_kernel_disk_stats(&mAccumulate, "accumulated stats");
-#endif
-        mPrevious = curr;
-    }
-}
+namespace {
 
-/* disk_stats_monitor */
-void disk_stats_monitor::update_mean() {
-    CHECK(mValid);
-    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
-    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
-    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
-    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
-    mMean.queue = (uint32_t)mStats.queue.get_mean();
-}
+/*
+ * The system user is the initial user that is implicitly created on first boot
+ * and hosts most of the system services. Keep this in sync with
+ * frameworks/base/core/java/android/os/UserManager.java
+ */
+constexpr int USER_SYSTEM = 0;
 
-void disk_stats_monitor::update_std() {
-    CHECK(mValid);
-    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
-    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
-    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
-    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
-    mStd.queue = (uint32_t)mStats.queue.get_std();
-}
+constexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB
 
-void disk_stats_monitor::add(struct disk_perf* perf) {
-    mStats.read_perf.add(perf->read_perf);
-    mStats.read_ios.add(perf->read_ios);
-    mStats.write_perf.add(perf->write_perf);
-    mStats.write_ios.add(perf->write_ios);
-    mStats.queue.add(perf->queue);
-}
+constexpr ssize_t min_benchmark_size = 128 * 1024;  // 128KB
 
-void disk_stats_monitor::evict(struct disk_perf* perf) {
-    mStats.read_perf.evict(perf->read_perf);
-    mStats.read_ios.evict(perf->read_ios);
-    mStats.write_perf.evict(perf->write_perf);
-    mStats.write_ios.evict(perf->write_ios);
-    mStats.queue.evict(perf->queue);
-}
+}  // namespace
 
-bool disk_stats_monitor::detect(struct disk_perf* perf) {
-    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
-            ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
-            ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
-}
+const uint32_t storaged_t::current_version = 4;
 
-void disk_stats_monitor::update(struct disk_stats* stats) {
-    struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
-    struct disk_perf perf = get_disk_perf(&inc);
-    // Update internal data structures
-    if (LIKELY(mValid)) {
-        CHECK_EQ(mBuffer.size(), mWindow);
+using android::hardware::interfacesEqual;
+using android::hardware::Return;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hidl::manager::V1_0::IServiceManager;
 
-        if (UNLIKELY(detect(&perf))) {
-            mStall = true;
-            add_disk_stats(&inc, &mAccumulate);
-            log_debug_disk_perf(&mMean, "stalled_mean");
-            log_debug_disk_perf(&mStd, "stalled_std");
-        } else {
-            if (mStall) {
-                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
-                log_debug_disk_perf(&acc_perf, "stalled");
-                log_event_disk_stats(&mAccumulate, "stalled");
-                mStall = false;
-                memset(&mAccumulate, 0, sizeof(mAccumulate));
-            }
-        }
 
-        evict(&mBuffer.front());
-        mBuffer.pop();
-        add(&perf);
-        mBuffer.push(perf);
-
-        update_mean();
-        update_std();
-
-    } else { /* mValid == false */
-        CHECK_LT(mBuffer.size(), mWindow);
-        add(&perf);
-        mBuffer.push(perf);
-        if (mBuffer.size() == mWindow) {
-            mValid = true;
-            update_mean();
-            update_std();
-        }
-    }
-
-    mPrevious = *stats;
-}
-
-void disk_stats_monitor::update(void) {
-    struct disk_stats curr;
-    if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
-        update(&curr);
-    }
-}
-
-static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
-    sp<IServiceManager> sm = defaultServiceManager();
-    if (sm == NULL) return NULL;
-
-    sp<IBinder> binder = sm->getService(String16("batteryproperties"));
-    if (binder == NULL) return NULL;
-
-    sp<IBatteryPropertiesRegistrar> battery_properties =
-        interface_cast<IBatteryPropertiesRegistrar>(binder);
-
-    return battery_properties;
-}
-
-static inline charger_stat_t is_charger_on(int64_t prop) {
-    return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+inline charger_stat_t is_charger_on(BatteryStatus prop) {
+    return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
         CHARGER_ON : CHARGER_OFF;
 }
 
-void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
-    mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
+    mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
+    return android::hardware::Void();
 }
 
-void storaged_t::init_battery_service() {
-    if (!mConfig.proc_uid_io_available)
+void storaged_t::init() {
+    init_health_service();
+    mDsm = std::make_unique<disk_stats_monitor>(health);
+    storage_info.reset(storage_info_t::get_storage_info(health));
+}
+
+void storaged_t::init_health_service() {
+    if (!mUidm.enabled())
         return;
 
-    battery_properties = get_battery_properties_service();
-    if (battery_properties == NULL) {
-        LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+    health = get_health_service();
+    if (health == NULL) {
+        LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
         return;
     }
 
-    struct BatteryProperty val;
-    battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
-    mUidm.init(is_charger_on(val.valueInt64));
+    BatteryStatus status = BatteryStatus::UNKNOWN;
+    auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
+        if (r != Result::SUCCESS) {
+            LOG_TO(SYSTEM, WARNING)
+                << "health: cannot get battery status " << toString(r);
+            return;
+        }
+        if (v == BatteryStatus::UNKNOWN) {
+            LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+        }
+        status = v;
+    });
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
+            << ret.description();
+    }
 
+    mUidm.init(is_charger_on(status));
     // register listener after init uid_monitor
-    battery_properties->registerListener(this);
-    IInterface::asBinder(battery_properties)->linkToDeath(this);
+    health->registerCallback(this);
+    health->linkToDeath(this, 0 /* cookie */);
 }
 
-void storaged_t::binderDied(const wp<IBinder>& who) {
-    if (battery_properties != NULL &&
-        IInterface::asBinder(battery_properties) == who) {
-        LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
-        IPCThreadState::self()->stopProcess();
+void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
+    if (health != NULL && interfacesEqual(health, who.promote())) {
+        LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+        android::hardware::IPCThreadState::self()->stopProcess();
         exit(1);
     } else {
         LOG_TO(SYSTEM, ERROR) << "unknown service died";
@@ -206,44 +139,195 @@
 
 /* storaged_t */
 storaged_t::storaged_t(void) {
-    if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
-        mConfig.diskstats_available = false;
-    } else {
-        mConfig.diskstats_available = true;
-    }
-
-    mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
-
     mConfig.periodic_chores_interval_unit =
-        property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+        property_get_int32("ro.storaged.event.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
 
     mConfig.event_time_check_usec =
         property_get_int32("ro.storaged.event.perf_check", 0);
 
     mConfig.periodic_chores_interval_disk_stats_publish =
-        property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+        property_get_int32("ro.storaged.disk_stats_pub",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
 
     mConfig.periodic_chores_interval_uid_io =
-        property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+        property_get_int32("ro.storaged.uid_io.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
 
-    storage_info.reset(storage_info_t::get_storage_info());
+    mConfig.periodic_chores_interval_flush_proto =
+        property_get_int32("ro.storaged.flush_proto.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
 
     mStarttime = time(NULL);
+    mTimer = 0;
 }
 
-void storaged_t::event(void) {
-    if (mConfig.diskstats_available) {
-        mDiskStats.update();
-        mDsm.update();
-        storage_info->refresh();
-        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
-            mDiskStats.publish();
+void storaged_t::add_user_ce(userid_t user_id) {
+    load_proto(user_id);
+    proto_loaded[user_id] = true;
+}
+
+void storaged_t::remove_user_ce(userid_t user_id) {
+    proto_loaded[user_id] = false;
+    mUidm.clear_user_history(user_id);
+    RemoveFileIfExists(proto_path(user_id), nullptr);
+}
+
+void storaged_t::load_proto(userid_t user_id) {
+    string proto_file = proto_path(user_id);
+    ifstream in(proto_file, ofstream::in | ofstream::binary);
+
+    if (!in.good()) return;
+
+    stringstream ss;
+    ss << in.rdbuf();
+    StoragedProto proto;
+    proto.ParseFromString(ss.str());
+
+    const UidIOUsage& uid_io_usage = proto.uid_io_usage();
+    uint32_t computed_crc = crc32(current_version,
+        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+        uid_io_usage.ByteSize());
+    if (proto.crc() != computed_crc) {
+        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+        return;
+    }
+
+    mUidm.load_uid_io_proto(proto.uid_io_usage());
+
+    if (user_id == USER_SYSTEM) {
+        storage_info->load_perf_history_proto(proto.perf_history());
+    }
+}
+
+char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
+    proto->set_version(current_version);
+
+    const UidIOUsage& uid_io_usage = proto->uid_io_usage();
+    proto->set_crc(crc32(current_version,
+        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+        uid_io_usage.ByteSize()));
+
+    uint32_t pagesize = sysconf(_SC_PAGESIZE);
+    if (user_id == USER_SYSTEM) {
+        proto->set_padding("", 1);
+        vector<char> padding;
+        ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
+                                pagesize);
+        padding = vector<char>(size - proto->ByteSize(), 0xFD);
+        proto->set_padding(padding.data(), padding.size());
+        while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
+            padding.push_back(0xFD);
+            proto->set_padding(padding.data(), padding.size());
         }
     }
 
-    if (mConfig.proc_uid_io_available && mTimer &&
-            (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
-         mUidm.report();
+    char* data = nullptr;
+    if (posix_memalign(reinterpret_cast<void**>(&data),
+                       pagesize, proto->ByteSize())) {
+        PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
+                               << proto->ByteSize() << ")";
+        return data;
+    }
+
+    proto->SerializeToArray(data, proto->ByteSize());
+    return data;
+}
+
+void storaged_t::flush_proto_data(userid_t user_id,
+                                  const char* data, ssize_t size) {
+    string proto_file = proto_path(user_id);
+    string tmp_file = proto_file + "_tmp";
+    unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
+                 O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
+                    (user_id == USER_SYSTEM ? O_DIRECT : 0),
+                 S_IRUSR | S_IWUSR)));
+    if (fd == -1) {
+        PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+        return;
+    }
+
+    if (user_id == USER_SYSTEM) {
+        time_point<steady_clock> start, end;
+        uint32_t benchmark_size = 0;
+        uint64_t benchmark_time_ns = 0;
+        ssize_t ret;
+        bool first_write = true;
+
+        while (size > 0) {
+            start = steady_clock::now();
+            ret = write(fd, data, MIN(benchmark_unit_size, size));
+            if (ret <= 0) {
+                PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+                return;
+            }
+            end = steady_clock::now();
+            /*
+            * compute bandwidth after the first write and if write returns
+            * exactly unit size.
+            */
+            if (!first_write && ret == benchmark_unit_size) {
+                benchmark_size += benchmark_unit_size;
+                benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
+            }
+            size -= ret;
+            data += ret;
+            first_write = false;
+        }
+
+        if (benchmark_size) {
+            int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
+            storage_info->update_perf_history(perf, system_clock::now());
+        }
+    } else {
+        if (!WriteFully(fd, data, size)) {
+            PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+            return;
+        }
+    }
+
+    fd.reset(-1);
+    rename(tmp_file.c_str(), proto_file.c_str());
+}
+
+void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
+    unique_ptr<char> proto_data(prepare_proto(user_id, proto));
+    if (proto_data == nullptr) return;
+
+    flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
+}
+
+void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
+    for (auto& it : *protos) {
+        /*
+         * Don't flush proto if we haven't attempted to load it from file.
+         */
+        if (proto_loaded[it.first]) {
+            flush_proto(it.first, &it.second);
+        }
+    }
+}
+
+void storaged_t::event(void) {
+    unordered_map<int, StoragedProto> protos;
+
+    if (mDsm->enabled()) {
+        mDsm->update();
+        if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
+            mDsm->publish();
+        }
+    }
+
+    if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
+        mUidm.report(&protos);
+    }
+
+    if (storage_info) {
+        storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
+    }
+
+    if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
+        flush_protos(&protos);
     }
 
     mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..2000c0b
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,60 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package storaged_proto;
+option java_package = "com.android.storaged.proto";
+option java_outer_classname = "Storaged";
+
+message IOUsage {
+  optional uint64 rd_fg_chg_on  = 1;
+  optional uint64 rd_fg_chg_off = 2;
+  optional uint64 rd_bg_chg_on  = 3;
+  optional uint64 rd_bg_chg_off = 4;
+  optional uint64 wr_fg_chg_on  = 5;
+  optional uint64 wr_fg_chg_off = 6;
+  optional uint64 wr_bg_chg_on  = 7;
+  optional uint64 wr_bg_chg_off = 8;
+}
+
+message TaskIOUsage {
+  optional string task_name = 1;
+  optional IOUsage ios = 2;
+}
+
+message UidRecord {
+  optional string uid_name = 1;
+  optional uint32 user_id = 2;
+  optional IOUsage uid_io = 3;
+  repeated TaskIOUsage task_io = 4;
+}
+
+message UidIORecords {
+  optional uint64 start_ts = 1;
+  repeated UidRecord entries = 2;
+}
+
+message UidIOItem {
+  optional uint64 end_ts = 1;
+  optional UidIORecords records = 2;
+}
+
+message UidIOUsage {
+  repeated UidIOItem uid_io_items = 2;
+}
+
+message IOPerfHistory {
+  optional uint64 day_start_sec = 1;
+  repeated uint32 recent_perf = 2;
+  optional uint32 nr_samples = 3;
+  repeated uint32 daily_perf = 4;
+  optional uint32 nr_days = 5;
+  repeated uint32 weekly_perf = 6;
+  optional uint32 nr_weeks = 7;
+}
+
+message StoragedProto {
+  optional uint32 crc = 1;
+  optional uint32 version = 2;
+  optional UidIOUsage uid_io_usage = 3;
+  optional IOPerfHistory perf_history = 4;
+  optional bytes padding = 5;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index a24c7fb..1840d05 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -4,4 +4,4 @@
     file /d/mmc0/mmc0:0001/ext_csd r
     writepid /dev/cpuset/system-background/tasks
     user root
-    group package_info
\ No newline at end of file
+    group package_info
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
new file mode 100644
index 0000000..1050033
--- /dev/null
+++ b/storaged/storaged_diskstats.cpp
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2017 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 "storaged"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+#include "storaged_diskstats.h"
+
+namespace {
+
+using android::sp;
+using android::hardware::health::V2_0::DiskStats;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::toString;
+
+#ifdef DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+    // skip if the input structure are all zeros
+    if (perf == NULL || perf->is_zero()) return;
+
+    LOG_TO(SYSTEM, INFO) << "disk_perf " << type
+              << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+              << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
+              << " q: " << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL || stats->is_zero()) return;
+
+    android_log_event_list(EVENTLOGTAG_DISKSTATS)
+        << type << stats->start_time << stats->end_time
+        << stats->read_ios << stats->read_merges
+        << stats->read_sectors << stats->read_ticks
+        << stats->write_ios << stats->write_merges
+        << stats->write_sectors << stats->write_ticks
+        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+        << LOG_ID_EVENTS;
+}
+
+} // namespace
+
+bool get_time(struct timespec* ts) {
+    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+    // when system is running.
+    int ret = clock_gettime(CLOCK_MONOTONIC, ts);
+    if (ret < 0) {
+        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+        return false;
+    }
+    return true;
+}
+
+void init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {
+    stats->start_time = 0;
+    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+    stats->counter = 1;
+    stats->io_avg = (double)stats->io_in_flight;
+}
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+    // Get time
+    struct timespec ts;
+    if (!get_time(&ts)) {
+        return false;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+        return false;
+    }
+
+    // Regular diskstats entries
+    std::stringstream ss(buffer);
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        ss >> *((uint64_t*)stats + i);
+    }
+    // Other entries
+    init_disk_stats_other(ts, stats);
+    return true;
+}
+
+void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
+    dst->read_ios = src.reads;
+    dst->read_merges = src.readMerges;
+    dst->read_sectors = src.readSectors;
+    dst->read_ticks = src.readTicks;
+    dst->write_ios = src.writes;
+    dst->write_merges = src.writeMerges;
+    dst->write_sectors = src.writeSectors;
+    dst->write_ticks = src.writeTicks;
+    dst->io_in_flight = src.ioInFlight;
+    dst->io_ticks = src.ioTicks;
+    dst->io_in_queue = src.ioInQueue;
+}
+
+bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
+    struct timespec ts;
+    if (!get_time(&ts)) {
+        return false;
+    }
+
+    bool success = false;
+    auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result != Result::SUCCESS || halStats.size() == 0) {
+            LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
+                                  << " and size " << halStats.size();
+            return;
+        }
+
+        convert_hal_disk_stats(stats, halStats[0]);
+        success = true;
+    });
+
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+        return false;
+    }
+
+    if (!success) {
+        return false;
+    }
+
+    init_disk_stats_other(ts, stats);
+    return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats)
+{
+    struct disk_perf perf = {};
+
+    if (stats->io_ticks) {
+        if (stats->read_ticks) {
+            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+                              stats->read_sectors * stats->io_in_queue +
+                              (divisor >> 1)) / divisor;
+            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+                             stats->read_ios * stats->io_in_queue +
+                             (divisor >> 1)) / divisor;
+        }
+        if (stats->write_ticks) {
+            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+            perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+                               stats->write_sectors * stats->io_in_queue +
+                               (divisor >> 1)) / divisor;
+            perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+                              stats->write_ios * stats->io_in_queue +
+                              (divisor >> 1)) / divisor;
+        }
+        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+                     stats->io_ticks;
+    }
+    return perf;
+}
+
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,
+                        struct disk_stats* inc)
+{
+    *inc = *curr - *prev;
+    inc->start_time = prev->end_time;
+    inc->end_time = curr->end_time;
+    inc->io_avg = curr->io_avg;
+    inc->counter = 1;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
+{
+    if (dst->end_time != 0 && dst->end_time != src->start_time) {
+        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+            << " are added. dst end with " << dst->end_time
+            << ", src start with " << src->start_time;
+    }
+
+    *dst += *src;
+
+    dst->io_in_flight = src->io_in_flight;
+    if (dst->counter + src->counter) {
+        dst->io_avg =
+            ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+            (dst->counter + src->counter);
+    }
+    dst->counter += src->counter;
+    dst->end_time = src->end_time;
+    if (dst->start_time == 0) {
+        dst->start_time = src->start_time;
+    }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean()
+{
+    CHECK(mValid);
+    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+    mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std()
+{
+    CHECK(mValid);
+    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+    mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf)
+{
+    mStats.read_perf.add(perf->read_perf);
+    mStats.read_ios.add(perf->read_ios);
+    mStats.write_perf.add(perf->write_perf);
+    mStats.write_ios.add(perf->write_ios);
+    mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+    mStats.read_perf.evict(perf->read_perf);
+    mStats.read_ios.evict(perf->read_ios);
+    mStats.write_perf.evict(perf->write_perf);
+    mStats.write_ios.evict(perf->write_ios);
+    mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf)
+{
+    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+        ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+        ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* curr)
+{
+    disk_stats inc;
+    get_inc_disk_stats(&mPrevious, curr, &inc);
+    add_disk_stats(&inc, &mAccumulate_pub);
+
+    struct disk_perf perf = get_disk_perf(&inc);
+    log_debug_disk_perf(&perf, "regular");
+
+    add(&perf);
+    mBuffer.push(perf);
+    if (mBuffer.size() > mWindow) {
+        evict(&mBuffer.front());
+        mBuffer.pop();
+        mValid = true;
+    }
+
+    // Update internal data structures
+    if (LIKELY(mValid)) {
+        CHECK_EQ(mBuffer.size(), mWindow);
+        update_mean();
+        update_std();
+        if (UNLIKELY(detect(&perf))) {
+            mStall = true;
+            add_disk_stats(&inc, &mAccumulate);
+            log_debug_disk_perf(&mMean, "stalled_mean");
+            log_debug_disk_perf(&mStd, "stalled_std");
+        } else {
+            if (mStall) {
+                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+                log_debug_disk_perf(&acc_perf, "stalled");
+                log_event_disk_stats(&mAccumulate, "stalled");
+                mStall = false;
+                memset(&mAccumulate, 0, sizeof(mAccumulate));
+            }
+        }
+    }
+
+    mPrevious = *curr;
+}
+
+void disk_stats_monitor::update() {
+    disk_stats curr;
+    if (mHealth != nullptr) {
+        if (!get_disk_stats_from_health_hal(mHealth, &curr)) {
+            return;
+        }
+    } else {
+        if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
+            return;
+        }
+    }
+
+    update(&curr);
+}
+
+void disk_stats_monitor::publish(void)
+{
+    struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
+    log_debug_disk_perf(&perf, "regular");
+    log_event_disk_stats(&mAccumulate, "regular");
+    // Reset global structures
+    memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
+}
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index b5fb13e..055f375 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/statvfs.h>
 
+#include <numeric>
+
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/logging.h>
@@ -27,9 +29,16 @@
 #include <log/log_event_list.h>
 
 #include "storaged.h"
+#include "storaged_info.h"
 
 using namespace std;
+using namespace chrono;
 using namespace android::base;
+using namespace storaged_proto;
+
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
 const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
@@ -39,14 +48,20 @@
 
 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
 
-static bool FileExists(const std::string& filename)
+namespace {
+
+bool FileExists(const std::string& filename)
 {
   struct stat buffer;
   return stat(filename.c_str(), &buffer) == 0;
 }
 
-storage_info_t* storage_info_t::get_storage_info()
-{
+} // namespace
+
+storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
+    if (healthService != nullptr) {
+        return new health_storage_info_t(healthService);
+    }
     if (FileExists(emmc_info_t::emmc_sysfs) ||
         FileExists(emmc_info_t::emmc_debugfs)) {
         return new emmc_info_t;
@@ -57,7 +72,39 @@
     return new storage_info_t;
 }
 
-void storage_info_t::refresh()
+void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
+{
+    Mutex::Autolock _l(si_mutex);
+
+    if (!perf_history.has_day_start_sec() ||
+        perf_history.daily_perf_size() > (int)daily_perf.size() ||
+        perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
+        LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+        return;
+    }
+
+    day_start_tp = {};
+    day_start_tp += chrono::seconds(perf_history.day_start_sec());
+
+    nr_samples = perf_history.nr_samples();
+    for (auto bw : perf_history.recent_perf()) {
+        recent_perf.push_back(bw);
+    }
+
+    nr_days = perf_history.nr_days();
+    int i = 0;
+    for (auto bw : perf_history.daily_perf()) {
+        daily_perf[i++] = bw;
+    }
+
+    nr_weeks = perf_history.nr_weeks();
+    i = 0;
+    for (auto bw : perf_history.weekly_perf()) {
+        weekly_perf[i++] = bw;
+    }
+}
+
+void storage_info_t::refresh(IOPerfHistory* perf_history)
 {
     struct statvfs buf;
     if (statvfs(userdata_path.c_str(), &buf) != 0) {
@@ -67,6 +114,24 @@
 
     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
+
+    Mutex::Autolock _l(si_mutex);
+
+    perf_history->Clear();
+    perf_history->set_day_start_sec(
+        duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
+    for (const uint32_t& bw : recent_perf) {
+        perf_history->add_recent_perf(bw);
+    }
+    perf_history->set_nr_samples(nr_samples);
+    for (const uint32_t& bw : daily_perf) {
+        perf_history->add_daily_perf(bw);
+    }
+    perf_history->set_nr_days(nr_days);
+    for (const uint32_t& bw : weekly_perf) {
+        perf_history->add_weekly_perf(bw);
+    }
+    perf_history->set_nr_weeks(nr_weeks);
 }
 
 void storage_info_t::publish()
@@ -76,6 +141,91 @@
         << LOG_ID_EVENTS;
 }
 
+void storage_info_t::update_perf_history(uint32_t bw,
+                                         const time_point<system_clock>& tp)
+{
+    Mutex::Autolock _l(si_mutex);
+
+    if (tp > day_start_tp &&
+        duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
+        if (nr_samples >= recent_perf.size()) {
+            recent_perf.push_back(bw);
+        } else {
+            recent_perf[nr_samples] = bw;
+        }
+        nr_samples++;
+        return;
+    }
+
+    recent_perf.erase(recent_perf.begin() + nr_samples,
+                      recent_perf.end());
+
+    uint32_t daily_avg_bw = accumulate(recent_perf.begin(),
+        recent_perf.begin() + nr_samples, 0) / nr_samples;
+
+    day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
+        tp.time_since_epoch()).count() % DAY_TO_SEC);
+
+    nr_samples = 0;
+    if (recent_perf.empty())
+        recent_perf.resize(1);
+    recent_perf[nr_samples++] = bw;
+
+    if (nr_days < WEEK_TO_DAYS) {
+        daily_perf[nr_days++] = daily_avg_bw;
+        return;
+    }
+
+    uint32_t week_avg_bw = accumulate(daily_perf.begin(),
+        daily_perf.begin() + nr_days, 0) / nr_days;
+
+    nr_days = 0;
+    daily_perf[nr_days++] = daily_avg_bw;
+
+    if (nr_weeks >= YEAR_TO_WEEKS) {
+        nr_weeks = 0;
+    }
+    weekly_perf[nr_weeks++] = week_avg_bw;
+}
+
+vector<int> storage_info_t::get_perf_history()
+{
+    Mutex::Autolock _l(si_mutex);
+
+    vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
+
+    ret[0] = recent_perf.size();
+    ret[1] = daily_perf.size();
+    ret[2] = weekly_perf.size();
+
+    int start = 3;
+    for (size_t i = 0; i < recent_perf.size(); i++) {
+        int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
+        ret[start + i] = recent_perf[idx];
+    }
+
+    start += recent_perf.size();
+    for (size_t i = 0; i < daily_perf.size(); i++) {
+        int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
+        ret[start + i] = daily_perf[idx];
+    }
+
+    start += daily_perf.size();
+    for (size_t i = 0; i < weekly_perf.size(); i++) {
+        int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
+        ret[start + i] = weekly_perf[idx];
+    }
+
+    return ret;
+}
+
+uint32_t storage_info_t::get_recent_perf() {
+    Mutex::Autolock _l(si_mutex);
+    if (recent_perf.size() == 0) return 0;
+    return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
+           recent_perf.size();
+}
+
 void emmc_info_t::report()
 {
     if (!report_sysfs() && !report_debugfs())
@@ -121,6 +271,8 @@
     return true;
 }
 
+namespace {
+
 const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
 /* 2 characters in string for each byte */
 const size_t EXT_CSD_REV_IDX = 192 * 2;
@@ -128,6 +280,8 @@
 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
 
+} // namespace
+
 bool emmc_info_t::report_debugfs()
 {
     string buffer;
@@ -210,3 +364,25 @@
     publish();
 }
 
+void health_storage_info_t::report() {
+    auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result != Result::SUCCESS || halInfos.size() == 0) {
+            LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+                                  << " and size " << halInfos.size();
+            return;
+        }
+        set_values_from_hal_storage_info(halInfos[0]);
+        publish();
+    });
+
+    if (!ret.isOk()) {
+        LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+    }
+}
+
+void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
+    eol = halInfo.eol;
+    lifetime_a = halInfo.lifetimeA;
+    lifetime_b = halInfo.lifetimeB;
+    version = halInfo.version;
+}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..17ea25b 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <inttypes.h>
 #include <stdint.h>
 
 #include <vector>
@@ -29,60 +30,69 @@
 #include <private/android_filesystem_config.h>
 
 #include <storaged.h>
+#include <storaged_utils.h>
 #include <storaged_service.h>
 
+using namespace std;
 using namespace android::base;
 
-extern sp<storaged_t> storaged;
+extern sp<storaged_t> storaged_sp;
 
-std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
-    Parcel data, reply;
-    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
-
-    remote()->transact(DUMPUIDS, data, &reply);
-
-    uint32_t res_size = reply.readInt32();
-    std::vector<struct uid_info> res(res_size);
-    for (auto&& uid : res) {
-        uid.uid = reply.readInt32();
-        uid.name = reply.readCString();
-        reply.read(&uid.io, sizeof(uid.io));
-    }
-    return res;
+status_t StoragedService::start() {
+    return BinderService<StoragedService>::publish();
 }
-IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
 
-status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
-    switch(code) {
-        case DUMPUIDS: {
-                if (!data.checkInterface(this))
-                    return BAD_TYPE;
-                std::vector<struct uid_info> res = dump_uids(NULL);
-                reply->writeInt32(res.size());
-                for (auto uid : res) {
-                    reply->writeInt32(uid.uid);
-                    reply->writeCString(uid.name.c_str());
-                    reply->write(&uid.io, sizeof(uid.io));
-                }
-                return NO_ERROR;
-            }
-            break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
+void StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries = merge_io_usage(entries);
+    for (const auto& rec : merged_entries) {
+        dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                rec.first.c_str(),
+                rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                rec.second.bytes[READ][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                rec.second.bytes[READ][BACKGROUND][CHARGER_ON],
+                rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);
     }
 }
 
-std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
-    std::vector<struct uid_info> uids_v;
-    std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
+void StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {
+    for (const auto& record : entries) {
+        const io_usage& uid_usage = record.ios.uid_ios;
+        dprintf(fd, "%s_%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                record.name.c_str(), record.ios.user_id,
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
 
-    for (const auto& it : uids_m) {
-        uids_v.push_back(it.second);
+        for (const auto& task_it : record.ios.task_ios) {
+            const io_usage& task_usage = task_it.second;
+            const string& comm = task_it.first;
+            dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                    " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                    comm.c_str(),
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+                    task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+                    task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        }
     }
-    return uids_v;
 }
 
-status_t Storaged::dump(int fd, const Vector<String16>& args) {
+status_t StoragedService::dump(int fd, const Vector<String16>& args) {
     IPCThreadState* self = IPCThreadState::self();
     const int pid = self->getCallingPid();
     const int uid = self->getCallingUid();
@@ -96,6 +106,7 @@
     int time_window = 0;
     uint64_t threshold = 0;
     bool force_report = false;
+    bool debug = false;
     for (size_t i = 0; i < args.size(); i++) {
         const auto& arg = args[i];
         if (arg == String16("--hours")) {
@@ -123,47 +134,87 @@
             force_report = true;
             continue;
         }
+        if (arg == String16("--debug")) {
+            debug = true;
+            continue;
+        }
     }
 
     uint64_t last_ts = 0;
-    const std::map<uint64_t, struct uid_records>& records =
-                storaged->get_uid_records(hours, threshold, force_report);
+    map<uint64_t, struct uid_records> records =
+                storaged_sp->get_uid_records(hours, threshold, force_report);
     for (const auto& it : records) {
         if (last_ts != it.second.start_ts) {
-            dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
+            dprintf(fd, "%" PRIu64, it.second.start_ts);
         }
-        dprintf(fd, ",%llu\n", (unsigned long long)it.first);
+        dprintf(fd, ",%" PRIu64 "\n", it.first);
         last_ts = it.first;
 
-        for (const auto& record : it.second.entries) {
-            dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
-                record.name.c_str(),
-                record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
-                record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
-                record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
-                record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
-                record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
-                record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
-                record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
-                record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+        if (!debug) {
+            dumpUidRecords(fd, it.second.entries);
+        } else {
+            dumpUidRecordsDebug(fd, it.second.entries);
         }
     }
 
     if (time_window) {
-        storaged->update_uid_io_interval(time_window);
+        storaged_sp->update_uid_io_interval(time_window);
     }
 
     return NO_ERROR;
 }
 
-sp<IStoraged> get_storaged_service() {
+binder::Status StoragedService::onUserStarted(int32_t userId) {
+    storaged_sp->add_user_ce(userId);
+    return binder::Status::ok();
+}
+
+binder::Status StoragedService::onUserStopped(int32_t userId) {
+    storaged_sp->remove_user_ce(userId);
+    return binder::Status::ok();
+}
+
+binder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {
+    uint32_t recent_perf = storaged_sp->get_recent_perf();
+    if (recent_perf > INT32_MAX) {
+        *_aidl_return = INT32_MAX;
+    } else {
+        *_aidl_return = static_cast<int32_t>(recent_perf);
+    }
+    return binder::Status::ok();
+}
+
+status_t StoragedPrivateService::start() {
+    return BinderService<StoragedPrivateService>::publish();
+}
+
+binder::Status StoragedPrivateService::dumpUids(
+        vector<::android::os::storaged::UidInfo>* _aidl_return) {
+    unordered_map<uint32_t, uid_info> uids_m = storaged_sp->get_uids();
+
+    for (const auto& it : uids_m) {
+        UidInfo uinfo;
+        uinfo.uid = it.second.uid;
+        uinfo.name = it.second.name;
+        uinfo.tasks = it.second.tasks;
+        memcpy(&uinfo.io, &it.second.io, sizeof(uinfo.io));
+        _aidl_return->push_back(uinfo);
+    }
+    return binder::Status::ok();
+}
+
+binder::Status StoragedPrivateService::dumpPerfHistory(
+        vector<int32_t>* _aidl_return) {
+    *_aidl_return = storaged_sp->get_perf_history();
+    return binder::Status::ok();
+}
+
+sp<IStoragedPrivate> get_storaged_pri_service() {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) return NULL;
 
-    sp<IBinder> binder = sm->getService(String16("storaged"));
+    sp<IBinder> binder = sm->getService(String16("storaged_pri"));
     if (binder == NULL) return NULL;
 
-    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
-
-    return storaged;
+    return interface_cast<IStoragedPrivate>(binder);
 }
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index dd8bdd6..5745782 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -38,16 +38,87 @@
 using namespace android;
 using namespace android::base;
 using namespace android::content::pm;
+using namespace android::os::storaged;
+using namespace storaged_proto;
 
-static bool refresh_uid_names;
+namespace {
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+bool refresh_uid_names;
+const char* UID_IO_STATS_PATH = "/proc/uid_io/stats";
+
+} // namepsace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
     return get_uid_io_stats_locked();
 };
 
-static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
+/* return true on parse success and false on failure */
+bool uid_info::parse_uid_io_stats(std::string&& s)
+{
+    std::vector<std::string> fields = Split(s, " ");
+    if (fields.size() < 11 ||
+        !ParseUint(fields[0],  &uid) ||
+        !ParseUint(fields[1],  &io[FOREGROUND].rchar) ||
+        !ParseUint(fields[2],  &io[FOREGROUND].wchar) ||
+        !ParseUint(fields[3],  &io[FOREGROUND].read_bytes) ||
+        !ParseUint(fields[4],  &io[FOREGROUND].write_bytes) ||
+        !ParseUint(fields[5],  &io[BACKGROUND].rchar) ||
+        !ParseUint(fields[6],  &io[BACKGROUND].wchar) ||
+        !ParseUint(fields[7],  &io[BACKGROUND].read_bytes) ||
+        !ParseUint(fields[8],  &io[BACKGROUND].write_bytes) ||
+        !ParseUint(fields[9],  &io[FOREGROUND].fsync) ||
+        !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
+        LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
+                                << s << "\"";
+        return false;
+    }
+    return true;
+}
+
+/* return true on parse success and false on failure */
+bool task_info::parse_task_io_stats(std::string&& s)
+{
+    std::vector<std::string> fields = Split(s, ",");
+    size_t size = fields.size();
+    if (size < 13 ||
+        !ParseInt(fields[size - 11],  &pid) ||
+        !ParseUint(fields[size - 10],  &io[FOREGROUND].rchar) ||
+        !ParseUint(fields[size - 9],  &io[FOREGROUND].wchar) ||
+        !ParseUint(fields[size - 8],  &io[FOREGROUND].read_bytes) ||
+        !ParseUint(fields[size - 7],  &io[FOREGROUND].write_bytes) ||
+        !ParseUint(fields[size - 6],  &io[BACKGROUND].rchar) ||
+        !ParseUint(fields[size - 5],  &io[BACKGROUND].wchar) ||
+        !ParseUint(fields[size - 4],  &io[BACKGROUND].read_bytes) ||
+        !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
+        !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
+        !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
+        LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
+                                << s << "\"";
+        return false;
+    }
+    comm = Join(std::vector<std::string>(
+                fields.begin() + 1, fields.end() - 11), ',');
+    return true;
+}
+
+bool io_usage::is_zero() const
+{
+    for (int i = 0; i < IO_TYPES; i++) {
+        for (int j = 0; j < UID_STATS; j++) {
+            for (int k = 0; k < CHARGER_STATS; k++) {
+                if (bytes[i][j][k])
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+namespace {
+
+void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
 {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) {
@@ -79,17 +150,19 @@
     refresh_uid_names = false;
 }
 
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+} // namespace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+    std::unordered_map<uint32_t, uid_info> uid_io_stats;
     std::string buffer;
     if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
         PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
         return uid_io_stats;
     }
 
-    std::vector<std::string> io_stats = Split(buffer, "\n");
-    struct uid_info u;
+    std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
+    uid_info u;
     vector<int> uids;
     vector<std::string*> uid_names;
 
@@ -97,32 +170,24 @@
         if (io_stats[i].empty()) {
             continue;
         }
-        std::vector<std::string> fields = Split(io_stats[i], " ");
-        if (fields.size() < 11 ||
-            !ParseUint(fields[0],  &u.uid) ||
-            !ParseUint(fields[1],  &u.io[FOREGROUND].rchar) ||
-            !ParseUint(fields[2],  &u.io[FOREGROUND].wchar) ||
-            !ParseUint(fields[3],  &u.io[FOREGROUND].read_bytes) ||
-            !ParseUint(fields[4],  &u.io[FOREGROUND].write_bytes) ||
-            !ParseUint(fields[5],  &u.io[BACKGROUND].rchar) ||
-            !ParseUint(fields[6],  &u.io[BACKGROUND].wchar) ||
-            !ParseUint(fields[7],  &u.io[BACKGROUND].read_bytes) ||
-            !ParseUint(fields[8],  &u.io[BACKGROUND].write_bytes) ||
-            !ParseUint(fields[9],  &u.io[FOREGROUND].fsync) ||
-            !ParseUint(fields[10], &u.io[BACKGROUND].fsync)) {
-            LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
-                                    << io_stats[i] << "\"";
-            continue;
-        }
 
-        uid_io_stats[u.uid] = u;
-        uid_io_stats[u.uid].name = std::to_string(u.uid);
-        uids.push_back(u.uid);
-        uid_names.push_back(&uid_io_stats[u.uid].name);
-        if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
-            refresh_uid_names = true;
+        if (io_stats[i].compare(0, 4, "task")) {
+            if (!u.parse_uid_io_stats(std::move(io_stats[i])))
+                continue;
+            uid_io_stats[u.uid] = u;
+            uid_io_stats[u.uid].name = std::to_string(u.uid);
+            uids.push_back(u.uid);
+            uid_names.push_back(&uid_io_stats[u.uid].name);
+            if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
+                refresh_uid_names = true;
+            } else {
+                uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+            }
         } else {
-            uid_io_stats[u.uid].name = last_uid_io_stats[u.uid].name;
+            task_info t;
+            if (!t.parse_task_io_stats(std::move(io_stats[i])))
+                continue;
+            uid_io_stats[u.uid].tasks[t.pid] = t;
         }
     }
 
@@ -133,34 +198,41 @@
     return uid_io_stats;
 }
 
-static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+namespace {
 
-static inline int records_size(
-    const std::map<uint64_t, struct uid_records>& curr_records)
+const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+
+inline size_t history_size(
+    const std::map<uint64_t, struct uid_records>& history)
 {
-    int count = 0;
-    for (auto const& it : curr_records) {
+    size_t count = 0;
+    for (auto const& it : history) {
         count += it.second.entries.size();
     }
     return count;
 }
 
-static struct uid_io_usage zero_io_usage;
+} // namespace
 
 void uid_monitor::add_records_locked(uint64_t curr_ts)
 {
     // remove records more than 5 days old
     if (curr_ts > 5 * DAY_TO_SEC) {
-        auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
-        records.erase(records.begin(), it);
+        auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+        io_history.erase(io_history.begin(), it);
     }
 
     struct uid_records new_records;
     for (const auto& p : curr_io_stats) {
         struct uid_record record = {};
         record.name = p.first;
-        record.ios = p.second;
-        if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+        if (!p.second.uid_ios.is_zero()) {
+            record.ios.user_id = p.second.user_id;
+            record.ios.uid_ios = p.second.uid_ios;
+            for (const auto& p_task : p.second.task_ios) {
+                if (!p_task.second.is_zero())
+                    record.ios.task_ios[p_task.first] = p_task.second;
+            }
             new_records.entries.push_back(record);
         }
     }
@@ -173,25 +245,25 @@
       return;
 
     // make some room for new records
-    int overflow = records_size(records) +
+    ssize_t overflow = history_size(io_history) +
         new_records.entries.size() - MAX_UID_RECORDS_SIZE;
-    while (overflow > 0 && records.size() > 0) {
-        auto del_it = records.begin();
+    while (overflow > 0 && io_history.size() > 0) {
+        auto del_it = io_history.begin();
         overflow -= del_it->second.entries.size();
-        records.erase(records.begin());
+        io_history.erase(io_history.begin());
     }
 
-    records[curr_ts] = new_records;
+    io_history[curr_ts] = new_records;
 }
 
 std::map<uint64_t, struct uid_records> uid_monitor::dump(
     double hours, uint64_t threshold, bool force_report)
 {
     if (force_report) {
-        report();
+        report(nullptr);
     }
 
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     std::map<uint64_t, struct uid_records> dump_records;
     uint64_t first_ts = 0;
@@ -200,19 +272,20 @@
         first_ts = time(NULL) - hours * HOUR_TO_SEC;
     }
 
-    for (auto it = records.lower_bound(first_ts); it != records.end(); ++it) {
+    for (auto it = io_history.lower_bound(first_ts); it != io_history.end(); ++it) {
         const std::vector<struct uid_record>& recs = it->second.entries;
         struct uid_records filtered;
 
         for (const auto& rec : recs) {
-            if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
-                rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
-                rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
-                rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
-                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
-                rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
-                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
-                rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
+            const io_usage& uid_usage = rec.ios.uid_ios;
+            if (uid_usage.bytes[READ][FOREGROUND][CHARGER_ON] +
+                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF] +
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON] +
+                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF] +
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON] +
+                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON] +
+                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
                 filtered.entries.push_back(rec);
             }
         }
@@ -230,20 +303,21 @@
 
 void uid_monitor::update_curr_io_stats_locked()
 {
-    std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+    std::unordered_map<uint32_t, uid_info> uid_io_stats =
         get_uid_io_stats_locked();
     if (uid_io_stats.empty()) {
         return;
     }
 
     for (const auto& it : uid_io_stats) {
-        const struct uid_info& uid = it.second;
-
+        const uid_info& uid = it.second;
         if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
-          curr_io_stats[uid.name] = {};
+            curr_io_stats[uid.name] = {};
         }
 
         struct uid_io_usage& usage = curr_io_stats[uid.name];
+        usage.user_id = multiuser_get_user_id(uid.uid);
+
         int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
             last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
         int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
@@ -253,30 +327,177 @@
         int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
             last_uid_io_stats[uid.uid].io[BACKGROUND].write_bytes;
 
-        usage.bytes[READ][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat] +=
             (fg_rd_delta < 0) ? 0 : fg_rd_delta;
-        usage.bytes[READ][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat] +=
             (bg_rd_delta < 0) ? 0 : bg_rd_delta;
-        usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat] +=
             (fg_wr_delta < 0) ? 0 : fg_wr_delta;
-        usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat] +=
             (bg_wr_delta < 0) ? 0 : bg_wr_delta;
+
+        for (const auto& task_it : uid.tasks) {
+            const task_info& task = task_it.second;
+            const pid_t pid = task_it.first;
+            const std::string& comm = task_it.second.comm;
+            int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -
+                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;
+            int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -
+                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;
+            int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -
+                last_uid_io_stats[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;
+            int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -
+                last_uid_io_stats[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;
+
+            io_usage& task_usage = usage.task_ios[comm];
+            task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+                (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
+            task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+                (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
+            task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+                (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
+            task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+                (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
+        }
     }
 
     last_uid_io_stats = uid_io_stats;
 }
 
-void uid_monitor::report()
+void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    if (!enabled()) return;
+
+    Mutex::Autolock _l(uidm_mutex);
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
+
+    if (protos) {
+        update_uid_io_proto(protos);
+    }
+}
+
+namespace {
+
+void set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage)
+{
+    usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);
+    usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);
+    usage_proto->set_rd_bg_chg_on(usage.bytes[READ][BACKGROUND][CHARGER_ON]);
+    usage_proto->set_rd_bg_chg_off(usage.bytes[READ][BACKGROUND][CHARGER_OFF]);
+    usage_proto->set_wr_fg_chg_on(usage.bytes[WRITE][FOREGROUND][CHARGER_ON]);
+    usage_proto->set_wr_fg_chg_off(usage.bytes[WRITE][FOREGROUND][CHARGER_OFF]);
+    usage_proto->set_wr_bg_chg_on(usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+    usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);
+}
+
+void get_io_usage_proto(io_usage* usage, const IOUsage& io_proto)
+{
+    usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();
+    usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();
+    usage->bytes[READ][BACKGROUND][CHARGER_ON] = io_proto.rd_bg_chg_on();
+    usage->bytes[READ][BACKGROUND][CHARGER_OFF] = io_proto.rd_bg_chg_off();
+    usage->bytes[WRITE][FOREGROUND][CHARGER_ON] = io_proto.wr_fg_chg_on();
+    usage->bytes[WRITE][FOREGROUND][CHARGER_OFF] = io_proto.wr_fg_chg_off();
+    usage->bytes[WRITE][BACKGROUND][CHARGER_ON] = io_proto.wr_bg_chg_on();
+    usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off();
+}
+
+} // namespace
+
+void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
+{
+    for (const auto& item : io_history) {
+        const uint64_t& end_ts = item.first;
+        const struct uid_records& recs = item.second;
+        unordered_map<userid_t, UidIOItem*> user_items;
+
+        for (const auto& entry : recs.entries) {
+            userid_t user_id = entry.ios.user_id;
+            UidIOItem* item_proto = user_items[user_id];
+            if (item_proto == nullptr) {
+                item_proto = (*protos)[user_id].mutable_uid_io_usage()
+                             ->add_uid_io_items();
+                user_items[user_id] = item_proto;
+            }
+            item_proto->set_end_ts(end_ts);
+
+            UidIORecords* recs_proto = item_proto->mutable_records();
+            recs_proto->set_start_ts(recs.start_ts);
+
+            UidRecord* rec_proto = recs_proto->add_entries();
+            rec_proto->set_uid_name(entry.name);
+            rec_proto->set_user_id(user_id);
+
+            IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
+            const io_usage& uio_ios = entry.ios.uid_ios;
+            set_io_usage_proto(uid_io_proto, uio_ios);
+
+            for (const auto& task_io : entry.ios.task_ios) {
+                const std::string& task_name = task_io.first;
+                const io_usage& task_ios = task_io.second;
+
+                TaskIOUsage* task_io_proto = rec_proto->add_task_io();
+                task_io_proto->set_task_name(task_name);
+                set_io_usage_proto(task_io_proto->mutable_ios(), task_ios);
+            }
+        }
+    }
+}
+
+void uid_monitor::clear_user_history(userid_t user_id)
+{
+    Mutex::Autolock _l(uidm_mutex);
+
+    for (auto& item : io_history) {
+        vector<uid_record>* entries = &item.second.entries;
+        entries->erase(
+            remove_if(entries->begin(), entries->end(),
+                [user_id](const uid_record& rec) {
+                    return rec.ios.user_id == user_id;}),
+            entries->end());
+    }
+
+    for (auto it = io_history.begin(); it != io_history.end(); ) {
+        if (it->second.entries.empty()) {
+            it = io_history.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+{
+    if (!enabled()) return;
+
+    Mutex::Autolock _l(uidm_mutex);
+
+    for (const auto& item_proto : uid_io_proto.uid_io_items()) {
+        const UidIORecords& records_proto = item_proto.records();
+        struct uid_records* recs = &io_history[item_proto.end_ts()];
+
+        recs->start_ts = records_proto.start_ts();
+        for (const auto& rec_proto : records_proto.entries()) {
+            struct uid_record record;
+            record.name = rec_proto.uid_name();
+            record.ios.user_id = rec_proto.user_id();
+            get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io());
+
+            for (const auto& task_io_proto : rec_proto.task_io()) {
+                get_io_usage_proto(
+                    &record.ios.task_ios[task_io_proto.task_name()],
+                    task_io_proto.ios());
+            }
+            recs->entries.push_back(record);
+        }
+    }
 }
 
 void uid_monitor::set_charger_state(charger_stat_t stat)
 {
-    std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+    Mutex::Autolock _l(uidm_mutex);
 
     if (charger_stat == stat) {
         return;
@@ -289,16 +510,11 @@
 void uid_monitor::init(charger_stat_t stat)
 {
     charger_stat = stat;
+
     start_ts = time(NULL);
     last_uid_io_stats = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
-{
-    sem_init(&um_lock, 0, 1);
-}
-
-uid_monitor::~uid_monitor()
-{
-    sem_destroy(&um_lock);
+    : enable(!access(UID_IO_STATS_PATH, R_OK)) {
 }
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..4fd4bc9 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -18,6 +18,7 @@
 
 #include <dirent.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/time.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -41,124 +42,7 @@
 #include <storaged.h>
 #include <storaged_utils.h>
 
-bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
-    // Get time
-    struct timespec ts;
-    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
-    // when system is running.
-    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
-    if (ret < 0) {
-        PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
-        return false;
-    }
-
-    std::string buffer;
-    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
-        return false;
-    }
-
-    // Regular diskstats entries
-    std::stringstream ss(buffer);
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        ss >> *((uint64_t*)stats + i);
-    }
-    // Other entries
-    stats->start_time = 0;
-    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
-        ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
-    stats->counter = 1;
-    stats->io_avg = (double)stats->io_in_flight;
-    return true;
-}
-
-struct disk_perf get_disk_perf(struct disk_stats* stats) {
-    struct disk_perf perf;
-    memset(&perf, 0, sizeof(struct disk_perf));  // initialize
-
-    if (stats->io_ticks) {
-        if (stats->read_ticks) {
-            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
-            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
-                                        stats->read_sectors *
-                                        stats->io_in_queue +
-                                        (divisor >> 1)) /
-                                            divisor;
-            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
-                                        stats->read_ios *
-                                        stats->io_in_queue +
-                                        (divisor >> 1)) /
-                                            divisor;
-        }
-        if (stats->write_ticks) {
-            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
-                        perf.write_perf = ((unsigned long long)SECTOR_SIZE *
-                                                    stats->write_sectors *
-                                                    stats->io_in_queue +
-                                                    (divisor >> 1)) /
-                                                        divisor;
-                        perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
-                                                    stats->write_ios *
-                                                    stats->io_in_queue +
-                                                    (divisor >> 1)) /
-                                                        divisor;
-        }
-        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
-                                stats->io_ticks;
-    }
-    return perf;
-}
-
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
-    struct disk_stats inc;
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
-            continue;
-        }
-
-        *((uint64_t*)&inc + i) =
-                *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
-    }
-    // io_in_flight is exception
-    inc.io_in_flight = curr->io_in_flight;
-
-    inc.start_time = prev->end_time;
-    inc.end_time = curr->end_time;
-    inc.io_avg = curr->io_avg;
-    inc.counter = 1;
-
-    return inc;
-}
-
-// Add src to dst
-void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
-    if (dst->end_time != 0 && dst->end_time != src->start_time) {
-        LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
-            << " are added. dst end with " << dst->end_time
-            << ", src start with " << src->start_time;
-    }
-
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
-            continue;
-        }
-
-        *((uint64_t*)dst + i) += *((uint64_t*)src + i);
-    }
-
-    dst->io_in_flight = src->io_in_flight;
-    if (dst->counter + src->counter) {
-        dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
-                        (dst->counter + src->counter);
-    }
-    dst->counter += src->counter;
-    dst->end_time = src->end_time;
-    if (dst->start_time == 0) {
-        dst->start_time = src->start_time;
-    }
-}
-
-static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+bool cmp_uid_info(const UidInfo& l, const UidInfo& r) {
     // Compare background I/O first.
     for (int i = UID_STATS - 1; i >= 0; i--) {
         uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
@@ -177,56 +61,72 @@
     return l.name < r.name;
 }
 
-void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+void sort_running_uids_info(std::vector<UidInfo> &uids) {
     std::sort(uids.begin(), uids.end(), cmp_uid_info);
 }
 
 // Logging functions
-void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task) {
     printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
            "bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
 
     for (const auto& uid : uids) {
-        printf("%s %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju\n", uid.name.c_str(),
+        printf("%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+            uid.name.c_str(),
             uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,
             uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
             uid.io[0].fsync, uid.io[1].fsync);
+        if (flag_dump_task) {
+            for (const auto& task_it : uid.tasks) {
+                const task_info& task = task_it.second;
+                printf("-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                        " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+                    task.comm.c_str(),
+                    task.io[0].rchar, task.io[0].wchar, task.io[0].read_bytes, task.io[0].write_bytes,
+                    task.io[1].rchar, task.io[1].wchar, task.io[1].read_bytes, task.io[1].write_bytes,
+                    task.io[0].fsync, task.io[1].fsync);
+            }
+        }
     }
     fflush(stdout);
 }
 
-#if DEBUG
-void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
-    // skip if the input structure are all zeros
-    if (perf == NULL) return;
-    struct disk_perf zero_cmp;
-    memset(&zero_cmp, 0, sizeof(zero_cmp));
-    if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+void log_console_perf_history(const vector<int>& perf_history) {
+    if (perf_history.size() < 3 ||
+        perf_history.size() != perf_history[0] +
+                               perf_history[1] +
+                               perf_history[2] + (size_t)3) {
+        return;
+    }
 
-    LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
-              << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
-              << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
-              << " q:" << perf->queue;
-}
-#else
-void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
-#endif
+    printf("\nI/O perf history (KB/s) :  most_recent  <---------  least_recent \n");
 
-void log_event_disk_stats(struct disk_stats* stats, const char* type) {
-    // skip if the input structure are all zeros
-    if (stats == NULL) return;
-    struct disk_stats zero_cmp;
-    memset(&zero_cmp, 0, sizeof(zero_cmp));
-    // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
-    if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+    std::stringstream line;
+    int start = 3;
+    int end = 3 + perf_history[0];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
+              std::ostream_iterator<int>(line, " "));
+    printf("last 24 hours : %s\n", line.str().c_str());
 
-    android_log_event_list(EVENTLOGTAG_DISKSTATS)
-        << type << stats->start_time << stats->end_time
-        << stats->read_ios << stats->read_merges
-        << stats->read_sectors << stats->read_ticks
-        << stats->write_ios << stats->write_merges
-        << stats->write_sectors << stats->write_ticks
-        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
-        << LOG_ID_EVENTS;
+    line.str("");
+    start = end;
+    end += perf_history[1];
+    std::copy(perf_history.begin() + start, perf_history.begin() + end,
+              std::ostream_iterator<int>(line, " "));
+    printf("last 7 days   : %s\n", line.str().c_str());
+
+    line.str("");
+    start = end;
+    std::copy(perf_history.begin() + start, perf_history.end(),
+              std::ostream_iterator<int>(line, " "));
+    printf("last 52 weeks : %s\n", line.str().c_str());
 }
 
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries) {
+    map<string, io_usage> merged_entries;
+    for (const auto& record : entries) {
+        merged_entries[record.name] += record.ios.uid_ios;
+    }
+    return merged_entries;
+}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
deleted file mode 100644
index 26d04b1..0000000
--- a/storaged/tests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := storaged-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
-    -fstack-protector-all \
-    -g \
-    -Wall -Wextra \
-    -Werror \
-    -fno-builtin \
-
-test_src_files := \
-    storaged_test.cpp \
-
-# Build tests for the logger. Run with:
-#   adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index b103ac1..d1fa9ed 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <chrono>
 #include <deque>
 #include <fcntl.h>
 #include <random>
@@ -24,13 +25,20 @@
 
 #include <gtest/gtest.h>
 
+#include <healthhalutils/HealthHalUtils.h>
 #include <storaged.h>               // data structures
 #include <storaged_utils.h>         // functions to test
 
 #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
 #define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
 
-static void pause(uint32_t sec) {
+using namespace std;
+using namespace chrono;
+using namespace storaged_proto;
+
+namespace {
+
+void write_and_pause(uint32_t sec) {
     const char* path = "/cache/test";
     int fd = open(path, O_WRONLY | O_CREAT, 0600);
     ASSERT_LT(-1, fd);
@@ -53,6 +61,8 @@
     sleep(sec);
 }
 
+} // namespace
+
 // the return values of the tested functions should be the expected ones
 const char* DISK_STATS_PATH;
 TEST(storaged_test, retvals) {
@@ -77,13 +87,11 @@
     EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
 
     // reading a wrong path should not damage the output structure
-    EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+    EXPECT_EQ(stats, old_stats);
 }
 
 TEST(storaged_test, disk_stats) {
-    struct disk_stats stats;
-    memset(&stats, 0, sizeof(struct disk_stats));
-
+    struct disk_stats stats = {};
     ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
 
     // every entry of stats (except io_in_flight) should all be greater than 0
@@ -93,11 +101,7 @@
     }
 
     // accumulation of the increments should be the same with the overall increment
-    struct disk_stats base, tmp, curr, acc, inc[5];
-    memset(&base, 0, sizeof(struct disk_stats));
-    memset(&tmp, 0, sizeof(struct disk_stats));
-    memset(&acc, 0, sizeof(struct disk_stats));
-
+    struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5];
     for (uint i = 0; i < 5; ++i) {
         ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
         if (i == 0) {
@@ -106,22 +110,18 @@
             sleep(5);
             continue;
         }
-        inc[i] = get_inc_disk_stats(&tmp, &curr);
+        get_inc_disk_stats(&tmp, &curr, &inc[i]);
         add_disk_stats(&inc[i], &acc);
         tmp = curr;
-        pause(5);
+        write_and_pause(5);
     }
-    struct disk_stats overall_inc;
-    memset(&overall_inc, 0, sizeof(disk_stats));
-    overall_inc= get_inc_disk_stats(&base, &curr);
+    struct disk_stats overall_inc = {};
+    get_inc_disk_stats(&base, &curr, &overall_inc);
 
-    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
-        if (i == 8) continue; // skip io_in_flight which can be 0
-        EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
-    }
+    EXPECT_EQ(overall_inc, acc);
 }
 
-static double mean(std::deque<uint32_t> nums) {
+double mean(std::deque<uint32_t> nums) {
     double sum = 0.0;
     for (uint32_t i : nums) {
     sum += i;
@@ -129,7 +129,7 @@
     return sum / nums.size();
 }
 
-static double standard_deviation(std::deque<uint32_t> nums) {
+double standard_deviation(std::deque<uint32_t> nums) {
     double sum = 0.0;
     double avg = mean(nums);
     for (uint32_t i : nums) {
@@ -181,7 +181,7 @@
     }
 }
 
-static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
     struct disk_perf retval;
     retval.read_perf = (double)perf.read_perf * mul;
     retval.read_ios = (double)perf.read_ios * mul;
@@ -192,7 +192,7 @@
     return retval;
 }
 
-static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
     struct disk_stats retval;
     retval.read_ios = stats1.read_ios + stats2.read_ios;
     retval.read_merges = stats1.read_merges + stats2.read_merges;
@@ -210,11 +210,42 @@
     return retval;
 }
 
+void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+    EXPECT_LE(stats1.read_ios, stats2.read_ios);
+    EXPECT_LE(stats1.read_merges, stats2.read_merges);
+    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+    EXPECT_LE(stats1.write_ios, stats2.write_ios);
+    EXPECT_LE(stats1.write_merges, stats2.write_merges);
+    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+
+    EXPECT_TRUE(stats1.read_ios < stats2.read_ios ||
+        stats1.read_merges < stats2.read_merges ||
+        stats1.read_sectors < stats2.read_sectors ||
+        stats1.read_ticks < stats2.read_ticks ||
+        stats1.write_ios < stats2.write_ios ||
+        stats1.write_merges < stats2.write_merges ||
+        stats1.write_sectors < stats2.write_sectors ||
+        stats1.write_ticks < stats2.write_ticks ||
+        stats1.io_ticks < stats2.io_ticks ||
+        stats1.io_in_queue < stats2.io_in_queue);
+}
+
 TEST(storaged_test, disk_stats_monitor) {
+    using android::hardware::health::V2_0::get_health_service;
+
+    auto healthService = get_health_service();
+
     // asserting that there is one file for diskstats
-    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
+                access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+
     // testing if detect() will return the right value
-    disk_stats_monitor dsm_detect;
+    disk_stats_monitor dsm_detect{healthService};
+    ASSERT_TRUE(dsm_detect.enabled());
     // feed monitor with constant perf data for io perf baseline
     // using constant perf is reasonable since the functionality of stream_stats
     // has already been tested
@@ -257,7 +288,7 @@
     }
 
     // testing if stalled disk_stats can be correctly accumulated in the monitor
-    disk_stats_monitor dsm_acc;
+    disk_stats_monitor dsm_acc{healthService};
     struct disk_stats norm_inc = {
         .read_ios = 200,
         .read_merges = 0,
@@ -294,14 +325,12 @@
         .io_avg = 0
     };
 
-    struct disk_stats stats_base;
-    memset(&stats_base, 0, sizeof(stats_base));
-
+    struct disk_stats stats_base = {};
     int loop_size = 100;
     for (int i = 0; i < loop_size; ++i) {
         stats_base = disk_stats_add(stats_base, norm_inc);
         dsm_acc.update(&stats_base);
-        EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+        EXPECT_EQ(dsm_acc.mValid, (uint32_t)i >= dsm_acc.mWindow);
         EXPECT_FALSE(dsm_acc.mStall);
     }
 
@@ -316,36 +345,259 @@
         EXPECT_TRUE(dsm_acc.mValid);
         EXPECT_FALSE(dsm_acc.mStall);
     }
-}
 
-static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
-    EXPECT_LE(stats1.read_ios, stats2.read_ios);
-    EXPECT_LE(stats1.read_merges, stats2.read_merges);
-    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
-    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
-
-    EXPECT_LE(stats1.write_ios, stats2.write_ios);
-    EXPECT_LE(stats1.write_merges, stats2.write_merges);
-    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
-    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
-
-    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
-    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
-}
-
-#define TEST_LOOPS 20
-TEST(storaged_test, disk_stats_publisher) {
-    // asserting that there is one file for diskstats
-    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
-    disk_stats_publisher dsp;
-    struct disk_stats prev;
-    memset(&prev, 0, sizeof(prev));
-
-    for (int i = 0; i < TEST_LOOPS; ++i) {
-        dsp.update();
-        expect_increasing(prev, dsp.mPrevious);
-        prev = dsp.mPrevious;
-        pause(10);
+    struct disk_stats stats_prev = {};
+    loop_size = 10;
+    write_and_pause(5);
+    for (int i = 0; i < loop_size; ++i) {
+        dsm_detect.update();
+        expect_increasing(stats_prev, dsm_detect.mPrevious);
+        stats_prev = dsm_detect.mPrevious;
+        write_and_pause(5);
     }
 }
 
+TEST(storaged_test, storage_info_t) {
+    storage_info_t si;
+    time_point<steady_clock> tp;
+    time_point<system_clock> stp;
+
+    // generate perf history [least_recent  ------> most recent]
+    // day 1:   5,  10,  15,  20            | daily average 12
+    // day 2:  25,  30,  35,  40,  45       | daily average 35
+    // day 3:  50,  55,  60,  65,  70       | daily average 60
+    // day 4:  75,  80,  85,  90,  95       | daily average 85
+    // day 5: 100, 105, 110, 115,           | daily average 107
+    // day 6: 120, 125, 130, 135, 140       | daily average 130
+    // day 7: 145, 150, 155, 160, 165       | daily average 155
+    // end of week 1:                       | weekly average 83
+    // day 1: 170, 175, 180, 185, 190       | daily average 180
+    // day 2: 195, 200, 205, 210, 215       | daily average 205
+    // day 3: 220, 225, 230, 235            | daily average 227
+    // day 4: 240, 245, 250, 255, 260       | daily average 250
+    // day 5: 265, 270, 275, 280, 285       | daily average 275
+    // day 6: 290, 295, 300, 305, 310       | daily average 300
+    // day 7: 315, 320, 325, 330, 335       | daily average 325
+    // end of week 2:                       | weekly average 251
+    // day 1: 340, 345, 350, 355            | daily average 347
+    // day 2: 360, 365, 370, 375
+    si.day_start_tp = {};
+    for (int i = 0; i < 75; i++) {
+        tp += hours(5);
+        stp = {};
+        stp += duration_cast<chrono::seconds>(tp.time_since_epoch());
+        si.update_perf_history((i + 1) * 5, stp);
+    }
+
+    vector<int> history = si.get_perf_history();
+    EXPECT_EQ(history.size(), 66UL);
+    size_t i = 0;
+    EXPECT_EQ(history[i++], 4);
+    EXPECT_EQ(history[i++], 7);    // 7 days
+    EXPECT_EQ(history[i++], 52);   // 52 weeks
+    // last 24 hours
+    EXPECT_EQ(history[i++], 375);
+    EXPECT_EQ(history[i++], 370);
+    EXPECT_EQ(history[i++], 365);
+    EXPECT_EQ(history[i++], 360);
+    // daily average of last 7 days
+    EXPECT_EQ(history[i++], 347);
+    EXPECT_EQ(history[i++], 325);
+    EXPECT_EQ(history[i++], 300);
+    EXPECT_EQ(history[i++], 275);
+    EXPECT_EQ(history[i++], 250);
+    EXPECT_EQ(history[i++], 227);
+    EXPECT_EQ(history[i++], 205);
+    // weekly average of last 52 weeks
+    EXPECT_EQ(history[i++], 251);
+    EXPECT_EQ(history[i++], 83);
+    for (; i < history.size(); i++) {
+        EXPECT_EQ(history[i], 0);
+    }
+}
+
+TEST(storaged_test, uid_monitor) {
+    uid_monitor uidm;
+
+    uidm.io_history[200] = {
+        .start_ts = 100,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+            { "app2", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+                .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 1,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+            { "app3", {
+                .user_id = 0,
+                .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000,
+              }
+            },
+        },
+    };
+
+    unordered_map<int, StoragedProto> protos;
+
+    uidm.update_uid_io_proto(&protos);
+
+    EXPECT_EQ(protos.size(), 2U);
+    EXPECT_EQ(protos.count(0), 1UL);
+    EXPECT_EQ(protos.count(1), 1UL);
+
+    EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_0_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_0_item_0.records().entries_size(), 2);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), "app2");
+    EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL);
+    const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_0_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_0_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), "app3");
+    EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL);
+    EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL);
+
+    EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2);
+    const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0);
+    EXPECT_EQ(user_1_item_0.end_ts(), 200UL);
+    EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL);
+    EXPECT_EQ(user_1_item_0.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL);
+    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+    const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1);
+    EXPECT_EQ(user_1_item_1.end_ts(), 300UL);
+    EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL);
+    EXPECT_EQ(user_1_item_1.records().entries_size(), 1);
+    EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), "app1");
+    EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
+    EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
+
+    uidm.io_history.clear();
+
+    uidm.io_history[300] = {
+        .start_ts = 200,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.io_history[400] = {
+        .start_ts = 300,
+        .entries = {
+            { "app1", {
+                .user_id = 0,
+                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+              }
+            },
+        },
+    };
+
+    uidm.load_uid_io_proto(protos[0].uid_io_usage());
+    uidm.load_uid_io_proto(protos[1].uid_io_usage());
+
+    EXPECT_EQ(uidm.io_history.size(), 3UL);
+    EXPECT_EQ(uidm.io_history.count(200), 1UL);
+    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+    EXPECT_EQ(uidm.io_history.count(400), 1UL);
+
+    EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
+    const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+    EXPECT_EQ(entries_0.size(), 3UL);
+    EXPECT_EQ(entries_0[0].name, "app1");
+    EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_0[1].name, "app2");
+    EXPECT_EQ(entries_0[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_0[2].name, "app1");
+    EXPECT_EQ(entries_0[2].ios.user_id, 1UL);
+    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+
+    EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
+    const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+    EXPECT_EQ(entries_1.size(), 3UL);
+    EXPECT_EQ(entries_1[0].name, "app1");
+    EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(entries_1[1].name, "app3");
+    EXPECT_EQ(entries_1[1].ios.user_id, 0UL);
+    EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(entries_1[2].name, "app1");
+    EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
+    EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+
+    EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
+    const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+    EXPECT_EQ(entries_2.size(), 1UL);
+    EXPECT_EQ(entries_2[0].name, "app1");
+    EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
+    EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    map<string, io_usage> merged_entries_0 = merge_io_usage(entries_0);
+    EXPECT_EQ(merged_entries_0.size(), 2UL);
+    EXPECT_EQ(merged_entries_0.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_0.count("app2"), 1UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_0["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL);
+    EXPECT_EQ(merged_entries_0["app2"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_1 = merge_io_usage(entries_1);
+    EXPECT_EQ(merged_entries_1.size(), 2UL);
+    EXPECT_EQ(merged_entries_1.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_1.count("app3"), 1UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+    EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+    EXPECT_EQ(merged_entries_1["app3"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+
+    map<string, io_usage> merged_entries_2 = merge_io_usage(entries_2);
+    EXPECT_EQ(merged_entries_2.size(), 1UL);
+    EXPECT_EQ(merged_entries_2.count("app1"), 1UL);
+    EXPECT_EQ(merged_entries_2["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+    uidm.clear_user_history(0);
+
+    EXPECT_EQ(uidm.io_history.size(), 2UL);
+    EXPECT_EQ(uidm.io_history.count(200), 1UL);
+    EXPECT_EQ(uidm.io_history.count(300), 1UL);
+
+    EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
+    EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+
+    uidm.clear_user_history(1);
+
+    EXPECT_EQ(uidm.io_history.size(), 0UL);
+}
diff --git a/storaged/tools/ranker.py b/storaged/tools/ranker.py
new file mode 100644
index 0000000..d8096b7
--- /dev/null
+++ b/storaged/tools/ranker.py
@@ -0,0 +1,181 @@
+# Copyright 2017 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.
+
+"""Parser and ranker for dumpsys storaged output.
+
+This module parses output from dumpsys storaged by ranking uids based on
+their io usage measured in 8 different stats. It must be provided the input
+file through command line argument -i/--input.
+
+For more details, see:
+    $ python ranker.py -h
+
+Example:
+    $ python ranker.py -i io.txt -o output.txt -u 20 -cnt
+"""
+
+import argparse
+import sys
+
+IO_NAMES = ["[READ][FOREGROUND][CHARGER_OFF]",
+            "[WRITE][FOREGROUND][CHARGER_OFF]",
+            "[READ][BACKGROUND][CHARGER_OFF]",
+            "[WRITE][BACKGROUND][CHARGER_OFF]",
+            "[READ][FOREGROUND][CHARGER_ON]",
+            "[WRITE][FOREGROUND][CHARGER_ON]",
+            "[READ][BACKGROUND][CHARGER_ON]",
+            "[WRITE][BACKGROUND][CHARGER_ON]"]
+
+
+def get_args():
+  """Get arguments from command line.
+
+  The only required argument is input file.
+
+  Returns:
+    Args containing cmdline arguments
+  """
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument("-i", "--input", dest="input", required="true",
+                      help="input io FILE, must provide", metavar="FILE")
+  parser.add_argument("-o", "--output", dest="output", default="stdout",
+                      help="output FILE, default to stdout", metavar="FILE")
+  parser.add_argument("-u", "--uidcnt", dest="uidcnt", type=int, default=10,
+                      help="set number of uids to display for each rank, "
+                      "default 10")
+  parser.add_argument("-c", "--combine", dest="combine", default=False,
+                      action="store_true", help="add io stats for same uids, "
+                      "default to take io stats of last appearing uids")
+  parser.add_argument("-n", "--native", dest="native", default=False,
+                      action="store_true", help="only include native apps in "
+                      "ranking, default to include all apps")
+  parser.add_argument("-t", "--task", dest="task", default=False,
+                      action="store_true", help="display task io under uids, "
+                      "default to not display tasks")
+  return parser.parse_args()
+
+
+def is_number(word):
+  try:
+    int(word)
+    return True
+  except ValueError:
+    return False
+
+
+def combine_or_filter(args):
+  """Parser for io input.
+
+  Either args.combine io stats for the same uids
+  or take the io stats for the last uid and ignore
+  the same uids before it.
+
+  If task is required, store task ios along with uid
+  for later display.
+
+  Returns:
+    The structure for the return value uids is as follows:
+    uids: {uid -> [UID_STATS, TASK_STATS(optional)]}
+    UID_STATS: [io1, io2, ..., io8]
+    TASK_STATS: {task_name -> [io1, io2, ..., io8]}
+  """
+  fin = open(args.input, "r")
+  uids = {}
+  cur_uid = 0
+  task_enabled = args.task
+  for line in fin:
+    words = line.split()
+    if words[0] == "->":
+      # task io
+      if not task_enabled:
+        continue
+      # get task command line
+      i = len(words) - 8
+      task = " ".join(words[1:i])
+      if task in uids[cur_uid][1]:
+        task_io = uids[cur_uid][1][task]
+        for j in range(8):
+          task_io[j] += long(words[i+j])
+      else:
+        task_io = []
+        for j in range(8):
+          task_io.append(long(words[i+j]))
+      uids[cur_uid][1][task] = task_io
+
+    elif len(words) > 8:
+      if not is_number(words[0]) and args.native:
+        # uid not requested, ignore its tasks as well
+        task_enabled = False
+        continue
+      task_enabled = args.task
+      i = len(words) - 8
+      uid = " ".join(words[:i])
+      if uid in uids and args.combine:
+        uid_io = uids[uid][0]
+        for j in range(8):
+          uid_io[j] += long(words[i+j])
+        uids[uid][0] = uid_io
+      else:
+        uid_io = [long(words[i+j]) for j in range(8)]
+        uids[uid] = [uid_io]
+        if task_enabled:
+          uids[uid].append({})
+      cur_uid = uid
+
+  return uids
+
+
+def rank_uids(uids):
+  """Sort uids based on eight different io stats.
+
+  Returns:
+    uid_rank is a 2d list of tuples:
+    The first dimension represent the 8 different io stats.
+    The second dimension is a sorted list of tuples by tup[0],
+    each tuple is a uid's perticular stat at the first dimension and the uid.
+  """
+  uid_rank = [[(uids[uid][0][i], uid) for uid in uids] for i in range(8)]
+  for i in range(8):
+    uid_rank[i].sort(key=lambda tup: tup[0], reverse=True)
+  return uid_rank
+
+
+def display_uids(uid_rank, uids, args):
+  """Display ranked uid io, along with task io if specified."""
+  fout = sys.stdout
+  if args.output != "stdout":
+    fout = open(args.output, "w")
+
+  for i in range(8):
+    fout.write("RANKING BY " + IO_NAMES[i] + "\n")
+    for j in range(min(args.uidcnt, len(uid_rank[0]))):
+      uid = uid_rank[i][j][1]
+      uid_stat = " ".join([str(uid_io) for uid_io in uids[uid][0]])
+      fout.write(uid + " " + uid_stat + "\n")
+      if args.task:
+        for task in uids[uid][1]:
+          task_stat = " ".join([str(task_io) for task_io in uids[uid][1][task]])
+          fout.write("-> " + task + " " + task_stat + "\n")
+      fout.write("\n")
+
+
+def main():
+  args = get_args()
+  uids = combine_or_filter(args)
+  uid_rank = rank_uids(uids)
+  display_uids(uid_rank, uids, args)
+
+if __name__ == "__main__":
+  main()
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
new file mode 100644
index 0000000..58e3fd2
--- /dev/null
+++ b/storaged/uid_info.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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 <binder/Parcel.h>
+
+#include "uid_info.h"
+
+using namespace android;
+using namespace android::os::storaged;
+
+status_t UidInfo::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(uid);
+    parcel->writeCString(name.c_str());
+    parcel->write(&io, sizeof(io));
+
+    parcel->writeInt32(tasks.size());
+    for (const auto& task_it : tasks) {
+        parcel->writeInt32(task_it.first);
+        parcel->writeCString(task_it.second.comm.c_str());
+        parcel->write(&task_it.second.io, sizeof(task_it.second.io));
+    }
+    return NO_ERROR;
+}
+
+status_t UidInfo::readFromParcel(const Parcel* parcel) {
+    uid = parcel->readInt32();
+    name = parcel->readCString();
+    parcel->read(&io, sizeof(io));
+
+    uint32_t tasks_size = parcel->readInt32();
+    for (uint32_t i = 0; i < tasks_size; i++) {
+        task_info task;
+        task.pid = parcel->readInt32();
+        task.comm = parcel->readCString();
+        parcel->read(&task.io, sizeof(task.io));
+        tasks[task.pid] = task;
+    }
+    return NO_ERROR;
+}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 5428e73..899f8a3 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -39,7 +39,6 @@
         "libcrypto",
         "libcutils",
         "libkeymaster_portable",
-        "libkeymaster_staging",
         "libtrusty",
         "libkeymaster_messages",
         "libsoftkeymasterdevice",