Merge "Move some properties users to __system_property_read_callback()" am: 4a5a337ef2 am: 604f61dcbd am: 6597b95707
am: 33c3c190f7

Change-Id: I40a1f94388407d4c79437f44b64006609830fa9d
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/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index fc88217..85a593f 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -34,23 +34,35 @@
 #include "fs_mgr_priv.h"
 #include "cryptfs.h"
 
-static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+static int get_dev_sz(char *fs_blkdev, uint64_t *dev_sz)
 {
-    uint64_t dev_sz;
-    int fd, rc = 0;
+    int fd;
 
-    if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
+    if ((fd = open(fs_blkdev, O_RDONLY)) < 0) {
         PERROR << "Cannot open block device";
         return -1;
     }
 
-    if ((ioctl(fd, BLKGETSIZE64, &dev_sz)) == -1) {
+    if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {
         PERROR << "Cannot get block device size";
         close(fd);
         return -1;
     }
 
     close(fd);
+    return 0;
+}
+
+static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
+{
+    uint64_t dev_sz;
+    int rc = 0;
+    int status;
+
+    rc = get_dev_sz(fs_blkdev, &dev_sz);
+    if (rc) {
+        return rc;
+    }
 
     /* Format the partition using the calculated length */
     if (crypt_footer) {
@@ -85,9 +97,25 @@
     return rc;
 }
 
-static int format_f2fs(char *fs_blkdev)
+static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
 {
-    const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
+    int status;
+
+    if (!dev_sz) {
+        int rc = get_dev_sz(fs_blkdev, &dev_sz);
+        if (rc) {
+            return rc;
+        }
+    }
+
+    /* Format the partition using the calculated length */
+    if (crypt_footer) {
+        dev_sz -= CRYPT_FOOTER_OFFSET;
+    }
+
+    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};
 
     return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
                                    LOG_KLOG, true, nullptr, nullptr, 0);
@@ -101,7 +129,7 @@
            << " as '" << fstab->fs_type << "'";
 
     if (!strncmp(fstab->fs_type, "f2fs", 4)) {
-        rc = format_f2fs(fstab->blk_device);
+        rc = format_f2fs(fstab->blk_device, fstab->length, crypt_footer);
     } else if (!strncmp(fstab->fs_type, "ext4", 4)) {
         rc = format_ext4(fstab->blk_device, fstab->mount_point, crypt_footer);
     } else {
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..d348866 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,72 @@
     header_libs: ["libbatteryservice_headers"],
     export_header_lib_headers: ["libbatteryservice_headers"],
 }
+
+cc_library_static {
+    name: "libbatterymonitor",
+    srcs: ["BatteryMonitor.cpp"],
+    vendor_available: true,
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "libutils",
+        "libbase",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
+
+cc_library_static {
+    name: "android.hardware.health@2.0-impl",
+    vendor_available: true,
+    srcs: [
+        "Health.cpp",
+        "healthd_common.cpp",
+    ],
+
+    cflags: ["-DHEALTHD_USE_HEALTH_2_0"],
+
+    export_include_dirs: ["include"],
+
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "libcutils",
+        "android.hardware.health@2.0",
+    ],
+
+    static_libs: [
+        "libbatterymonitor",
+        "android.hardware.health@1.0-convert",
+    ],
+}
+
+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: ["HealthService.cpp"],
+
+    cflags: ["-DHEALTH_INSTANCE_NAME=\"default\""],
+
+    static_libs: [
+        "android.hardware.health@2.0-impl",
+        "android.hardware.health@1.0-convert",
+        "libbatterymonitor",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "liblog",
+        "libutils",
+        "android.hardware.health@2.0",
+    ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..8c3dcfd 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,14 +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
diff --git a/healthd/Health.cpp b/healthd/Health.cpp
new file mode 100644
index 0000000..ec05398
--- /dev/null
+++ b/healthd/Health.cpp
@@ -0,0 +1,172 @@
+#define LOG_TAG "Health"
+#include <android-base/logging.h>
+
+#include <health2/Health.h>
+
+#include <hidl/HidlTransportSupport.h>
+
+extern void healthd_battery_update_internal(bool);
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+Health::Health(struct healthd_config* c) {
+    battery_monitor_ = std::make_unique<BatteryMonitor>();
+    battery_monitor_->init(c);
+}
+
+// Methods from IHealth follow.
+Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) {
+    if (callback == nullptr) {
+        return Result::SUCCESS;
+    }
+
+    {
+        std::lock_guard<std::mutex> _lock(callbacks_lock_);
+        callbacks_.push_back(callback);
+        // unlock
+    }
+
+    auto linkRet = callback->linkToDeath(this, 0u /* cookie */);
+    if (!linkRet.withDefault(false)) {
+        LOG(WARNING) << __func__ << "Cannot link to death: "
+                     << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());
+        // ignore the error
+    }
+
+    return update();
+}
+
+bool Health::unregisterCallbackInternal(const sp<IBase>& callback) {
+    if (callback == nullptr) return false;
+
+    bool removed = false;
+    std::lock_guard<std::mutex> _lock(callbacks_lock_);
+    for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+        if (interfacesEqual(*it, callback)) {
+            it = callbacks_.erase(it);
+            removed = true;
+        } else {
+            ++it;
+        }
+    }
+    (void)callback->unlinkToDeath(this).isOk();  // ignore errors
+    return removed;
+}
+
+Return<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) {
+    return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
+}
+
+template<typename T>
+void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue,
+                 const std::function<void(Result, T)>& callback) {
+    struct BatteryProperty prop;
+    T ret = defaultValue;
+    Result result = Result::SUCCESS;
+    status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+    if (err != OK) {
+        LOG(DEBUG) << "getProperty(" << id << ")" << " fails: (" << err << ") " << strerror(-err);
+    } else {
+        ret = static_cast<T>(prop.valueInt64);
+    }
+    switch (err) {
+        case OK:             result = Result::SUCCESS; break;
+        case NAME_NOT_FOUND: result = Result::NOT_SUPPORTED; break;
+        default:             result = Result::UNKNOWN; break;
+    }
+    callback(result, static_cast<T>(ret));
+}
+
+Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb);
+    return Void();
+}
+
+Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb);
+    return Void();
+}
+
+Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb);
+    return Void();
+}
+
+Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb);
+    return Void();
+}
+
+Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb);
+    return Void();
+}
+
+Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
+    getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb);
+    return Void();
+}
+
+
+Return<Result> Health::update() {
+    if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
+        LOG(WARNING) << "health@2.0: update: not initialized. "
+                     << "update() should not be called in charger / recovery.";
+        return Result::UNKNOWN;
+    }
+
+    // Retrieve all information and call healthd_mode_ops->battery_update, which calls
+    // updateAndNotify.
+    bool chargerOnline = battery_monitor_->update();
+
+    // adjust uevent / wakealarm periods
+    healthd_battery_update_internal(chargerOnline);
+
+    return Result::SUCCESS;
+}
+
+void Health::updateAndNotify(HealthInfo* info) {
+    // update 2.0 specific fields
+    struct BatteryProperty prop;
+    if (battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop) == OK)
+        info->batteryCurrentAverage = static_cast<int32_t>(prop.valueInt64);
+    if (battery_monitor_->getProperty(BATTERY_PROP_CAPACITY, &prop) == OK)
+        info->batteryCapacity = static_cast<int32_t>(prop.valueInt64);
+    if (battery_monitor_->getProperty(BATTERY_PROP_ENERGY_COUNTER, &prop) == OK)
+        info->energyCounter = prop.valueInt64;
+
+    std::lock_guard<std::mutex> _lock(callbacks_lock_);
+    for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+        auto ret = (*it)->healthInfoChanged(*info);
+        if (!ret.isOk() && ret.isDeadObject()) {
+            it = callbacks_.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
+    if (handle != nullptr && handle->numFds >= 1) {
+        int fd = handle->data[0];
+        battery_monitor_->dumpState(fd);
+        fsync(fd);
+    }
+    return Void();
+}
+
+void Health::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {
+    (void)unregisterCallbackInternal(who.promote());
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace health
+}  // namespace hardware
+}  // namespace android
diff --git a/healthd/HealthService.cpp b/healthd/HealthService.cpp
new file mode 100644
index 0000000..29a29ed
--- /dev/null
+++ b/healthd/HealthService.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+// This is a reference implementation for health@2.0 HAL. A vendor
+// can write its own HealthService.cpp with customized init and update functions.
+
+#define LOG_TAG "health@2.0/" HEALTH_INSTANCE_NAME
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::hardware::IPCThreadState;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::implementation::Health;
+
+// see healthd_common.cpp
+android::sp<IHealth> gHealth;
+
+static int gBinderFd;
+
+extern int healthd_main(void);
+
+static void binder_event(uint32_t /*epevents*/) {
+    IPCThreadState::self()->handlePolledCommands();
+}
+
+// TODO(b/67463967): healthd_board_* functions should be removed in health@2.0
+int healthd_board_battery_update(struct android::BatteryProperties*)
+{
+    // return 0 to log periodic polled battery status to kernel log
+    return 0;
+}
+
+void healthd_mode_service_2_0_init(struct healthd_config* config) {
+    LOG(INFO) << LOG_TAG << " Hal is starting up...";
+
+    // 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.
+
+    configureRpcThreadpool(1, false /* callerWillJoin */);
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+    IPCThreadState::self()->setupPolling(&gBinderFd);
+
+    if (gBinderFd >= 0) {
+        if (healthd_register_event(gBinderFd, binder_event))
+            LOG(ERROR) << LOG_TAG << ": Register for binder events failed";
+    }
+
+    gHealth = new ::android::hardware::health::V2_0::implementation::Health(config);
+    CHECK_EQ(gHealth->registerAsService(HEALTH_INSTANCE_NAME), android::OK)
+        << LOG_TAG << ": Failed to register HAL";
+
+    LOG(INFO) << LOG_TAG << ": Hal init done";
+}
+
+int healthd_mode_service_2_0_preparetowait(void) {
+    IPCThreadState::self()->flushCommands();
+    return -1;
+}
+
+void healthd_mode_service_2_0_heartbeat(void) {
+    // noop
+}
+
+void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
+
+    // Implementation-defined update logic goes here. An implementation
+    // can make modifications to prop before broadcasting it to all callbacks.
+
+    HealthInfo info{};
+    convertToHealthInfo(prop, info.legacy);
+    static_cast<Health*>(gHealth.get())->updateAndNotify(&info);
+}
+
+static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
+    .init = healthd_mode_service_2_0_init,
+    .preparetowait = healthd_mode_service_2_0_preparetowait,
+    .heartbeat = healthd_mode_service_2_0_heartbeat,
+    .battery_update = healthd_mode_service_2_0_battery_update,
+};
+
+int main() {
+    healthd_mode_ops = &healthd_mode_service_2_0_ops;
+    LOG(INFO) << LOG_TAG << ": Hal starting main loop...";
+    return healthd_main();
+}
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/healthd_common.cpp b/healthd/healthd_common.cpp
index 6599919..19e600f 100644
--- a/healthd/healthd_common.cpp
+++ b/healthd/healthd_common.cpp
@@ -33,6 +33,10 @@
 #include <sys/timerfd.h>
 #include <utils/Errors.h>
 
