Merge "Make init builtin command installkey respect property updates" into oc-dev
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index a762038..7512eb9 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -390,10 +390,12 @@
             continue;
         }
 
-        // Ensures that hashtree descriptor is either in /vbmeta or in
+        // Ensures that hashtree descriptor is in /vbmeta or /boot or in
         // the same partition for verity setup.
         std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
-        if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+        if (vbmeta_partition_name != "vbmeta" &&
+            vbmeta_partition_name != "boot" &&  // for legacy device to append top-level vbmeta
+            vbmeta_partition_name != partition_name) {
             LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
                      << " for partition: " << partition_name.c_str();
             continue;
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index 2c14c9b..8e49663 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,7 +39,28 @@
 #include "fs_mgr_avb_ops.h"
 #include "fs_mgr_priv.h"
 
-static struct fstab* fs_mgr_fstab = nullptr;
+static std::string fstab_by_name_prefix;
+
+static std::string extract_by_name_prefix(struct fstab* fstab) {
+    // In AVB, we can assume that there's an entry for the /misc mount
+    // point in the fstab file and use that to get the device file for
+    // the misc partition. The device needs not to have an actual /misc
+    // partition. Then returns the prefix by removing the trailing "misc":
+    //
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/
+
+    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+    if (fstab_entry == nullptr) {
+        LERROR << "/misc mount point not found in fstab";
+        return "";
+    }
+
+    std::string full_path(fstab_entry->blk_device);
+    size_t end_slash = full_path.find_last_of("/");
+
+    return full_path.substr(0, end_slash + 1);
+}
 
 static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
                                        int64_t offset, size_t num_bytes, void* buffer,
@@ -49,30 +70,14 @@
     // for partitions having 'slotselect' optin in fstab file, but it
     // won't be appended to the mount point.
     //
-    // In AVB, we can assume that there's an entry for the /misc mount
-    // point and use that to get the device file for the misc partition.
-    // From there we'll assume that a by-name scheme is used
-    // so we can just replace the trailing "misc" by the given
-    // |partition|, e.g.
+    // Appends |partition| to the fstab_by_name_prefix, which is obtained
+    // by removing the trailing "misc" from the device file of /misc mount
+    // point. e.g.,
     //
-    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+    //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
     //    - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
 
-    struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
-
-    if (fstab_entry == nullptr) {
-        LERROR << "/misc mount point not found in fstab";
-        return AVB_IO_RESULT_ERROR_IO;
-    }
-
-    std::string partition_name(partition);
-    std::string path(fstab_entry->blk_device);
-    // Replaces the last field of device file if it's not misc.
-    if (!android::base::StartsWith(partition_name, "misc")) {
-        size_t end_slash = path.find_last_of("/");
-        std::string by_name_prefix(path.substr(0, end_slash + 1));
-        path = by_name_prefix + partition_name;
-    }
+    std::string path = fstab_by_name_prefix + partition;
 
     // Ensures the device path (a symlink created by init) is ready to
     // access. fs_mgr_test_access() will test a few iterations if the
@@ -168,8 +173,8 @@
 AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
     AvbOps* ops;
 
-    // Assigns the fstab to the static variable for later use.
-    fs_mgr_fstab = fstab;
+    fstab_by_name_prefix = extract_by_name_prefix(fstab);
+    if (fstab_by_name_prefix.empty()) return nullptr;
 
     ops = (AvbOps*)calloc(1, sizeof(AvbOps));
     if (ops == nullptr) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1f52d04..bdbe725 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -109,11 +109,11 @@
 
 LogBuffer::LogBuffer(LastLogTimes* times)
     : monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
-    pthread_mutex_init(&mLogElementsLock, NULL);
+    pthread_mutex_init(&mLogElementsLock, nullptr);
 
     log_id_for_each(i) {
-        lastLoggedElements[i] = NULL;
-        droppedElements[i] = NULL;
+        lastLoggedElements[i] = nullptr;
+        droppedElements[i] = nullptr;
     }
 
     init();
@@ -198,7 +198,7 @@
         new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
     if (log_id != LOG_ID_SECURITY) {
         int prio = ANDROID_LOG_INFO;
-        const char* tag = NULL;
+        const char* tag = nullptr;
         if (log_id == LOG_ID_EVENTS) {
             tag = tagToName(elem->getTag());
         } else {
@@ -224,24 +224,24 @@
         //
         // State Init
         //     incoming:
-        //         dropped = NULL
-        //         currentLast = NULL;
+        //         dropped = nullptr
+        //         currentLast = nullptr;
         //         elem = incoming message
         //     outgoing:
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         currentLast = copy of elem
         //         log elem
         // State 0
         //     incoming:
         //         count = 0
-        //         dropped = NULL
+        //         dropped = nullptr
         //         currentLast = copy of last message
         //         elem = incoming message
         //     outgoing: if match != DIFFERENT
         //         dropped = copy of first identical message -> State 1
         //         currentLast = reference to elem
         //     break: if match == DIFFERENT
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         delete copy of last message (incoming currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -268,7 +268,7 @@
         //             currentLast = reference to elem, sum liblog.
         //     break: if match == DIFFERENT
         //         delete dropped
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         log reference to last held-back (currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -287,7 +287,7 @@
         //         currentLast = reference to elem
         //     break: if match == DIFFERENT
         //         log dropped (chatty message)
-        //         dropped = NULL -> State 0
+        //         dropped = nullptr -> State 0
         //         log reference to last held-back (currentLast)
         //         currentLast = copy of elem
         //         log elem
@@ -352,7 +352,7 @@
             } else {           // State 1
                 delete dropped;
             }
-            droppedElements[log_id] = NULL;
+            droppedElements[log_id] = nullptr;
             log(currentLast);  // report last message in the series
         } else {               // State 0
             delete currentLast;
@@ -656,7 +656,7 @@
 // mLogElementsLock must be held when this function is called.
 //
 bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
-    LogTimeEntry* oldest = NULL;
+    LogTimeEntry* oldest = nullptr;
     bool busy = false;
     bool clearAll = pruneRows == ULONG_MAX;
 
@@ -1078,9 +1078,11 @@
     return retval;
 }
 
-log_time LogBuffer::flushTo(
-    SocketClient* reader, const log_time& start, bool privileged, bool security,
-    int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+                            pid_t* lastTid, bool privileged, bool security,
+                            int (*filter)(const LogBufferElement* element,
+                                          void* arg),
+                            void* arg) {
     LogBufferElementCollection::iterator it;
     uid_t uid = reader->getUid();
 
@@ -1109,9 +1111,6 @@
     }
 
     log_time max = start;
-    // Help detect if the valid message before is from the same source so
-    // we can differentiate chatty filter types.
-    pid_t lastTid[LOG_ID_MAX] = { 0 };
 
     for (; it != mLogElements.end(); ++it) {
         LogBufferElement* element = *it;
@@ -1139,14 +1138,17 @@
             }
         }
 
-        bool sameTid = lastTid[element->getLogId()] == element->getTid();
-        // Dropped (chatty) immediately following a valid log from the
-        // same source in the same log buffer indicates we have a
-        // multiple identical squash.  chatty that differs source
-        // is due to spam filter.  chatty to chatty of different
-        // source is also due to spam filter.
-        lastTid[element->getLogId()] =
-            (element->getDropped() && !sameTid) ? 0 : element->getTid();
+        bool sameTid = false;
+        if (lastTid) {
+            sameTid = lastTid[element->getLogId()] == element->getTid();
+            // Dropped (chatty) immediately following a valid log from the
+            // same source in the same log buffer indicates we have a
+            // multiple identical squash.  chatty that differs source
+            // is due to spam filter.  chatty to chatty of different
+            // source is also due to spam filter.
+            lastTid[element->getLogId()] =
+                (element->getDropped() && !sameTid) ? 0 : element->getTid();
+        }
 
         pthread_mutex_unlock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index fcf6b9a..19d11cb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,11 +115,15 @@
 
     int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
             const char* msg, unsigned short len);
+    // lastTid is an optional context to help detect if the last previous
+    // valid message was from the same source so we can differentiate chatty
+    // filter types (identical or expired)
     log_time flushTo(SocketClient* writer, const log_time& start,
+                     pid_t* lastTid,  // &lastTid[LOG_ID_MAX] or nullptr
                      bool privileged, bool security,
                      int (*filter)(const LogBufferElement* element,
-                                   void* arg) = NULL,
-                     void* arg = NULL);
+                                   void* arg) = nullptr,
+                     void* arg = nullptr);
 
     bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 81356fe..04a620c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -235,7 +235,9 @@
     }
     iovec[1].iov_len = entry.len;
 
