Merge "Reland "Remove comments and code about ro.boot.slot"" am: dea5e081ac am: 9bc2a967cf am: 687cb99e96
am: 99de249cf5

Change-Id: I989b0db7107ae47d5facbb6b5ca3905aed077161
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d5b7554..e2e9577 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -70,3 +70,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
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/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 7c22199..1d3e5ea 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -80,7 +80,6 @@
     { 00775, AID_ROOT,         AID_ROOT,         0, "data/preloads" },
     { 00771, AID_SYSTEM,       AID_SYSTEM,       0, "data" },
     { 00755, AID_ROOT,         AID_SYSTEM,       0, "mnt" },
-    { 00755, AID_ROOT,         AID_ROOT,         0, "root" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "sbin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
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/Android.mk b/rootdir/Android.mk
index 3377716..5c1ffb3 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -142,7 +142,7 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    sbin dev proc sys system data oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..25b433c
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,88 @@
+/*
+ * 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_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..bd99361 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -97,22 +97,6 @@
     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:
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..86d3c28 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -41,7 +41,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 +49,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 {
@@ -76,13 +92,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;
+    // protobuf file for io_history
+    static const std::string io_history_proto_file;
 
     // reads from /proc/uid_io/stats
     std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
@@ -90,6 +108,10 @@
     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 file
+    void load_io_history_from_proto();
+    // converts io_history to protobuf and writes to a file
+    void flush_io_history_to_proto();
 
 public:
     uid_monitor();
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..1435707 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -35,7 +35,7 @@
 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_debug_disk_perf(struct disk_perf* perf, const char* type);
 
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 6b82904..49bd916 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -63,13 +63,15 @@
 static 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("  -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;
     int opt;
 
     for (;;) {
@@ -78,19 +80,23 @@
             {"start",       no_argument,        0, 's'},
             {"kill",        no_argument,        0, 'k'},
             {"uid",         no_argument,        0, 'u'},
+            {"task",        no_argument,        0, 't'},
             {"help",        no_argument,        0, 'h'}
         };
-        opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+        opt = getopt_long(argc, argv, ":skdhu0t", 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 'h':
             help_message();
@@ -104,10 +110,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,7 +136,7 @@
         return 0;
     }
 
-    if (flag_dump_uid) {
+    if (flag_dump_uid || flag_dump_task) {
         sp<IStoraged> storaged_service = get_storaged_service();
         if (storaged_service == NULL) {
             fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
@@ -144,7 +150,7 @@
         }
 
         sort_running_uids_info(res);
-        log_console_running_uids_info(res);
+        log_console_running_uids_info(res, flag_dump_task);
 
         return 0;
     }
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..49592eb 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -243,7 +243,7 @@
 
     if (mConfig.proc_uid_io_available && mTimer &&
             (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
-         mUidm.report();
+        mUidm.report();
     }
 
     mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..5222846
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,42 @@
+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 UidIOHistoryProto {
+  optional uint32 crc = 1;
+  repeated UidIOItem items = 2;
+}
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_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..4364c6a 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>
@@ -47,6 +48,15 @@
         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;
 }
@@ -59,10 +69,17 @@
                     return BAD_TYPE;
                 std::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;
             }
@@ -96,6 +113,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,6 +141,10 @@
             force_report = true;
             continue;
         }
+        if (arg == String16("--debug")) {
+            debug = true;
+            continue;
+        }
     }
 
     uint64_t last_ts = 0;
@@ -130,22 +152,41 @@
                 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 std::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..1c98477 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -17,8 +17,11 @@
 #define LOG_TAG "storaged"
 
 #include <stdint.h>
+#include <stdio.h>
 #include <time.h>
+#include <zlib.h>
 
+#include <fstream>
 #include <string>
 #include <unordered_map>
 
@@ -34,12 +37,19 @@
 
 #include "storaged.h"
 #include "storaged_uid_monitor.h"
+#include "system/core/storaged/storaged.pb.h"
 
 using namespace android;
 using namespace android::base;
 using namespace android::content::pm;
+using namespace google::protobuf::io;
+using namespace storaged_proto;
 
 static bool refresh_uid_names;
+static const uint32_t crc_init = 0x5108A4ED; /* STORAGED */
+
+const std::string uid_monitor::io_history_proto_file =
+    "/data/misc/storaged/io_history.proto";
 
 std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
 {
@@ -47,6 +57,66 @@
     return get_uid_io_stats_locked();
 };
 
+/* 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 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, ",");
+    if (fields.size() < 13 ||
+        !ParseInt(fields[2],  &pid) ||
+        !ParseUint(fields[3],  &io[FOREGROUND].rchar) ||
+        !ParseUint(fields[4],  &io[FOREGROUND].wchar) ||
+        !ParseUint(fields[5],  &io[FOREGROUND].read_bytes) ||
+        !ParseUint(fields[6],  &io[FOREGROUND].write_bytes) ||
+        !ParseUint(fields[7],  &io[BACKGROUND].rchar) ||
+        !ParseUint(fields[8],  &io[BACKGROUND].wchar) ||
+        !ParseUint(fields[9],  &io[BACKGROUND].read_bytes) ||
+        !ParseUint(fields[10], &io[BACKGROUND].write_bytes) ||
+        !ParseUint(fields[11], &io[FOREGROUND].fsync) ||
+        !ParseUint(fields[12], &io[BACKGROUND].fsync)) {
+        LOG_TO(SYSTEM, WARNING) << "Invalid I/O stats: \""
+                                << s << "\"";
+        return false;
+    }
+    comm = fields[1];
+    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;
+}
+
 static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
 {
     sp<IServiceManager> sm = defaultServiceManager();
@@ -88,7 +158,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 +167,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;
         }
     }
 
@@ -135,32 +197,34 @@
 
 static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
 
-static inline int records_size(
-    const std::map<uint64_t, struct uid_records>& curr_records)
+static 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;
-
 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,15 +237,15 @@
       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(
@@ -200,19 +264,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,14 +318,38 @@
         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;
@@ -272,6 +361,128 @@
 
     update_curr_io_stats_locked();
     add_records_locked(time(NULL));
+
+    flush_io_history_to_proto();
+}
+
+static 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]);
+}
+
+static 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();
+}
+
+void uid_monitor::flush_io_history_to_proto()
+{
+    UidIOHistoryProto out_proto;
+
+    for (const auto& item : io_history) {
+        const uint64_t& end_ts = item.first;
+        const struct uid_records& recs = item.second;
+
+        UidIOItem* item_proto = out_proto.add_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);
+            }
+        }
+    }
+
+    out_proto.set_crc(crc_init);
+    std::string out_proto_str = out_proto.SerializeAsString();
+    out_proto.set_crc(crc32(crc_init,
+        reinterpret_cast<const Bytef*>(out_proto_str.c_str()),
+        out_proto_str.size()));
+
+    std::string tmp_file = io_history_proto_file + "_tmp";
+    std::ofstream out(tmp_file,
+        std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
+    out << out_proto.SerializeAsString();
+    out.close();
+
+    /* Atomically replace existing proto file to reduce chance of data loss. */
+    rename(tmp_file.c_str(), io_history_proto_file.c_str());
+}
+
+void uid_monitor::load_io_history_from_proto()
+{
+    std::ifstream in(io_history_proto_file,
+        std::ofstream::in | std::ofstream::binary);
+
+    if (!in.good()) {
+        PLOG_TO(SYSTEM, INFO) << "Open " << io_history_proto_file << " failed";
+        return;
+    }
+
+    stringstream ss;
+    ss << in.rdbuf();
+    UidIOHistoryProto in_proto;
+    in_proto.ParseFromString(ss.str());
+
+    uint32_t crc = in_proto.crc();
+    in_proto.set_crc(crc_init);
+    std::string io_proto_str = in_proto.SerializeAsString();
+    uint32_t computed_crc = crc32(crc_init,
+        reinterpret_cast<const Bytef*>(io_proto_str.c_str()),
+        io_proto_str.size());
+
+    if (crc != computed_crc) {
+        LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << io_history_proto_file;
+        return;
+    }
+
+    for (const auto& item_proto : in_proto.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)
@@ -289,6 +500,9 @@
 void uid_monitor::init(charger_stat_t stat)
 {
     charger_stat = stat;
+
+    load_io_history_from_proto();
+
     start_ts = time(NULL);
     last_uid_io_stats = get_uid_io_stats();
 }
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..59cf251 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>
@@ -182,15 +183,28 @@
 }
 
 // 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);
 }
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/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()