+#ifdef HEALTHD_USE_HEALTH_2_0
+#include <health2/Health.h>
+#endif
+
 using namespace android;
 
 #ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
@@ -84,9 +88,13 @@
 
 static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
 
-static BatteryMonitor* gBatteryMonitor;
+#ifndef HEALTHD_USE_HEALTH_2_0
+static BatteryMonitor* gBatteryMonitor = nullptr;
+#else
+extern sp<::android::hardware::health::V2_0::IHealth> gHealth;
+#endif
 
-struct healthd_mode_ops *healthd_mode_ops;
+struct healthd_mode_ops *healthd_mode_ops = nullptr;
 
 int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
     struct epoll_event ev;
@@ -127,17 +135,87 @@
         KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
 }
 
+#ifdef HEALTHD_USE_HEALTH_2_0
+status_t convertStatus(android::hardware::health::V2_0::Result r) {
+    using android::hardware::health::V2_0::Result;
+    switch(r) {
+        case Result::SUCCESS:       return OK;
+        case Result::NOT_SUPPORTED: return BAD_VALUE;
+        case Result::NOT_FOUND:     return NAME_NOT_FOUND;
+        case Result::CALLBACK_DIED: return DEAD_OBJECT;
+        case Result::UNKNOWN: // fallthrough
+        default:
+            return UNKNOWN_ERROR;
+    }
+}
+#endif
+
 status_t healthd_get_property(int id, struct BatteryProperty *val) {
+#ifndef HEALTHD_USE_HEALTH_2_0
     return gBatteryMonitor->getProperty(id, val);
+#else
+    using android::hardware::health::V1_0::BatteryStatus;
+    using android::hardware::health::V2_0::Result;
+    val->valueInt64 = INT64_MIN;
+    status_t err = UNKNOWN_ERROR;
+    switch (id) {
+        case BATTERY_PROP_CHARGE_COUNTER: {
+            gHealth->getChargeCounter([&](Result r, int32_t v) {
+                err = convertStatus(r);
+                val->valueInt64 = v;
+            });
+            break;
+        }
+        case BATTERY_PROP_CURRENT_NOW: {
+            gHealth->getCurrentNow([&](Result r, int32_t v) {
+                err = convertStatus(r);
+                val->valueInt64 = v;
+            });
+            break;
+        }
+        case BATTERY_PROP_CURRENT_AVG: {
+            gHealth->getCurrentAverage([&](Result r, int32_t v) {
+                err = convertStatus(r);
+                val->valueInt64 = v;
+            });
+            break;
+        }
+        case BATTERY_PROP_CAPACITY: {
+            gHealth->getCapacity([&](Result r, int32_t v) {
+                err = convertStatus(r);
+                val->valueInt64 = v;
+            });
+            break;
+        }
+        case BATTERY_PROP_ENERGY_COUNTER: {
+            gHealth->getEnergyCounter([&](Result r, int64_t v) {
+                err = convertStatus(r);
+                val->valueInt64 = v;
+            });
+            break;
+        }
+        case BATTERY_PROP_BATTERY_STATUS: {
+            gHealth->getChargeStatus([&](Result r, BatteryStatus v) {
+                err = convertStatus(r);
+                val->valueInt64 = static_cast<int64_t>(v);
+            });
+            break;
+        }
+        default: {
+            err = BAD_VALUE;
+            break;
+        }
+    }
+    return err;
+#endif
 }
 