-    log_time retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mRealTime;
+    log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
+                          ? FLUSH_ERROR
+                          : mRealTime;
 
     if (buffer) free(buffer);
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 620d4d0..af19279 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -182,7 +182,7 @@
         } logFindStart(pid, logMask, sequence,
                        logbuf().isMonotonic() && android::isMonotonic(start));
 
-        logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+        logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
                          FlushCommand::hasSecurityLogs(cli),
                          logFindStart.callback, &logFindStart);
 
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 04e531f..ccc550a 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <errno.h>
+#include <string.h>
 #include <sys/prctl.h>
 
 #include <private/android_logger.h>
@@ -47,7 +48,8 @@
       mEnd(log_time(android_log_clockid())) {
     mTimeout.tv_sec = timeout / NS_PER_SEC;
     mTimeout.tv_nsec = timeout % NS_PER_SEC;
-    pthread_cond_init(&threadTriggeredCondition, NULL);
+    memset(mLastTid, 0, sizeof(mLastTid));
+    pthread_cond_init(&threadTriggeredCondition, nullptr);
     cleanSkip_Locked();
 }
 
@@ -98,7 +100,7 @@
             it++;
         }
 
-        me->mClient = NULL;
+        me->mClient = nullptr;
         reader.release(client);
     }
 
