storaged: rewrite emmc info class

Test: adb logcat -d -b events | grep storaged_emmc_info
Bug: 36228467
Change-Id: Ib799e60ed65661a9fb99be8ad4c930f547339975
diff --git a/storaged/Android.mk b/storaged/Android.mk
index 2adb14d..5e6a3c0 100644
--- a/storaged/Android.mk
+++ b/storaged/Android.mk
@@ -16,6 +16,7 @@
 
 LOCAL_SRC_FILES := \
     storaged.cpp \
+    storaged_info.cpp \
     storaged_service.cpp \
     storaged_utils.cpp \
     storaged_uid_monitor.cpp \
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index c291bd9..bd1391c 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -28,6 +28,7 @@
 
 #include <batteryservice/IBatteryPropertiesListener.h>
 
+#include "storaged_info.h"
 #include "storaged_uid_monitor.h"
 
 using namespace android;
@@ -44,6 +45,8 @@
 #define debuginfo(...)
 #endif
 
+#define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))
+
 #define SECTOR_SIZE ( 512 )
 #define SEC_TO_MSEC ( 1000 )
 #define MSEC_TO_USEC ( 1000 )
@@ -83,15 +86,7 @@
     double   io_avg;         // average io_in_flight for accumulate calculations
 };
 
-#define MMC_VER_STR_LEN ( 9 )   // maximum length of the MMC version string, including NULL terminator
-// minimum size of a ext_csd file
-#define EXT_CSD_FILE_MIN_SIZE ( 1024 )
-struct emmc_info {
-    int eol;                        // pre-eol (end of life) information
-    int lifetime_a;                 // device life time estimation (type A)
-    int lifetime_b;                 // device life time estimation (type B)
-    char mmc_ver[MMC_VER_STR_LEN];  // device version string
-};
+
 
 struct disk_perf {
     uint32_t read_perf;         // read speed (kbytes/s)
@@ -232,26 +227,6 @@
     void update(void);
 };
 
-class emmc_info_t {
-private:
-    struct emmc_info mInfo;
-    bool mValid;
-    int mFdEmmc;
-public:
-    emmc_info_t(void) :
-            mValid(false),
-            mFdEmmc(-1) {
-        memset(&mInfo, 0, sizeof(struct emmc_info));
-    }
-    ~emmc_info_t(void) {}
-
-    void publish(void);
-    void update(void);
-    void set_emmc_fd(int fd) {
-        mFdEmmc = fd;
-    }
-};
-
 // Periodic chores intervals in seconds
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
 #define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
@@ -268,7 +243,6 @@
     int periodic_chores_interval_emmc_info_publish;
     int periodic_chores_interval_uid_io;
     bool proc_uid_io_available;      // whether uid_io is accessible
-    bool emmc_available;        // whether eMMC est_csd file is readable
     bool diskstats_available;   // whether diskstats is accessible
     int event_time_check_usec;  // check how much cputime spent in event loop
 };
@@ -279,7 +253,7 @@
     storaged_config mConfig;
     disk_stats_publisher mDiskStats;
     disk_stats_monitor mDsm;
-    emmc_info_t mEmmcInfo;
+    storage_info_t *info = nullptr;
     uid_monitor mUidm;
     time_t mStarttime;
 public:
@@ -290,8 +264,8 @@
     void pause(void) {
         sleep(mConfig.periodic_chores_interval_unit);
     }