-void healthd_battery_update(void) {
+void healthd_battery_update_internal(bool charger_online) {
     // 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;
+    int new_wake_interval = charger_online ? 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);
@@ -155,8 +233,25 @@
                 -1 : healthd_config.periodic_chores_interval_fast * 1000;
 }
 
+void healthd_battery_update(void) {
+#ifndef HEALTHD_USE_HEALTH_2_0
+    healthd_battery_update_internal(gBatteryMonitor->update());
+#else
+    gHealth->update();
+#endif
+}
+
 void healthd_dump_battery_state(int fd) {
+#ifndef HEALTHD_USE_HEALTH_2_0
     gBatteryMonitor->dumpState(fd);
+#else
+    native_handle_t* nativeHandle = native_handle_create(1, 0);
+    nativeHandle->data[0] = fd;
+    ::android::hardware::hidl_handle handle;
+    handle.setTo(nativeHandle, true /* shouldOwn */);
+    gHealth->debug(handle, {} /* options */);
+#endif
+
     fsync(fd);
 }
 
@@ -273,12 +368,21 @@
         return -1;
     }
 
+#ifndef HEALTHD_USE_HEALTH_2_0
     healthd_board_init(&healthd_config);
+#else
+    // healthd_board_* functions are removed in health@2.0
+#endif
+
     healthd_mode_ops->init(&healthd_config);
     wakealarm_init();
     uevent_init();
+
+#ifndef HEALTHD_USE_HEALTH_2_0
     gBatteryMonitor = new BatteryMonitor();
     gBatteryMonitor->init(&healthd_config);
+#endif
+
     return 0;
 }
 
diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h
new file mode 100644
index 0000000..d390b92
--- /dev/null
+++ b/healthd/include/health2/Health.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/1.0/types.h>
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthd/BatteryMonitor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+using V1_0::BatteryStatus;
+
+using ::android::hidl::base::V1_0::IBase;
+
+struct Health : public IHealth, hidl_death_recipient {
+  public:
+    Health(struct healthd_config* c);
+
+    // TODO(b/62229583): clean up and hide these functions.
+    void updateAndNotify(HealthInfo* info);
+
+    // Methods from IHealth follow.
+    Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override;
+    Return<Result> unregisterCallback(const sp<IHealthInfoCallback>& callback) override;
+    Return<Result> update() override;
+    Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
+    Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
+    Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
+    Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
+    Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
+    Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
+
+    // Methods from ::android::hidl::base::V1_0::IBase follow.
+    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+    void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override;
+
+  private:
+    std::mutex callbacks_lock_;
+    std::vector<sp<IHealthInfoCallback>> callbacks_;
+    std::unique_ptr<BatteryMonitor> battery_monitor_;
+
+    bool unregisterCallbackInternal(const sp<IBase>& cb);
+
+};
+
+}  // namespace implementation
+}  // namespace V2_0
+}  // namespace health
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8865a7d..f9067cc 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>
 
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index d01708d..3813e6e 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -95,6 +95,8 @@
                           size_t len);
 int __android_log_bswrite(int32_t tag, const char* payload);
 
+int __android_log_stats_bwrite(int32_t tag, const void* payload, size_t len);
+
 #define android_bWriteLog(tag, payload, len) \
   __android_log_bwrite(tag, payload, len)
 #define android_btWriteLog(tag, type, payload, len) \
diff --git a/liblog/include/log/log_id.h b/liblog/include/log/log_id.h
index 7bfa277..c44f5a2 100644
--- a/liblog/include/log/log_id.h
+++ b/liblog/include/log/log_id.h
@@ -31,8 +31,9 @@
   LOG_ID_EVENTS = 2,
   LOG_ID_SYSTEM = 3,
   LOG_ID_CRASH = 4,
-  LOG_ID_SECURITY = 5,
-  LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+  LOG_ID_STATS = 5,
+  LOG_ID_SECURITY = 6,
+  LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
 
   LOG_ID_MAX
 } log_id_t;
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 59ea5ef..a59cb87 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -301,7 +301,7 @@
   const char* msg;
   ssize_t len;
 
-  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+  if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY) && (id != LOG_ID_STATS)) {
     return -EINVAL;
   }
 