@@ -122,7 +124,7 @@
     SocketClient* client = me->mClient;
     if (!client) {
         me->error();
-        return NULL;
+        return nullptr;
     }
 
     LogBuffer& logbuf = me->mReader.logbuf();
@@ -151,12 +153,12 @@
         unlock();
 
         if (me->mTail) {
-            logbuf.flushTo(client, start, privileged, security, FilterFirstPass,
-                           me);
+            logbuf.flushTo(client, start, nullptr, privileged, security,
+                           FilterFirstPass, me);
             me->leadingDropped = true;
         }
-        start = logbuf.flushTo(client, start, privileged, security,
-                               FilterSecondPass, me);
+        start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+                               security, FilterSecondPass, me);
 
         lock();
 
@@ -182,7 +184,7 @@
 
     pthread_cleanup_pop(true);
 
-    return NULL;
+    return nullptr;
 }
 
 // A first pass to count the number of elements
@@ -281,7 +283,5 @@
 }
 
 void LogTimeEntry::cleanSkip_Locked(void) {
-    for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t)(i + 1)) {
-        skipAhead[i] = 0;
-    }
+    memset(skipAhead, 0, sizeof(skipAhead));
 }
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9a3ddab..ec8252e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -44,6 +44,7 @@
     const unsigned int mLogMask;
     const pid_t mPid;
     unsigned int skipAhead[LOG_ID_MAX];
+    pid_t mLastTid[LOG_ID_MAX];
     unsigned long mCount;
     unsigned long mTail;
     unsigned long mIndex;
diff --git a/storaged/README.properties b/storaged/README.properties
index 70e6026..2d8397f 100644
--- a/storaged/README.properties
+++ b/storaged/README.properties
@@ -1,6 +1,5 @@
 ro.storaged.event.interval    # interval storaged scans for IO stats, in seconds
 ro.storaged.event.perf_check  # check for time spent in event loop, in microseconds
 ro.storaged.disk_stats_pub    # interval storaged publish disk stats, in seconds
-ro.storaged.emmc_info_pub     # interval storaged publish emmc info, in seconds
 ro.storaged.uid_io.interval   # interval storaged checks Per UID IO usage, in seconds
 ro.storaged.uid_io.threshold  # Per UID IO usage limit, in bytes
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index bd1391c..b6a0850 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -230,7 +230,6 @@
 // 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_EMMC_INFO_PUBLISH ( 86400 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
 