-    void set_privileged_fds(int fd_emmc) {
-        mEmmcInfo.set_emmc_fd(fd_emmc);
+    void set_storage_info(storage_info_t *storage_info) {
+        info = storage_info;
     }
 
     time_t get_starttime(void) {
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
new file mode 100644
index 0000000..cb5b8a8
--- /dev/null
+++ b/storaged/include/storaged_info.h
@@ -0,0 +1,66 @@
+/*
+ * 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_INFO_H_
+#define _STORAGED_INFO_H_
+
+#include <string.h>
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+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)
+    string version;                 // version string
+public:
+    void publish();
+    virtual ~storage_info_t() {}
+    virtual bool init() = 0;
+    virtual bool update() = 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"
+    };
+public:
+    virtual ~emmc_info_t() {}
+    bool init();
+    bool update();
+};
+
+#endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 672f453..e25298b 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -43,6 +43,7 @@
 #include <storaged_utils.h>
 
 storaged_t storaged;
+emmc_info_t emmc_info;
 
 // Function of storaged's main thread
 void* storaged_main(void* s) {
@@ -69,7 +70,6 @@
 int main(int argc, char** argv) {
     int flag_main_service = 0;
     int flag_dump_uid = 0;
-    int fd_emmc = -1;
     int opt;
 
     for (;;) {
@@ -114,12 +114,9 @@
     }
 
     if (flag_main_service) { // start main thread
-        static const char mmc0_ext_csd[] = "/d/mmc0/mmc0:0001/ext_csd";
-        fd_emmc = android_get_control_file(mmc0_ext_csd);
-        if (fd_emmc < 0)
-            fd_emmc = TEMP_FAILURE_RETRY(open(mmc0_ext_csd, O_RDONLY));
-
-        storaged.set_privileged_fds(fd_emmc);
+        if (emmc_info.init()) {
+            storaged.set_storage_info(&emmc_info);
+        }
 
         // Start the main thread of storaged
         pthread_t storaged_main_thread;
@@ -134,8 +131,6 @@
         IPCThreadState::self()->joinThreadPool();
         pthread_join(storaged_main_thread, NULL);
 
-        close(fd_emmc);
-
         return 0;
     }
 
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 2f02074..88fbb7a 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -147,19 +147,6 @@
     }
 }
 
-/* emmc_info_t */
-void emmc_info_t::publish(void) {
-    if (mValid) {
-        log_event_emmc_info(&mInfo);
-    }
-}
-
-void emmc_info_t::update(void) {
-    if (mFdEmmc >= 0) {
-        mValid = parse_emmc_ecsd(mFdEmmc, &mInfo);
-    }
-}
-
 static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
     sp<IServiceManager> sm = defaultServiceManager();
     if (sm == NULL) return NULL;
@@ -199,8 +186,6 @@
 
 /* storaged_t */
 storaged_t::storaged_t(void) {
-    mConfig.emmc_available = (access(EMMC_ECSD_PATH, R_OK) >= 0);
-
     if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
         mConfig.diskstats_available = false;
     } else {
@@ -236,10 +221,10 @@
         }
     }
 
-    if (mConfig.emmc_available && mTimer &&
-            (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
-        mEmmcInfo.update();
-        mEmmcInfo.publish();
+    if (info && mTimer &&
+        (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
+        info->update();
+        info->publish();
     }
 
     if (mConfig.proc_uid_io_available && mTimer &&
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
new file mode 100644
index 0000000..73d611c
--- /dev/null
+++ b/storaged/storaged_info.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <string.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+
+using namespace std;
+using namespace android;
+using namespace android::base;
+
+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()
+{
+    string buffer;
+    if (!ReadFileToString(ext_csd_file, &buffer) ||
+        buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
+        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.";
+        return false;
+    }
+
+    version = "emmc ";
+    version += (ext_csd_rev < ARRAY_SIZE(emmc_ver_str)) ?
+                emmc_ver_str[ext_csd_rev] : "Unknown";
+
+    if (ext_csd_rev < 7) {
+        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) {
+        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.";
+        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.";
+        return false;
+    }
+
+    return true;
+}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 9fcf1fa..74b7436 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -158,93 +158,6 @@
     }
 }
 
-bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
-    CHECK(ext_csd_fd >= 0);
-    struct hex {
-        char str[2];
-    };
-    // List of interesting offsets
-    static const size_t EXT_CSD_REV_IDX = 192 * sizeof(hex);
-    static const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(hex);
-    static const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(hex);
-    static const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(hex);
-
-    // Read file
-    CHECK(lseek(ext_csd_fd, 0, SEEK_SET) == 0);
-    std::string buffer;
-    if (!android::base::ReadFdToString(ext_csd_fd, &buffer)) {
-        PLOG_TO(SYSTEM, ERROR) << "ReadFdToString failed.";
-        return false;
-    }
-
-    if (buffer.length() < EXT_CSD_FILE_MIN_SIZE) {
-        LOG_TO(SYSTEM, ERROR) << "EMMC ext csd file has truncated content. "
-            << "File length: " << buffer.length();
-        return false;
-    }
-
-    std::string sub;
-    std::stringstream ss;
-    // Parse EXT_CSD_REV
-    int ext_csd_rev = -1;
-    sub = buffer.substr(EXT_CSD_REV_IDX, sizeof(hex));
-    ss << sub;
-    ss >> std::hex >> ext_csd_rev;
-    if (ext_csd_rev < 0) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_CSD_REV.";
-        return false;
-    }
-    ss.clear();
-
-    static const char* ver_str[] = {
-        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
-    };
-
-    strlcpy(info->mmc_ver,
-            (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
-                           ver_str[ext_csd_rev] :
-                           "Unknown",
-            MMC_VER_STR_LEN);
-
-    if (ext_csd_rev < 7) {
-        return 0;
-    }
-
-    // Parse EXT_PRE_EOL_INFO
-    info->eol = -1;
-    sub = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(hex));
-    ss << sub;
-    ss >> std::hex >> info->eol;
-    if (info->eol < 0) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_PRE_EOL_INFO.";
-        return false;
-    }
-    ss.clear();
-
-    // Parse DEVICE_LIFE_TIME_EST
-    info->lifetime_a = -1;
-    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(hex));
-    ss << sub;
-    ss >> std::hex >> info->lifetime_a;
-    if (info->lifetime_a < 0) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.";
-        return false;
-    }
-    ss.clear();
-
-    info->lifetime_b = -1;
-    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(hex));
-    ss << sub;
-    ss >> std::hex >> info->lifetime_b;
-    if (info->lifetime_b < 0) {
-        LOG_TO(SYSTEM, ERROR) << "Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.";
-        return false;
-    }
-    ss.clear();
-
-    return true;
-}
-
 static 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--) {
@@ -317,14 +230,3 @@
         << LOG_ID_EVENTS;
 }
 