@@ -326,7 +326,9 @@
   }
   return (id == LOG_ID_EVENTS)
              ? __android_log_bwrite(context->tag, msg, len)
-             : __android_log_security_bwrite(context->tag, msg, len);
+             : ((id == LOG_ID_STATS)
+                    ? __android_log_stats_bwrite(context->tag, msg, len)
+                    : __android_log_security_bwrite(context->tag, msg, len));
 }
 
 LIBLOG_ABI_PRIVATE int android_log_write_list_buffer(android_log_context ctx,
diff --git a/liblog/logger_name.c b/liblog/logger_name.c
index a5a83e0..979b82d 100644
--- a/liblog/logger_name.c
+++ b/liblog/logger_name.c
@@ -28,6 +28,7 @@
   [LOG_ID_EVENTS] = "events",
   [LOG_ID_SYSTEM] = "system",
   [LOG_ID_CRASH] = "crash",
+  [LOG_ID_STATS] = "stats",
   [LOG_ID_SECURITY] = "security",
   [LOG_ID_KERNEL] = "kernel",
   /* clang-format on */
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index 84feb20..589ce84 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -546,6 +546,19 @@
   return write_to_log(LOG_ID_EVENTS, vec, 2);
 }
 
+LIBLOG_ABI_PUBLIC int __android_log_stats_bwrite(int32_t tag,
+                                                 const void* payload,
+                                                 size_t len) {
+  struct iovec vec[2];
+
+  vec[0].iov_base = &tag;
+  vec[0].iov_len = sizeof(tag);
+  vec[1].iov_base = (void*)payload;
+  vec[1].iov_len = len;
+
+  return write_to_log(LOG_ID_STATS, vec, 2);
+}
+
 LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
                                                     const void* payload,
                                                     size_t len) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 3d56472..5ef220c 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -1126,8 +1126,9 @@
                     }
                     if (found) continue;
 
-                    bool binary =
-                        !strcmp(name, "events") || !strcmp(name, "security");
+                    bool binary = !strcmp(name, "events") ||
+                                  !strcmp(name, "security") ||
+                                  !strcmp(name, "stats");
                     log_device_t* d = new log_device_t(name, binary);
 
                     if (dev) {
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index cfcbaa5..86572f1 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -374,7 +374,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/logd/LogListener.cpp b/logd/LogListener.cpp
index d2df68e..0bd4008 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -101,6 +101,14 @@
         return false;
     }
 