@@ -240,7 +239,6 @@
 struct storaged_config {
     int periodic_chores_interval_unit;
     int periodic_chores_interval_disk_stats_publish;
-    int periodic_chores_interval_emmc_info_publish;
     int periodic_chores_interval_uid_io;
     bool proc_uid_io_available;      // whether uid_io is accessible
     bool diskstats_available;   // whether diskstats is accessible
@@ -253,7 +251,6 @@
     storaged_config mConfig;
     disk_stats_publisher mDiskStats;
     disk_stats_monitor mDsm;
-    storage_info_t *info = nullptr;
     uid_monitor mUidm;
     time_t mStarttime;
 public:
@@ -264,9 +261,6 @@
     void pause(void) {
         sleep(mConfig.periodic_chores_interval_unit);
     }
-    void set_storage_info(storage_info_t *storage_info) {
-        info = storage_info;
-    }
 
     time_t get_starttime(void) {
         return mStarttime;
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index cb5b8a8..913c814 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -24,43 +24,42 @@
 
 using namespace std;
 
-// two characters in string for each byte
-struct str_hex {
-    char str[2];
-};
-
 class storage_info_t {
 protected:
     FRIEND_TEST(storaged_test, storage_info_t);
-    uint8_t eol;                    // pre-eol (end of life) information
-    uint8_t lifetime_a;             // device life time estimation (type A)
-    uint8_t lifetime_b;             // device life time estimation (type B)
+    uint16_t eol;                   // pre-eol (end of life) information
+    uint16_t lifetime_a;            // device life time estimation (type A)
+    uint16_t lifetime_b;            // device life time estimation (type B)
     string version;                 // version string
-public:
     void publish();
+public:
+    storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0) {}
     virtual ~storage_info_t() {}
-    virtual bool init() = 0;
-    virtual bool update() = 0;
+    virtual bool report() = 0;
 };
 
 class emmc_info_t : public storage_info_t {
 private:
-    // minimum size of a ext_csd file
-    const int EXT_CSD_FILE_MIN_SIZE = 1024;
-    // List of interesting offsets
-    const size_t EXT_CSD_REV_IDX = 192 * sizeof(str_hex);
-    const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(str_hex);
-    const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(str_hex);
-    const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(str_hex);
-
-    const char* ext_csd_file = "/d/mmc0/mmc0:0001/ext_csd";
-    const char* emmc_ver_str[8] = {
-        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
+    const string emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
+    const string emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
+    const char* emmc_ver_str[9] = {
+        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
     };
 public:
     virtual ~emmc_info_t() {}
-    bool init();
-    bool update();
+    bool report();
+    bool report_sysfs();
+    bool report_debugfs();
 };
 
+class ufs_info_t : public storage_info_t {
+private:
+    const string health_file = "/sys/devices/soc/624000.ufshc/health";
+public:
+    virtual ~ufs_info_t() {}
+    bool report();
+};
+
+void report_storage_health();
+
 #endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index e25298b..2f2273d 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -43,7 +43,6 @@
 #include <storaged_utils.h>
 
 storaged_t storaged;
-emmc_info_t emmc_info;
 
 // Function of storaged's main thread
 void* storaged_main(void* s) {
@@ -114,10 +113,7 @@
     }
 
     if (flag_main_service) { // start main thread
-        if (emmc_info.init()) {
-            storaged.set_storage_info(&emmc_info);
-        }
-
+        report_storage_health();
         // Start the main thread of storaged
         pthread_t storaged_main_thread;
         errno = pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged);
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 88fbb7a..1770922 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -170,6 +170,9 @@
 }
 
 void storaged_t::init_battery_service() {
+    if (!mConfig.proc_uid_io_available)
+        return;
+
     sp<IBatteryPropertiesRegistrar> battery_properties = get_battery_properties_service();
     if (battery_properties == NULL) {
         LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
@@ -203,9 +206,6 @@
     mConfig.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_emmc_info_publish =
-        property_get_int32("ro.storaged.emmc_info_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH);
-
     mConfig.periodic_chores_interval_uid_io =
         property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
 
@@ -221,12 +221,6 @@
         }
     }
 
-    if (info && mTimer &&
-        (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
-        info->update();
-        info->publish();
-    }
-
     if (mConfig.proc_uid_io_available && mTimer &&
             (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
          mUidm.report();
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 73d611c..434bd74 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -16,83 +16,169 @@
 
 #define LOG_TAG "storaged"
 
+#include <stdio.h>
 #include <string.h>
 
 #include <android-base/file.h>
-#include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <log/log_event_list.h>
 
 #include "storaged.h"
 
 using namespace std;
-using namespace android;
 using namespace android::base;
 
+void report_storage_health()
+{
+    emmc_info_t mmc;
+    ufs_info_t ufs;
+
+    mmc.report();
+    ufs.report();
+}
+
 void storage_info_t::publish()
 {
-    if (eol == 0 && lifetime_a == 0 && lifetime_b == 0) {
-        return;
-    }
-
     android_log_event_list(EVENTLOGTAG_EMMCINFO)
         << version << eol << lifetime_a << lifetime_b
         << LOG_ID_EVENTS;
 }
 
-bool emmc_info_t::init()
+bool emmc_info_t::report()
+{
+    if (!report_sysfs() && !report_debugfs())
+        return false;
+
+    publish();
+    return true;
+}
+
+bool emmc_info_t::report_sysfs()
 {
     string buffer;
-    if (!ReadFileToString(ext_csd_file, &buffer) ||
-        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+    uint16_t rev = 0;
+
+    if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) {
         return false;
     }
 
-    string ver_str = buffer.substr(EXT_CSD_REV_IDX, sizeof(str_hex));
-    uint8_t ext_csd_rev;
-    if (!ParseUint(ver_str, &ext_csd_rev)) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV.";
+    if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 ||
+        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
         return false;
     }
 
     version = "emmc ";
-    version += (ext_csd_rev < ARRAY_SIZE(emmc_ver_str)) ?
-                emmc_ver_str[ext_csd_rev] : "Unknown";
+    version += emmc_ver_str[rev];
 
-    if (ext_csd_rev < 7) {
+    if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) {
         return false;
     }
 
-    return update();
-}
-
-bool emmc_info_t::update()
-{
-    string buffer;
-    if (!ReadFileToString(ext_csd_file, &buffer) ||
-        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+    if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) {
         return false;
     }
 
-    string str = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(str_hex));
-    if (!ParseUint(str, &eol)) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO.";
+    if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) {
         return false;
     }
 
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(str_hex));
-    if (!ParseUint(str, &lifetime_a)) {
-        LOG_TO(SYSTEM, ERROR)
-            << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.";
-        return false;
-    }
-
-    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(str_hex));
-    if (!ParseUint(str, &lifetime_b)) {
-        LOG_TO(SYSTEM, ERROR)
-            << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.";
+    if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 ||
+        (lifetime_a == 0 && lifetime_b == 0)) {
         return false;
     }
 
     return true;
 }