-void log_event_emmc_info(struct emmc_info* info) {
-    // skip if the input structure are all zeros
-    if (info == NULL) return;
-    struct emmc_info zero_cmp;
-    memset(&zero_cmp, 0, sizeof(zero_cmp));
-    if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
-
-    android_log_event_list(EVENTLOGTAG_EMMCINFO)
-        << info->mmc_ver << info->eol << info->lifetime_a << info->lifetime_b
-        << LOG_ID_EVENTS;
-}
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index 9e03c50..e335cad 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -58,13 +58,11 @@
 const char* DISK_STATS_PATH;
 TEST(storaged_test, retvals) {
     struct disk_stats stats;
-    struct emmc_info info;
+    emmc_info_t info;
     memset(&stats, 0, sizeof(struct disk_stats));
-    memset(&info, 0, sizeof(struct emmc_info));
 
-    int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
-    if (emmc_fd >= 0) {
-        EXPECT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
+    if (info.init()) {
+        EXPECT_TRUE(info.update());
     }
 
     if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
@@ -129,17 +127,17 @@
     }
 }
 
-TEST(storaged_test, emmc_info) {
-    struct emmc_info info, void_info;
-    memset(&info, 0, sizeof(struct emmc_info));
-    memset(&void_info, 0, sizeof(struct emmc_info));
+TEST(storaged_test, storage_info_t) {
+    emmc_info_t info;
 
     if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
-        int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
-        ASSERT_GE(emmc_fd, 0);
-        ASSERT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
-        // parse_emmc_ecsd() should put something in info.
-        EXPECT_NE(0, memcmp(&void_info, &info, sizeof(struct emmc_info)));
+        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);
+        }
     }
 }