+    if (header->id == LOG_ID_STATS) {
+        // Only accept logging from *ManagerService in system server
+        // Will add more later as we see fit.
+        if (cred->uid != AID_SYSTEM && cred->gid != AID_SYSTEM) {
+            return false;
+        }
+    }
+
     // Check credential validity, acquire corrected details if not supplied.
     if (cred->pid == 0) {
         cred->pid = logbuf ? logbuf->tidToPid(header->tid)
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 2c73056..394f0e1 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -30,10 +30,10 @@
 namespace.default.search.paths = /system/${LIB}
 # /vendor/app, /vendor/framework were added since libart should be able to dlopen
 # the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
+namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
 
 namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
 
 ###############################################################################
 # "sphal" namespace
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..6aef8c8
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,89 @@
+/*
+ * 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: [
+        "libbase",
+        "libbatteryservice",
+        "libbinder",
+        "libcutils",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libsysutils",
+        "libutils",
+        "libz",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter"
+    ],
+}
+
+cc_library_static {
+    name: "libstoraged",
+
+    defaults: ["storaged_defaults"],
+
+    srcs: [
+        "storaged.cpp",
+        "storaged_diskstats.cpp",
+        "storaged_info.cpp",
+        "storaged_service.cpp",
+        "storaged_utils.cpp",
+        "storaged_uid_monitor.cpp",
+        "storaged.proto",
+    ],
+
+    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: ["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: ["libstoraged"],
+}
\ No newline at end of file
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/include/storaged.h b/storaged/include/storaged.h
index fa68406..f5c78f9 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -29,23 +29,9 @@
 #include <batteryservice/IBatteryPropertiesListener.h>
 #include <batteryservice/IBatteryPropertiesRegistrar.h>
 
-#include "storaged_info.h"
-#include "storaged_uid_monitor.h"
-
-using namespace android;
-
 #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 SECTOR_SIZE ( 512 )
@@ -55,184 +41,23 @@
 #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"
 
-    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_FLUSH_PROTO (3600)
 
 // UID IO threshold in bytes
 #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
@@ -241,8 +66,7 @@
     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
 };
 
@@ -251,12 +75,14 @@
 private:
     time_t mTimer;
     storaged_config mConfig;
-    disk_stats_publisher mDiskStats;
     disk_stats_monitor mDsm;
     uid_monitor mUidm;
     time_t mStarttime;
     sp<IBatteryPropertiesRegistrar> battery_properties;
-    std::unique_ptr<storage_info_t> storage_info;
+    unique_ptr<storage_info_t> storage_info;
+    static const uint32_t crc_init;
+    static const string proto_file;
+    storaged_proto::StoragedProto proto;
 public:
     storaged_t(void);
     ~storaged_t() {}
@@ -270,12 +96,18 @@
         return mStarttime;
     }
 
-    std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+    unordered_map<uint32_t, struct uid_info> get_uids(void) {
         return mUidm.get_uid_io_stats();
     }
-    std::map<uint64_t, struct uid_records> get_uid_records(
+
+    vector<vector<uint32_t>> get_perf_history(void) {
+        return storage_info->get_perf_history();
+    }
+
+    map<uint64_t, struct uid_records> get_uid_records(
             double hours, uint64_t threshold, bool force_report) {
-        return mUidm.dump(hours, threshold, force_report);
+        return mUidm.dump(hours, threshold, force_report,
+                          proto.mutable_uid_io_usage());
     }
     void update_uid_io_interval(int interval) {
         if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
@@ -288,6 +120,9 @@
     void binderDied(const wp<IBinder>& who);
 
     void report_storage_info();
+
+    void load_proto();
+    void flush_proto();
 };
 
 // Eventlog tag
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
new file mode 100644
index 0000000..ff030f6
--- /dev/null
+++ b/storaged/include/storaged_diskstats.h
@@ -0,0 +1,190 @@
+/*
+ * 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>
+
+// 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;
+
+    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) :
+        DISK_STATS_PATH(access(MMC_DISK_STATS_PATH, R_OK) ?
+                            (access(SDA_DISK_STATS_PATH, R_OK) ?
+                                nullptr :
+                                SDA_DISK_STATS_PATH) :
+                            MMC_DISK_STATS_PATH),
+        mPrevious(), mAccumulate(), mAccumulate_pub(),
+        mStall(false), mValid(false),
+        mWindow(window_size), mSigma(sigma),
+        mMean(), mStd() {}
+    bool enabled() {
+        return DISK_STATS_PATH != nullptr;
+    }
+    void update(void);
+    void publish(void);
+};
+
+#endif /* _STORAGED_DISKSTATS_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 7d04c7a..8b07862 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -19,10 +19,17 @@
 
 #include <string.h>
 
+#include <chrono>
+
+#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 chrono;
+using namespace storaged_proto;
 
 class storage_info_t {
 protected:
@@ -36,16 +43,36 @@
     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;
+    sem_t si_lock;
 
     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) {
+            sem_init(&si_lock, 0, 1);
+            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() {}
+    virtual ~storage_info_t() { sem_destroy(&si_lock); }
     virtual void report() {};
-    void refresh();
+    void init(const IOPerfHistory& perf_history);
+    void refresh(IOPerfHistory* perf_history);
+    void update_perf_history(uint32_t bw,
+                             const time_point<system_clock>& tp);
+    vector<vector<uint32_t>> get_perf_history(void);
 };
 
 class emmc_info_t : public storage_info_t {
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index a8ddf4c..b7fe5b8 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -24,16 +24,19 @@
 
 #include "storaged.h"
 
+using namespace std;
 using namespace android;
 
 // Interface
 class IStoraged : public IInterface {
 public:
     enum {
-        DUMPUIDS  = IBinder::FIRST_CALL_TRANSACTION,
+        DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
+        DUMPPERF,
     };
     // Request the service to run the test function
-    virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+    virtual vector<struct uid_info> dump_uids(const char* option) = 0;
+    virtual vector<vector<uint32_t>> dump_perf_history(const char* option) = 0;
 
     DECLARE_META_INTERFACE(Storaged);
 };
@@ -42,7 +45,8 @@
 class BpStoraged : public BpInterface<IStoraged> {
 public:
     BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    virtual vector<struct uid_info> dump_uids(const char* option);
+    virtual vector<vector<uint32_t>> dump_perf_history(const char* option);
 };
 
 // Server
@@ -51,7 +55,8 @@
 };
 
 class Storaged : public BnStoraged {
-    virtual std::vector<struct uid_info> dump_uids(const char* option);
+    virtual vector<struct uid_info> dump_uids(const char* option);
+    virtual vector<vector<uint32_t>> dump_perf_history(const char* option);
     virtual status_t dump(int fd, const Vector<String16>& args);
 };
 
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..d2c7105 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -23,6 +23,10 @@
 #include <unordered_map>
 #include <vector>
 
+#include "storaged.pb.h"
+
+using namespace storaged_proto;
+
 enum uid_stat_t {
     FOREGROUND = 0,
     BACKGROUND = 1,
@@ -41,7 +45,7 @@
     IO_TYPES = 2
 };
 
-struct uid_io_stats {
+struct io_stats {
     uint64_t rchar;                 // characters read
     uint64_t wchar;                 // characters written
     uint64_t read_bytes;            // bytes read (from storage layer)
@@ -49,14 +53,30 @@
     uint64_t fsync;                 // number of fsync syscalls
 };
 
+struct task_info {
+    std::string comm;
+    pid_t pid;
+    struct io_stats io[UID_STATS];
+    bool parse_task_io_stats(std::string&& s);
+};
+
 struct uid_info {
     uint32_t uid;                   // user id
     std::string name;               // package name
-    struct uid_io_stats io[UID_STATS];    // [0]:foreground [1]:background
+    struct io_stats io[UID_STATS];    // [0]:foreground [1]:background
+    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+    bool parse_uid_io_stats(std::string&& s);
+};
+
+struct io_usage {
+    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    bool is_zero() const;
 };
 
 struct uid_io_usage {
-    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+    struct io_usage uid_ios;
+    // mapped from task comm to task io usage
+    std::map<std::string, struct io_usage> task_ios;
 };
 
 struct uid_record {
@@ -69,6 +89,18 @@
     std::vector<struct uid_record> entries;
 };
 
+class lock_t {
+    sem_t* mSem;
+public:
+    lock_t(sem_t* sem) {
+        mSem = sem;
+        sem_wait(mSem);
+    }
+    ~lock_t() {
+        sem_post(mSem);
+    }
+};
+
 class uid_monitor {
 private:
     // last dump from /proc/uid_io/stats, uid -> uid_info
@@ -76,13 +108,15 @@
     // current io usage for next report, app name -> uid_io_usage
     std::unordered_map<std::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;
+    std::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;
     // 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();
@@ -90,21 +124,27 @@
     void add_records_locked(uint64_t curr_ts);
     // updates curr_io_stats and set last_uid_io_stats
     void update_curr_io_stats_locked();
+    // restores io_history from protobuf
+    void load_uid_io_proto(const UidIOUsage& proto);
+    // writes io_history to protobuf
+    void update_uid_io_proto(UidIOUsage* proto);
 
 public:
     uid_monitor();
     ~uid_monitor();
     // called by storaged main thread
-    void init(charger_stat_t stat);
+    void init(charger_stat_t stat, const UidIOUsage& proto);
     // called by storaged -u
     std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
     // called by dumpsys
     std::map<uint64_t, struct uid_records> dump(
-        double hours, uint64_t threshold, bool force_report);
+        double hours, uint64_t threshold, bool force_report,
+        UidIOUsage* uid_io_proto);
     // 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(UidIOUsage* proto);
 };
 
 #endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..3b595b7 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -27,18 +27,14 @@
 // 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);
 
 // Logging
-void log_console_running_uids_info(std::vector<struct uid_info> uids);
+void log_console_running_uids_info(const std::vector<struct uid_info>& uids, bool flag_dump_task);
+void log_console_perf_history(const vector<vector<uint32_t>>& 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/main.cpp b/storaged/main.cpp
index 6b82904..adc550a 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -42,12 +42,15 @@
 #include <storaged_service.h>
 #include <storaged_utils.h>
 
+using namespace std;
+
 sp<storaged_t> storaged;
 
 // Function of storaged's main thread
 void* storaged_main(void* /* unused */) {
     storaged = new storaged_t();
 
+    storaged->load_proto();
     storaged->init_battery_service();
     storaged->report_storage_info();
 
@@ -60,16 +63,20 @@
     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 (;;) {
@@ -78,19 +85,27 @@
             {"start",       no_argument,        0, 's'},
             {"kill",        no_argument,        0, 'k'},
             {"uid",         no_argument,        0, 'u'},
+            {"task",        no_argument,        0, 't'},
+            {"perf",        no_argument,        0, 'p'},
             {"help",        no_argument,        0, 'h'}
         };
-        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        opt = getopt_long(argc, argv, ":skhutp", long_options, &opt_idx);
         if (opt == -1) {
             break;
         }
 
         switch (opt) {
         case 's':
-            flag_main_service = 1;
+            flag_main_service = true;
             break;
         case 'u':
-            flag_dump_uid = 1;
+            flag_dump_uid = true;
+            break;
+        case 't':
+            flag_dump_task = true;
+            break;
+        case 'p':
+            flag_dump_perf = true;
             break;
         case 'h':
             help_message();
@@ -104,10 +119,10 @@
     }
 
     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;
@@ -130,23 +145,31 @@
         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<IStoraged> storaged_service = get_storaged_service();
+    if (storaged_service == NULL) {
+        fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+        return -1;
+    }
 
+    if (flag_dump_uid || flag_dump_task) {
+        vector<struct uid_info> res = storaged_service->dump_uids(NULL);
         if (res.size() == 0) {
             fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
             return 0;
         }
 
         sort_running_uids_info(res);
-        log_console_running_uids_info(res);
+        log_console_running_uids_info(res, flag_dump_task);
+    }
 
-        return 0;
+    if (flag_dump_perf) {
+        vector<vector<uint32_t>> res = storaged_service->dump_perf_history(NULL);
+        if (res.size() == 0) {
+            fprintf(stderr, "I/O perf history is empty.\n");
+            return 0;
+        }
+
+        log_console_perf_history(res);
     }
 
     return 0;
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..1794fb5 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -17,8 +17,15 @@
 #define LOG_TAG "storaged"
 
 #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/logging.h>
 #include <batteryservice/BatteryServiceConstants.h>
@@ -31,124 +38,22 @@
 #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;
+
+namespace {
+
+const uint32_t benchmark_unit_size = 16 * 1024;  // 16KB
+
 }
 
-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;
-    }
-}
+const uint32_t storaged_t::crc_init = 0x5108A4ED; /* STORAGED */
+const std::string storaged_t::proto_file =
+    "/data/misc/storaged/storaged.proto";
 
