storaged: read emmc health data from sysfs am: 8197093497
am: fa578a57c1 -s ours
Change-Id: I6900fc3cceab8d3809220c1fc25922630753f623
diff --git a/adb/Android.mk b/adb/Android.mk
index e841205..a05bb55 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -128,7 +128,7 @@
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
@@ -361,6 +361,7 @@
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
+ libqemu_pipe \
libbootloader_message \
libfs_mgr \
libfec \
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 4198a52..12b98ba 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -289,7 +289,7 @@
#define open adb_open
#define read adb_read
#define write adb_write
-#include <system/qemu_pipe.h>
+#include <qemu_pipe.h>
#undef open
#undef read
#undef write
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
deleted file mode 100644
index af25079..0000000
--- a/include/system/qemu_pipe.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2011 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 ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-# define QEMU_PIPE_DEBUG(...) (void)0
-#endif
-
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-//
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL -> unknown/unsupported pipeName
-// ENOSYS -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-static __inline__ int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
- if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
- errno = EINVAL;
- return -1;
- }
-
- int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
- if (fd < 0) {
- QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
- strerror(errno));
- return -1;
- }
-
- // Write the pipe name, *including* the trailing zero which is necessary.
- size_t pipeNameLen = strlen(pipeName);
- ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
- if (ret != (ssize_t)pipeNameLen + 1) {
- QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
- __FUNCTION__, pipeName, strerror(errno));
- if (ret == 0) {
- errno = ECONNRESET;
- } else if (ret > 0) {
- errno = EINVAL;
- }
- return -1;
- }
- return fd;
-}
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-static int __inline__ qemu_pipe_frame_send(int fd,
- const void* buff,
- size_t len) {
- char header[5];
- snprintf(header, sizeof(header), "%04zx", len);
- ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
- if (ret != 4) {
- QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
- return -1;
- }
- ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
- if (ret != (ssize_t)len) {
- QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
- char header[5];
- ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
- if (ret != 4) {
- QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
- return -1;
- }
- header[4] = '\0';
- size_t size;
- if (sscanf(header, "%04zx", &size) != 1) {
- QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
- return -1;
- }
- if (size > len) {
- QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
- len);
- return -1;
- }
- ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
- if (ret != (ssize_t)size) {
- QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
- strerror(errno));
- return -1;
- }
- return size;
-}
-
-#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/qemu_pipe/Android.mk b/qemu_pipe/Android.mk
new file mode 100644
index 0000000..6e0144c
--- /dev/null
+++ b/qemu_pipe/Android.mk
@@ -0,0 +1,19 @@
+# Copyright 2011 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+common_static_libraries := \
+ libbase
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+ qemu_pipe.cpp
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ system/base/include
+LOCAL_MODULE:= libqemu_pipe
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
new file mode 100644
index 0000000..0987498
--- /dev/null
+++ b/qemu_pipe/include/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
new file mode 100644
index 0000000..beeccb0
--- /dev/null
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "qemu_pipe.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+# define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+ // Sanity check.
+ if (!pipeName) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+ strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+
+ // now, add 'pipe:' prefix and try again
+ // Note: host side will wait for the trailing '\0' to start
+ // service lookup.
+ const char pipe_prefix[] = "pipe:";
+ if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+ WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+ return fd;
+ }
+ QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
+ __FUNCTION__, pipeName, strerror(errno));
+ close(fd);
+ return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ if (!WriteFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ if (!WriteFully(fd, buff, len)) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ if (!ReadFully(fd, header, 4)) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+ len);
+ return -1;
+ }
+ if (!ReadFully(fd, buff, size)) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+ strerror(errno));
+ return -1;
+ }
+ return size;
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 86fec6a..345097f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -101,6 +101,21 @@
$(foreach binary, $(SANITIZE_ASAN_OPTIONS_FOR), $(eval $(call create-asan-options-module,$(binary))))
endif
+# ASAN extration.
+ASAN_EXTRACT_FILES :=
+ifeq ($(SANITIZE_TARGET_SYSTEM),true)
+include $(CLEAR_VARS)
+LOCAL_MODULE:= asan_extract
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := asan_extract.sh
+LOCAL_INIT_RC := asan_extract.rc
+# We need bzip2 on device for extraction.
+LOCAL_REQUIRED_MODULES := bzip2
+include $(BUILD_PREBUILT)
+ASAN_EXTRACT_FILES := asan_extract
+endif
+
endif
#######################################
@@ -114,7 +129,7 @@
EXPORT_GLOBAL_ASAN_OPTIONS :=
ifneq ($(filter address,$(SANITIZE_TARGET)),)
EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
- LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES)
+ LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
endif
EXPORT_GLOBAL_GCOV_OPTIONS :=
diff --git a/rootdir/asan_extract.rc b/rootdir/asan_extract.rc
new file mode 100644
index 0000000..705d872
--- /dev/null
+++ b/rootdir/asan_extract.rc
@@ -0,0 +1,6 @@
+# When /data is available, look for /system/asan.tar.gz and potentially extract.
+on post-fs-data
+ exec - system system -- /system/bin/asan_extract
+
+on early-boot && property:asan.restore_reboot=1
+ powerctl reboot
diff --git a/rootdir/asan_extract.sh b/rootdir/asan_extract.sh
new file mode 100644
index 0000000..c7d2fdf
--- /dev/null
+++ b/rootdir/asan_extract.sh
@@ -0,0 +1,67 @@
+#!/system/bin/sh
+
+#
+# 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.
+#
+
+# This script will extract ASAN libraries from /system/asan.tar.gz to /data and then reboot.
+
+# TODO:
+# * Timestamp or something to know when to run this again. Right now take the existence of
+# /data/lib as we're already done.
+# * Need to distinguish pre- from post-decryption for FDE.
+
+SRC=/system/asan.tar.bz2
+MD5_FILE=/data/asan.md5sum
+ASAN_DIR=/data/asan
+
+if ! test -f $SRC ; then
+ log -p i -t asan_install "Did not find $SRC!"
+ exit 1
+fi
+
+log -p i -t asan_install "Found $SRC, checking whether we need to apply it."
+
+ASAN_TAR_MD5=$(md5sum $SRC)
+if test -f $MD5_FILE ; then
+ INSTALLED_MD5=$(cat $MD5_FILE)
+ if [ "x$ASAN_TAR_MD5" = "x$INSTALLED_MD5" ] ; then
+ log -p i -t asan_install "Checksums match, nothing to be done here."
+ exit 0
+ fi
+fi
+
+# Just clean up, helps with restorecon.
+rm -rf $ASAN_DIR
+
+log -p i -t asan_install "Untarring $SRC..."
+
+# Unzip from /system/asan.tar.gz into data. Need to pipe as gunzip is not on device.
+bzip2 -c -d $SRC | tar -x -f - --no-same-owner -C / || exit 1
+
+# Cannot log here, log would run with system_data_file.
+
+restorecon -R -F $ASAN_DIR/*/lib*
+
+log -p i -t asan_install "Fixed selinux labels..."
+
+echo "$ASAN_TAR_MD5" > $MD5_FILE
+
+# We want to reboot now. It seems it is not possible to run "reboot" here, the device will
+# just be stuck.
+
+log -p i -t asan_install "Signaling init to reboot..."
+
+setprop asan.restore_reboot 1
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index b6a0850..514798b 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -27,6 +27,7 @@
#include <vector>
#include <batteryservice/IBatteryPropertiesListener.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
#include "storaged_info.h"
#include "storaged_uid_monitor.h"
@@ -245,7 +246,8 @@
int event_time_check_usec; // check how much cputime spent in event loop
};
-class storaged_t : public BnBatteryPropertiesListener {
+class storaged_t : public BnBatteryPropertiesListener,
+ public IBinder::DeathRecipient {
private:
time_t mTimer;
storaged_config mConfig;
@@ -253,6 +255,7 @@
disk_stats_monitor mDsm;
uid_monitor mUidm;
time_t mStarttime;
+ sp<IBatteryPropertiesRegistrar> battery_properties;
public:
storaged_t(void);
~storaged_t() {}
@@ -281,6 +284,7 @@
void init_battery_service();
virtual void batteryPropertiesChanged(struct BatteryProperties props);
+ void binderDied(const wp<IBinder>& who);
};
// Eventlog tag
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index cfcdd7f..913c814 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -52,6 +52,14 @@
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/storaged.cpp b/storaged/storaged.cpp
index 72bb6e2..aa3d1de 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -170,7 +170,10 @@
}
void storaged_t::init_battery_service() {
- sp<IBatteryPropertiesRegistrar> battery_properties = get_battery_properties_service();
+ if (!mConfig.proc_uid_io_available)
+ return;
+
+ battery_properties = get_battery_properties_service();
if (battery_properties == NULL) {
LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
return;
@@ -182,6 +185,17 @@
// register listener after init uid_monitor
battery_properties->registerListener(this);
+ IInterface::asBinder(battery_properties)->linkToDeath(this);
+}
+
+void storaged_t::binderDied(const wp<IBinder>& who) {
+ if (battery_properties != NULL &&
+ IInterface::asBinder(battery_properties) == who) {
+ LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
+ exit(1);
+ } else {
+ LOG_TO(SYSTEM, ERROR) << "unknown service died";
+ }
}
/* storaged_t */
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 1a5da41..434bd74 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -22,6 +22,7 @@
#include <android-base/file.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"
@@ -32,7 +33,10 @@
void report_storage_health()
{
emmc_info_t mmc;
+ ufs_info_t ufs;
+
mmc.report();
+ ufs.report();
}
void storage_info_t::publish()
@@ -130,4 +134,51 @@
}
return true;
-}
\ No newline at end of file
+}
+
+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;
+}
+