+
+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;
+const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
+const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+
+bool emmc_info_t::report_debugfs()
+{
+    string buffer;
+    uint16_t rev = 0;
+
+    if (!ReadFileToString(emmc_debugfs, &buffer) ||
+        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+        return false;
+    }
+
+    string str = buffer.substr(EXT_CSD_REV_IDX, 2);
+    if (!ParseUint(str, &rev) ||
+        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
+        return false;
+    }
+
+    version = "emmc ";
+    version += emmc_ver_str[rev];
+
+    str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
+    if (!ParseUint(str, &eol)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
+    if (!ParseUint(str, &lifetime_a)) {
+        return false;
+    }
+
+    str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
+    if (!ParseUint(str, &lifetime_b)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool ufs_info_t::report()
+{
+    string buffer;
+    if (!ReadFileToString(health_file, &buffer)) {
+        return false;
+    }
+
+    vector<string> lines = Split(buffer, "\n");
+    if (lines.empty()) {
+        return false;
+    }
+
+    char rev[8];
+    if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) {
+        return false;
+    }
+
+    version = "ufs " + string(rev);
+
+    for (size_t i = 1; i < lines.size(); i++) {
+        char token[32];
+        uint16_t val;
+        int ret;
+        if ((ret = sscanf(lines[i].c_str(),
+                   "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx",
+                   token, &val)) < 2) {
+            continue;
+        }
+
+        if (string(token) == "bPreEOLInfo") {
+            eol = val;
+        } else if (string(token) == "bDeviceLifeTimeEstA") {
+            lifetime_a = val;
+        } else if (string(token) == "bDeviceLifeTimeEstB") {
+            lifetime_b = val;
+        }
+    }
+
+    if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {
+        return false;
+    }
+
+    publish();
+    return true;
+}
+
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index e335cad..b103ac1 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -29,7 +29,6 @@
 
 #define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
 #define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
 
 static void pause(uint32_t sec) {
     const char* path = "/cache/test";
@@ -58,13 +57,8 @@
 const char* DISK_STATS_PATH;
 TEST(storaged_test, retvals) {
     struct disk_stats stats;
-    emmc_info_t info;
     memset(&stats, 0, sizeof(struct disk_stats));
 
-    if (info.init()) {
-        EXPECT_TRUE(info.update());
-    }
-
     if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
         DISK_STATS_PATH = MMC_DISK_STATS_PATH;
     } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
@@ -127,20 +121,6 @@
     }
 }
 
-TEST(storaged_test, storage_info_t) {
-    emmc_info_t info;
-
-    if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
-        int ret = info.init();
-        if (ret) {
-            EXPECT_TRUE(info.version.empty());
-            ASSERT_TRUE(info.update());
-            // update should put something in info.
-            EXPECT_TRUE(info.eol || info.lifetime_a || info.lifetime_b);
-        }
-    }
-}
-
 static double mean(std::deque<uint32_t> nums) {
     double sum = 0.0;
     for (uint32_t i : nums) {