-/* 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* 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);
-
-        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<IBatteryPropertiesRegistrar> get_battery_properties_service() {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) return NULL;
 
@@ -161,7 +66,7 @@
     return battery_properties;
 }
 
-static inline charger_stat_t is_charger_on(int64_t prop) {
+inline charger_stat_t is_charger_on(int64_t prop) {
     return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
         CHARGER_ON : CHARGER_OFF;
 }
@@ -171,7 +76,7 @@
 }
 
 void storaged_t::init_battery_service() {
-    if (!mConfig.proc_uid_io_available)
+    if (!mUidm.enabled())
         return;
 
     battery_properties = get_battery_properties_service();
@@ -182,7 +87,7 @@
 
     struct BatteryProperty val;
     battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
-    mUidm.init(is_charger_on(val.valueInt64));
+    mUidm.init(is_charger_on(val.valueInt64), proto.uid_io_usage());
 
     // register listener after init uid_monitor
     battery_properties->registerListener(this);
@@ -201,49 +106,138 @@
 }
 
 void storaged_t::report_storage_info() {
+    storage_info->init(proto.perf_history());
     storage_info->report();
 }
 
 /* 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);
+
+    mConfig.periodic_chores_interval_flush_proto =
+        property_get_int32("ro.storaged.flush_proto.interval",
+                           DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
 
     storage_info.reset(storage_info_t::get_storage_info());
 
     mStarttime = time(NULL);
+    mTimer = 0;
+}
+
+void storaged_t::load_proto() {
+    std::ifstream in(proto_file,
+        std::ofstream::in | std::ofstream::binary);
+
+    if (!in.good()) {
+        PLOG_TO(SYSTEM, INFO) << "Open " << proto_file << " failed";
+        return;
+    }
+
+    stringstream ss;
+    ss << in.rdbuf();
+    proto.ParseFromString(ss.str());
+
+    uint32_t crc = proto.crc();
+    proto.set_crc(crc_init);
+    std::string proto_str = proto.SerializeAsString();
+    uint32_t computed_crc = crc32(crc_init,
+        reinterpret_cast<const Bytef*>(proto_str.c_str()),
+        proto_str.size());
+
+    if (crc != computed_crc) {
+        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+        proto.Clear();
+    }
+}
+
+void storaged_t::flush_proto() {
+    proto.set_version(1);
+    proto.set_crc(crc_init);
+    while (proto.ByteSize() < 128 * 1024) {
+        proto.add_padding(0xFEEDBABE);
+    }
+    std::string proto_str = proto.SerializeAsString();
+    proto.set_crc(crc32(crc_init,
+        reinterpret_cast<const Bytef*>(proto_str.c_str()),
+        proto_str.size()));
+    proto_str = proto.SerializeAsString();
+
+    const char* data = proto_str.data();
+    uint32_t size = proto_str.size();
+    ssize_t ret;
+    time_point<steady_clock> start, end;
+
+    std::string tmp_file = proto_file + "_tmp";
+    unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
+                 O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+                 S_IRUSR | S_IWUSR)));
+    if (fd == -1) {
+        PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+        return;
+    }
+
+    uint32_t benchmark_size = 0;
+    uint64_t benchmark_time_ns = 0;
+    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 (size != proto_str.size() && ret == benchmark_unit_size) {
+            benchmark_size += benchmark_unit_size;
+            benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
+        }
+        size -= ret;
+        data += ret;
+    }
+
+    if (benchmark_size) {
+        int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
+        storage_info->update_perf_history(perf, system_clock::now());
+    }
+
+    fd.reset(-1);
+    /* Atomically replace existing proto file to reduce chance of data loss. */
+    rename(tmp_file.c_str(), proto_file.c_str());
 }
 
 void storaged_t::event(void) {
-    if (mConfig.diskstats_available) {
-        mDiskStats.update();
+    if (mDsm.enabled()) {
         mDsm.update();
-        storage_info->refresh();
-        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
-            mDiskStats.publish();
+        if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
+            mDsm.publish();
         }
     }
 
-    if (mConfig.proc_uid_io_available && mTimer &&
-            (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
-         mUidm.report();
+    if (mUidm.enabled() &&
+        !(mTimer % mConfig.periodic_chores_interval_uid_io)) {
+        mUidm.report(proto.mutable_uid_io_usage());
+    }
+
+    storage_info->refresh(proto.mutable_perf_history());
+
+    if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
+        flush_proto();
     }
 
     mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..05c1f91
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,59 @@
+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 IOUsage uid_io = 2;
+  repeated TaskIOUsage task_io = 3;
+}
+
+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;
+  repeated uint32 padding = 5;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index a24c7fb..bd4022b 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -1,7 +1,11 @@
+on post-fs-data
+    mkdir /data/misc/storaged 0700 root root
+    restorecon /data/misc/storaged
+
 service storaged /system/bin/storaged
     class main
     priority 10
     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..0604e0a
--- /dev/null
+++ b/storaged/storaged_diskstats.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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 {
+
+#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 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 = {};
+
+    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 (!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..c5552f6 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,12 @@
 #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;
 
 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,12 +44,16 @@
 
 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;
 }
 
+} // namespace
+
 storage_info_t* storage_info_t::get_storage_info()
 {
     if (FileExists(emmc_info_t::emmc_sysfs) ||
@@ -57,7 +66,37 @@
     return new storage_info_t;
 }
 
-void storage_info_t::refresh()
+void storage_info_t::init(const IOPerfHistory& perf_history)
+{
+    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 += 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 +106,24 @@
 
     userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
     userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
+
+    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+
+    perf_history->Clear();
+    perf_history->set_day_start_sec(
+        duration_cast<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 +133,80 @@
         << LOG_ID_EVENTS;
 }
 
+void storage_info_t::update_perf_history(uint32_t bw,
+                                         const time_point<system_clock>& tp)
+{
+    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+
+    if (tp > day_start_tp &&
+        duration_cast<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 - seconds(duration_cast<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<vector<uint32_t>> storage_info_t::get_perf_history()
+{
+    unique_ptr<lock_t> lock(new lock_t(&si_lock));
+
+    vector<vector<uint32_t>> ret(3);
+
+    ret[0].resize(recent_perf.size());
+    for (size_t i = 0; i < recent_perf.size(); i++) {
+        int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
+        ret[0][i] = recent_perf[idx];
+    }
+
+    ret[1].resize(daily_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[1][i] = daily_perf[idx];
+    }
+
+    ret[2].resize(weekly_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[2][i] = weekly_perf[idx];
+    }
+
+    return ret;
+}
+
 void emmc_info_t::report()
 {
     if (!report_sysfs() && !report_debugfs())
@@ -121,6 +252,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 +261,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;
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..e4ba380 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>
@@ -31,25 +32,61 @@
 #include <storaged.h>
 #include <storaged_service.h>
 
+using namespace std;
 using namespace android::base;
 
 extern sp<storaged_t> storaged;
 
-std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
+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);
+    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));
+
+        uint32_t tasks_size = reply.readInt32();
+        for (uint32_t i = 0; i < tasks_size; i++) {
+            struct task_info task;
+            task.pid = reply.readInt32();
+            task.comm = reply.readCString();
+            reply.read(&task.io, sizeof(task.io));
+            uid.tasks[task.pid] = task;
+        }
     }
     return res;
 }
+
+vector<vector<uint32_t>> BpStoraged::dump_perf_history(const char* /*option*/) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+    remote()->transact(DUMPPERF, data, &reply);
+
+    vector<vector<uint32_t>> res(3);
+    uint32_t size = reply.readUint32();
+    res[0].resize(size);
+    for (uint32_t i = 0; i < size; i++) {
+        res[0][i] = reply.readUint32();
+    }
+    size = reply.readUint32();
+    res[1].resize(size);
+    for (uint32_t i = 0; i < size; i++) {
+        res[1][i] = reply.readUint32();
+    }
+    size = reply.readUint32();
+    res[2].resize(size);
+    for (uint32_t i = 0; i < size; i++) {
+        res[2][i] = reply.readUint32();
+    }
+    return res;
+}
+
 IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
 
 status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
@@ -57,24 +94,50 @@
         case DUMPUIDS: {
                 if (!data.checkInterface(this))
                     return BAD_TYPE;
-                std::vector<struct uid_info> res = dump_uids(NULL);
+                vector<struct uid_info> res = dump_uids(NULL);
                 reply->writeInt32(res.size());
-                for (auto uid : res) {
+                for (const auto& uid : res) {
                     reply->writeInt32(uid.uid);
                     reply->writeCString(uid.name.c_str());
                     reply->write(&uid.io, sizeof(uid.io));
+
+                    reply->writeInt32(uid.tasks.size());
+                    for (const auto& task_it : uid.tasks) {
+                        reply->writeInt32(task_it.first);
+                        reply->writeCString(task_it.second.comm.c_str());
+                        reply->write(&task_it.second.io, sizeof(task_it.second.io));
+                    }
                 }
                 return NO_ERROR;
             }
             break;
+        case DUMPPERF: {
+            if (!data.checkInterface(this))
+                return BAD_TYPE;
+            vector<vector<uint32_t>> res = dump_perf_history(NULL);
+            reply->writeUint32(res[0].size());
+            for (const auto& item : res[0]) {
+                reply->writeUint32(item);
+            }
+            reply->writeUint32(res[1].size());
+            for (const auto& item : res[1]) {
+                reply->writeUint32(item);
+            }
+            reply->writeUint32(res[2].size());
+            for (const auto& item : res[2]) {
+                reply->writeUint32(item);
+            }
+            return NO_ERROR;
+        }
+        break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
 }
 
-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();
+vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
+    vector<struct uid_info> uids_v;
+    unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
 
     for (const auto& it : uids_m) {
         uids_v.push_back(it.second);
@@ -82,6 +145,10 @@
     return uids_v;
 }
 
+vector<vector<uint32_t>> Storaged::dump_perf_history(const char* /* option */) {
+    return storaged->get_perf_history();
+}
+
 status_t Storaged::dump(int fd, const Vector<String16>& args) {
     IPCThreadState* self = IPCThreadState::self();
     const int pid = self->getCallingPid();
@@ -96,6 +163,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,29 +191,52 @@
             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 =
+    const map<uint64_t, struct uid_records>& records =
                 storaged->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",
+            const struct io_usage& uid_usage = record.ios.uid_ios;
+            dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+                    " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\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]);
+                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]);
+            if (debug) {
+                for (const auto& task_it : record.ios.task_ios) {
+                    const struct 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]);
+                }
+            }
         }
     }
 
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index dd8bdd6..9295ff2 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -38,8 +38,14 @@
 using namespace android;
 using namespace android::base;
 using namespace android::content::pm;
+using namespace storaged_proto;
 
-static bool refresh_uid_names;
+namespace {
+
+bool refresh_uid_names;
+const char* UID_IO_STATS_PATH = "/proc/uid_io/stats";
+
+} // namepsace
 
 std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
 {
@@ -47,7 +53,71 @@
     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,6 +149,8 @@
     refresh_uid_names = false;
 }
 
+} // namespace
+
 std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
 {
     std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
@@ -88,7 +160,7 @@
         return uid_io_stats;
     }
 
-    std::vector<std::string> io_stats = Split(buffer, "\n");
+    std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
     struct uid_info u;
     vector<int> uids;
     vector<std::string*> uid_names;
@@ -97,32 +169,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;
+            struct 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 +197,40 @@
     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.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,22 +243,22 @@
       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)
+    double hours, uint64_t threshold, bool force_report, UidIOUsage* uid_io_proto)
 {
     if (force_report) {
-        report();
+        report(uid_io_proto);
     }
 
     std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
@@ -200,19 +270,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);
             }
         }
@@ -253,25 +324,135 @@
         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 struct 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;
+
+            struct 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(UidIOUsage* proto)
 {
     std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
+
+    update_uid_io_proto(proto);
+}
+
+namespace {
+
+void set_io_usage_proto(IOUsage* usage_proto, const struct 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(struct 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(UidIOUsage* uid_io_proto)
+{
+    uid_io_proto->Clear();
+
+    for (const auto& item : io_history) {
+        const uint64_t& end_ts = item.first;
+        const struct uid_records& recs = item.second;
+
+        UidIOItem* item_proto = uid_io_proto->add_uid_io_items();
+        item_proto->set_end_ts(end_ts);
+
+        UidIORecords* recs_proto = item_proto->mutable_records();
+        recs_proto->set_start_ts(recs.start_ts);
+
+        for (const auto& entry : recs.entries) {
+            UidRecord* rec_proto = recs_proto->add_entries();
+            rec_proto->set_uid_name(entry.name);
+
+            IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
+            const struct 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 struct 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::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+{
+    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();
+            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)
@@ -286,14 +467,18 @@
     charger_stat = stat;
 }
 
-void uid_monitor::init(charger_stat_t stat)
+void uid_monitor::init(charger_stat_t stat, const UidIOUsage& proto)
 {
     charger_stat = stat;
+
+    load_uid_io_proto(proto);
+
     start_ts = time(NULL);
     last_uid_io_stats = get_uid_io_stats();
 }
 
 uid_monitor::uid_monitor()
+    : enable(!access(UID_IO_STATS_PATH, R_OK))
 {
     sem_init(&um_lock, 0, 1);
 }
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..fcd2484 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(struct uid_info l, struct uid_info 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;
@@ -182,51 +66,51 @@
 }
 
 // Logging functions
-void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+void log_console_running_uids_info(const std::vector<struct uid_info>& 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 struct 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<vector<uint32_t>>& perf_history) {
+    if (perf_history.size() != 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;
+    std::copy(perf_history[0].begin(), perf_history[0].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("");
+    std::copy(perf_history[1].begin(), perf_history[1].end(),
+              std::ostream_iterator<int>(line, " "));
+    printf("last 7 days   : %s\n", line.str().c_str());
 
+    line.str("");
+    std::copy(perf_history[2].begin(), perf_history[2].end(),
+              std::ostream_iterator<int>(line, " "));
+    printf("last 52 weeks : %s\n", line.str().c_str());
+}
\ No newline at end of file
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..5ae1c91 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>
@@ -30,7 +31,12 @@
 #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;
+
+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 +59,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 +85,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 +99,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 +108,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 +127,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 +179,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 +190,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,6 +208,30 @@
     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) {
     // 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);
@@ -294,14 +316,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 +336,72 @@
         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<seconds>(tp.time_since_epoch());
+        si.update_perf_history((i + 1) * 5, stp);
+    }
+
+    vector<vector<uint32_t>> history = si.get_perf_history();
+    EXPECT_EQ(history.size(), 3UL);
+    EXPECT_EQ(history[0].size(), 4UL);
+    EXPECT_EQ(history[1].size(), 7UL);    // 7 days
+    EXPECT_EQ(history[2].size(), 52UL);   // 52 weeks
+    // last 24 hours
+    EXPECT_EQ(history[0][0], 375UL);
+    EXPECT_EQ(history[0][1], 370UL);
+    EXPECT_EQ(history[0][2], 365UL);
+    EXPECT_EQ(history[0][3], 360UL);
+    // daily average of last 7 days
+    EXPECT_EQ(history[1][0], 347UL);
+    EXPECT_EQ(history[1][1], 325UL);
+    EXPECT_EQ(history[1][2], 300UL);
+    EXPECT_EQ(history[1][3], 275UL);
+    EXPECT_EQ(history[1][4], 250UL);
+    EXPECT_EQ(history[1][5], 227UL);
+    EXPECT_EQ(history[1][6], 205UL);
+    // weekly average of last 52 weeks
+    EXPECT_EQ(history[2][0], 251UL);
+    EXPECT_EQ(history[2][1], 83UL);
+    for (int i = 2; i < 52; i++) {
+        EXPECT_EQ(history[2][i], 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()