Merge "bootstat: kernel_panic test w/o bootloader or pstore support" am: 70fda27eb2 am: 6c9f88863c
am: ae3bbc2fa2
Change-Id: Ied94a5c7a49d8cf252cca1c98624f504e8e82f2a
diff --git a/adf/libadf/adf.cpp b/adf/libadf/adf.cpp
index 60d8ef0..fd9c208 100644
--- a/adf/libadf/adf.cpp
+++ b/adf/libadf/adf.cpp
@@ -132,8 +132,11 @@
void adf_free_device_data(struct adf_device_data *data)
{
delete [] data->attachments;
+ data->attachments = nullptr;
delete [] data->allowed_attachments;
+ data->allowed_attachments = nullptr;
delete [] static_cast<char *>(data->custom_data);
+ data->custom_data = nullptr;
}
int adf_device_post(struct adf_device *dev,
@@ -236,9 +239,10 @@
return err;
std::vector<adf_id_t> ids;
- for (size_t i = 0; i < data.n_allowed_attachments; i++)
- if (data.allowed_attachments[i].overlay_engine == overlay_engine)
- ids.push_back(data.allowed_attachments[i].interface);
+ if (data.allowed_attachments != nullptr)
+ for (size_t i = 0; i < data.n_allowed_attachments; i++)
+ if (data.allowed_attachments[i].overlay_engine == overlay_engine)
+ ids.push_back(data.allowed_attachments[i].interface);
adf_free_device_data(&data);
return adf_id_vector_to_array(ids, interfaces);
@@ -450,9 +454,10 @@
return err;
std::vector<adf_id_t> ids;
- for (size_t i = 0; i < data.n_allowed_attachments; i++)
- if (data.allowed_attachments[i].interface == interface)
- ids.push_back(data.allowed_attachments[i].overlay_engine);
+ if (data.allowed_attachments != nullptr)
+ for (size_t i = 0; i < data.n_allowed_attachments; i++)
+ if (data.allowed_attachments[i].interface == interface)
+ ids.push_back(data.allowed_attachments[i].overlay_engine);
return adf_id_vector_to_array(ids, overlay_engines);
}
@@ -551,7 +556,9 @@
void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
{
delete [] data->supported_formats;
+ data->supported_formats = nullptr;
delete [] static_cast<char *>(data->custom_data);
+ data->custom_data = nullptr;
}
bool adf_overlay_engine_supports_format(int fd, __u32 format)
@@ -564,10 +571,12 @@
if (err < 0)
return false;
- for (i = 0; i < data.n_supported_formats; i++) {
- if (data.supported_formats[i] == format) {
- ret = true;
- break;
+ if (data.supported_formats != nullptr) {
+ for (i = 0; i < data.n_supported_formats; i++) {
+ if (data.supported_formats[i] == format) {
+ ret = true;
+ break;
+ }
}
}
@@ -638,18 +647,18 @@
const __u32 *formats, size_t n_formats,
adf_id_t interface, adf_id_t *overlay_engine)
{
- adf_id_t *engs;
+ adf_id_t *engs = nullptr;
ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
- if (n_engs <= 0)
+ if (engs == nullptr)
return false;
- adf_id_t *filtered_engs;
+ adf_id_t *filtered_engs = nullptr;
ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
formats, n_formats, engs, n_engs, &filtered_engs);
free(engs);
- if (n_filtered_engs <= 0)
+ if (filtered_engs == nullptr)
return false;
*overlay_engine = filtered_engs[0];
@@ -700,17 +709,17 @@
if (n_intfs < 0)
return n_intfs;
- else if (!n_intfs)
+ else if (!intfs)
return -ENODEV;
- adf_id_t *primary_intfs;
+ adf_id_t *primary_intfs = nullptr;
ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
free(intfs);
if (n_primary_intfs < 0)
return n_primary_intfs;
- else if (!n_primary_intfs)
+ else if (!primary_intfs)
return -ENODEV;
if (!formats) {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 8c11289..2d34e2d 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -388,9 +388,11 @@
if (needle.length() > pos) return std::string::npos;
pos -= needle.length();
// fuzzy match to maximum kBitErrorRate
- do {
+ for (;;) {
if (numError(pos, needle) != std::string::npos) return pos;
- } while (pos-- != 0);
+ if (pos == 0) break;
+ --pos;
+ }
return std::string::npos;
}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index fc88217..cbd8ffa 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,27 @@
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", "-d1", "-f",
+ "-O", "encrypt", "-O", "quota",
+ 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 +131,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/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 61c8804..5781765 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,14 +25,15 @@
#include <unistd.h>
#include <memory>
+#include <android/security/IKeystoreService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <gatekeeper/password_handle.h> // for password_handle_t
#include <hardware/gatekeeper.h>
#include <hardware/hw_auth_token.h>
-#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // For error code
+#include <keystore/keystore_return_types.h>
#include <log/log.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -315,11 +316,15 @@
// TODO: cache service?
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+ sp<security::IKeystoreService> service =
+ interface_cast<security::IKeystoreService>(binder);
if (service != NULL) {
- auto ret = service->addAuthToken(*auth_token, *auth_token_length);
- if (!ret.isOk()) {
- ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
+ std::vector<uint8_t> auth_token_vector(*auth_token,
+ (*auth_token) + *auth_token_length);
+ int result = 0;
+ auto binder_result = service->addAuthToken(auth_token_vector, &result);
+ if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+ ALOGE("Failure sending auth token to KeyStore: %" PRId32, result);
}
} else {
ALOGE("Unable to communicate with KeyStore");
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..ed1413e 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,105 @@
header_libs: ["libbatteryservice_headers"],
export_header_lib_headers: ["libbatteryservice_headers"],
}
+
+cc_library_static {
+ name: "libbatterymonitor",
+ srcs: ["BatteryMonitor.cpp"],
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libutils",
+ "libbase",
+ ],
+ header_libs: ["libhealthd_headers"],
+ export_header_lib_headers: ["libhealthd_headers"],
+}
+
+cc_library_static {
+ name: "android.hardware.health@2.0-impl",
+ vendor_available: true,
+ srcs: [
+ "Health.cpp",
+ "healthd_common.cpp",
+ ],
+
+ cflags: ["-DHEALTHD_USE_HEALTH_2_0"],
+
+ export_include_dirs: ["include"],
+
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "android.hardware.health@2.0",
+ ],
+
+ static_libs: [
+ "libbatterymonitor",
+ "android.hardware.health@1.0-convert",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.health@2.0-service",
+ init_rc: ["android.hardware.health@2.0-service.rc"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: [
+ "HealthServiceCommon.cpp",
+ "HealthServiceDefault.cpp",
+ ],
+
+ cflags: ["-DHEALTH_INSTANCE_NAME=\"default\""],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "android.hardware.health@2.0",
+ ],
+}
+
+cc_binary {
+ name: "healthd",
+ srcs: [
+ "HealthServiceCommon.cpp",
+ "HealthServiceHealthd.cpp",
+ ],
+ local_include_dirs: ["include"],
+
+ cflags: ["-DHEALTH_INSTANCE_NAME=\"backup\""],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "android.hardware.health@1.0",
+ "android.hardware.health@2.0",
+ ],
+
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..d725f73 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,35 +3,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- healthd_mode_android.cpp \
- BatteryPropertiesRegistrar.cpp
-
-LOCAL_MODULE := libhealthd_android
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(LOCAL_PATH) \
- $(LOCAL_PATH)/include
-
-LOCAL_STATIC_LIBRARIES := \
- libbatterymonitor \
- libbatteryservice \
- libutils \
- libbase \
- libcutils \
- liblog \
- libc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_MODULE := libhealthd_draw
@@ -76,6 +47,8 @@
$(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := \
+ android.hardware.health@2.0 \
+ android.hardware.health@1.0 \
libminui \
libpng \
libz \
@@ -103,7 +76,6 @@
endif
LOCAL_SRC_FILES := \
- healthd_common.cpp \
charger.cpp \
LOCAL_MODULE := charger
@@ -125,6 +97,13 @@
endif
LOCAL_STATIC_LIBRARIES := \
+ android.hardware.health@2.0-impl \
+ android.hardware.health@2.0 \
+ android.hardware.health@1.0 \
+ libhidltransport \
+ libhidlbase \
+ libhwbinder \
+ libvndksupport \
libhealthd_charger \
libhealthd_draw \
libbatterymonitor \
@@ -182,41 +161,3 @@
_add-charger-image :=
_img_modules :=
endif # LOCAL_CHARGER_NO_UI
-
-### healthd ###
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- healthd_common.cpp \
- healthd.cpp \
-
-LOCAL_MODULE := healthd
-LOCAL_MODULE_TAGS := optional
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
-endif
-ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
-LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
-endif
-
-LOCAL_STATIC_LIBRARIES := \
- libhealthd_android \
- libbatterymonitor \
- libbatteryservice \
- android.hardware.health@1.0-convert \
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libbase \
- libutils \
- libcutils \
- liblog \
- libm \
- libc \
- libhidlbase \
- libhidltransport \
- android.hardware.health@1.0 \
-
-include $(BUILD_EXECUTABLE)
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
deleted file mode 100644
index e51a06d..0000000
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2013 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 "BatteryPropertiesRegistrar.h"
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/PermissionCache.h>
-#include <private/android_filesystem_config.h>
-#include <utils/Errors.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-
-#include <healthd/healthd.h>
-
-namespace android {
-
-void BatteryPropertiesRegistrar::publish(
- const sp<BatteryPropertiesRegistrar>& service) {
- defaultServiceManager()->addService(String16("batteryproperties"), service);
-}
-
-void BatteryPropertiesRegistrar::notifyListeners(const struct BatteryProperties& props) {
- Vector<sp<IBatteryPropertiesListener> > listenersCopy;
-
- // Binder currently may service an incoming oneway transaction whenever an
- // outbound oneway call is made (if there is already a pending incoming
- // oneway call waiting). This is considered a bug and may change in the
- // future. For now, avoid recursive mutex lock while making outbound
- // calls by making a local copy of the current list of listeners.
- {
- Mutex::Autolock _l(mRegistrationLock);
- listenersCopy = mListeners;
- }
- for (size_t i = 0; i < listenersCopy.size(); i++) {
- listenersCopy[i]->batteryPropertiesChanged(props);
- }
-}
-
-void BatteryPropertiesRegistrar::registerListener(const sp<IBatteryPropertiesListener>& listener) {
- {
- if (listener == NULL)
- return;
- Mutex::Autolock _l(mRegistrationLock);
- // check whether this is a duplicate
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
- return;
- }
- }
-
- mListeners.add(listener);
- IInterface::asBinder(listener)->linkToDeath(this);
- }
- healthd_battery_update();
-}
-
-void BatteryPropertiesRegistrar::unregisterListener(const sp<IBatteryPropertiesListener>& listener) {
- if (listener == NULL)
- return;
- Mutex::Autolock _l(mRegistrationLock);
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
- IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
- mListeners.removeAt(i);
- break;
- }
- }
-}
-
-status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
- return healthd_get_property(id, val);
-}
-
-void BatteryPropertiesRegistrar::scheduleUpdate() {
- healthd_battery_update();
-}
-
-status_t BatteryPropertiesRegistrar::dump(int fd, const Vector<String16>& /*args*/) {
- IPCThreadState* self = IPCThreadState::self();
- const int pid = self->getCallingPid();
- const int uid = self->getCallingUid();
- if ((uid != AID_SHELL) &&
- !PermissionCache::checkPermission(
- String16("android.permission.DUMP"), pid, uid))
- return PERMISSION_DENIED;
-
- healthd_dump_battery_state(fd);
- return OK;
-}
-
-void BatteryPropertiesRegistrar::binderDied(const wp<IBinder>& who) {
- Mutex::Autolock _l(mRegistrationLock);
-
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (IInterface::asBinder(mListeners[i]) == who) {
- mListeners.removeAt(i);
- break;
- }
- }
-}
-
-} // namespace android
diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h
deleted file mode 100644
index 14e9145..0000000
--- a/healthd/BatteryPropertiesRegistrar.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2013 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 HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
-
-#include <binder/IBinder.h>
-#include <utils/Mutex.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-#include <batteryservice/BatteryService.h>
-#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-
-namespace android {
-
-class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar,
- public IBinder::DeathRecipient {
-public:
- void publish(const sp<BatteryPropertiesRegistrar>& service);
- void notifyListeners(const struct BatteryProperties& props);
- void scheduleUpdate();
-
-private:
- Mutex mRegistrationLock;
- Vector<sp<IBatteryPropertiesListener> > mListeners;
-
- void registerListener(const sp<IBatteryPropertiesListener>& listener);
- void unregisterListener(const sp<IBatteryPropertiesListener>& listener);
- status_t getProperty(int id, struct BatteryProperty *val);
- status_t dump(int fd, const Vector<String16>& args);
- void binderDied(const wp<IBinder>& who);
-};
-
-}; // namespace android
-
-#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H
diff --git a/healthd/Health.cpp b/healthd/Health.cpp
new file mode 100644
index 0000000..822f664
--- /dev/null
+++ b/healthd/Health.cpp
@@ -0,0 +1,177 @@
+#define LOG_TAG "Health"
+#include <android-base/logging.h>
+
+#include <health2/Health.h>
+
+#include <hidl/HidlTransportSupport.h>
+
+extern void healthd_battery_update_internal(bool);
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+sp<Health> Health::instance_;
+
+Health::Health(struct healthd_config* c) {
+ // TODO(b/69268160): remove when libhealthd is removed.
+ healthd_board_init(c);
+ battery_monitor_ = std::make_unique<BatteryMonitor>();
+ battery_monitor_->init(c);
+}
+
+// Methods from IHealth follow.
+Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ return Result::SUCCESS;
+ }
+
+ {
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ callbacks_.push_back(callback);
+ // unlock
+ }
+
+ auto linkRet = callback->linkToDeath(this, 0u /* cookie */);
+ if (!linkRet.withDefault(false)) {
+ LOG(WARNING) << __func__ << "Cannot link to death: "
+ << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());
+ // ignore the error
+ }
+
+ return update();
+}
+
+bool Health::unregisterCallbackInternal(const sp<IBase>& callback) {
+ if (callback == nullptr) return false;
+
+ bool removed = false;
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ if (interfacesEqual(*it, callback)) {
+ it = callbacks_.erase(it);
+ removed = true;
+ } else {
+ ++it;
+ }
+ }
+ (void)callback->unlinkToDeath(this).isOk(); // ignore errors
+ return removed;
+}
+
+Return<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) {
+ return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
+}
+
+template<typename T>
+void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue,
+ const std::function<void(Result, T)>& callback) {
+ struct BatteryProperty prop;
+ T ret = defaultValue;
+ Result result = Result::SUCCESS;
+ status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+ if (err != OK) {
+ LOG(DEBUG) << "getProperty(" << id << ")" << " fails: (" << err << ") " << strerror(-err);
+ } else {
+ ret = static_cast<T>(prop.valueInt64);
+ }
+ switch (err) {
+ case OK: result = Result::SUCCESS; break;
+ case NAME_NOT_FOUND: result = Result::NOT_SUPPORTED; break;
+ default: result = Result::UNKNOWN; break;
+ }
+ callback(result, static_cast<T>(ret));
+}
+
+Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb);
+ return Void();
+}
+
+
+Return<Result> Health::update() {
+ if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
+ LOG(WARNING) << "health@2.0: update: not initialized. "
+ << "update() should not be called in charger / recovery.";
+ return Result::UNKNOWN;
+ }
+
+ // Retrieve all information and call healthd_mode_ops->battery_update, which calls
+ // notifyListeners.
+ bool chargerOnline = battery_monitor_->update();
+
+ // adjust uevent / wakealarm periods
+ healthd_battery_update_internal(chargerOnline);
+
+ return Result::SUCCESS;
+}
+
+void Health::notifyListeners(const HealthInfo& info) {
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ auto ret = (*it)->healthInfoChanged(info);
+ if (!ret.isOk() && ret.isDeadObject()) {
+ it = callbacks_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
+ if (handle != nullptr && handle->numFds >= 1) {
+ int fd = handle->data[0];
+ battery_monitor_->dumpState(fd);
+ fsync(fd);
+ }
+ return Void();
+}
+
+void Health::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {
+ (void)unregisterCallbackInternal(who.promote());
+}
+
+sp<IHealth> Health::initInstance(struct healthd_config* c) {
+ if (instance_ == nullptr) {
+ instance_ = new Health(c);
+ }
+ return instance_;
+}
+
+sp<Health> Health::getImplementation() {
+ CHECK(instance_ != nullptr);
+ return instance_;
+}
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/healthd/HealthServiceCommon.cpp b/healthd/HealthServiceCommon.cpp
new file mode 100644
index 0000000..266ca3a
--- /dev/null
+++ b/healthd/HealthServiceCommon.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This is a reference implementation for health@2.0 HAL. A vendor
+// can write its own HealthService.cpp with customized init and update functions.
+
+#define LOG_TAG "health@2.0/" HEALTH_INSTANCE_NAME
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::hardware::IPCThreadState;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::implementation::Health;
+
+extern int healthd_main(void);
+
+static void binder_event(uint32_t /*epevents*/) {
+ IPCThreadState::self()->handlePolledCommands();
+}
+
+void healthd_mode_service_2_0_init(struct healthd_config* config) {
+ int binderFd;
+
+ LOG(INFO) << LOG_TAG << " Hal is starting up...";
+
+ configureRpcThreadpool(1, false /* callerWillJoin */);
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ IPCThreadState::self()->setupPolling(&binderFd);
+
+ if (binderFd >= 0) {
+ if (healthd_register_event(binderFd, binder_event))
+ LOG(ERROR) << LOG_TAG << ": Register for binder events failed";
+ }
+
+ android::sp<IHealth> service = Health::initInstance(config);
+ CHECK_EQ(service->registerAsService(HEALTH_INSTANCE_NAME), android::OK)
+ << LOG_TAG << ": Failed to register HAL";
+
+ LOG(INFO) << LOG_TAG << ": Hal init done";
+}
+
+int healthd_mode_service_2_0_preparetowait(void) {
+ IPCThreadState::self()->flushCommands();
+ return -1;
+}
+
+void healthd_mode_service_2_0_heartbeat(void) {
+ // noop
+}
+
+void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
+ HealthInfo info;
+ convertToHealthInfo(prop, info);
+ Health::getImplementation()->notifyListeners(info);
+}
+
+static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
+ .init = healthd_mode_service_2_0_init,
+ .preparetowait = healthd_mode_service_2_0_preparetowait,
+ .heartbeat = healthd_mode_service_2_0_heartbeat,
+ .battery_update = healthd_mode_service_2_0_battery_update,
+};
+
+int main() {
+ healthd_mode_ops = &healthd_mode_service_2_0_ops;
+ LOG(INFO) << LOG_TAG << ": Hal starting main loop...";
+ return healthd_main();
+}
diff --git a/healthd/HealthServiceDefault.cpp b/healthd/HealthServiceDefault.cpp
new file mode 100644
index 0000000..dbc480f
--- /dev/null
+++ b/healthd/HealthServiceDefault.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#include <healthd/healthd.h>
+
+void healthd_board_init(struct healthd_config*) {
+ // Implementation-defined init logic goes here.
+ // 1. config->periodic_chores_interval_* variables
+ // 2. config->battery*Path variables
+ // 3. config->energyCounter. In this implementation, energyCounter is not defined.
+
+ // use defaults
+}
+
+int healthd_board_battery_update(struct android::BatteryProperties*) {
+ // Implementation-defined update logic goes here. An implementation
+ // can make modifications to prop before broadcasting it to all callbacks.
+
+ // return 0 to log periodic polled battery status to kernel log
+ return 0;
+}
diff --git a/healthd/HealthServiceHealthd.cpp b/healthd/HealthServiceHealthd.cpp
new file mode 100644
index 0000000..72a446a
--- /dev/null
+++ b/healthd/HealthServiceHealthd.cpp
@@ -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.
+ */
+
+#define LOG_TAG "healthd"
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::OK;
+using android::NAME_NOT_FOUND;
+using android::hardware::health::V1_0::HealthConfig;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::Result;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
+using android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+
+using IHealthLegacy = android::hardware::health::V1_0::IHealth;
+
+static android::sp<IHealthLegacy> gHealth_1_0;
+
+static int healthd_board_get_energy_counter(int64_t* energy) {
+ if (gHealth_1_0 == nullptr) {
+ return NAME_NOT_FOUND;
+ }
+
+ Result result = Result::NOT_SUPPORTED;
+ gHealth_1_0->energyCounter([energy, &result](Result ret, int64_t energyOut) {
+ result = ret;
+ *energy = energyOut;
+ });
+
+ return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
+}
+
+void healthd_board_init(struct healthd_config* config) {
+ gHealth_1_0 = IHealthLegacy::getService();
+
+ if (gHealth_1_0 == nullptr) {
+ return;
+ }
+
+ HealthConfig halConfig{};
+ convertToHealthConfig(config, halConfig);
+ gHealth_1_0->init(halConfig, [config](const auto& halConfigOut) {
+ convertFromHealthConfig(halConfigOut, config);
+ // always redirect energy counter queries
+ config->energyCounter = healthd_board_get_energy_counter;
+ });
+ LOG(INFO) << LOG_TAG << ": redirecting calls to 1.0 health HAL";
+}
+
+// TODO(b/68724651): Move this function into healthd_mode_service_2_0_battery_update
+// with logthis returned.
+int healthd_board_battery_update(struct android::BatteryProperties* props) {
+ int logthis = 0;
+
+ if (gHealth_1_0 == nullptr) {
+ return logthis;
+ }
+
+ HealthInfo info;
+ convertToHealthInfo(props, info);
+ gHealth_1_0->update(info, [props, &logthis](int32_t ret, const auto& infoOut) {
+ logthis = ret;
+ convertFromHealthInfo(infoOut, props);
+ });
+
+ return logthis;
+}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
new file mode 100644
index 0000000..8b86868
--- /dev/null
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -0,0 +1,4 @@
+service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
+ class hal
+ user system
+ group system
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 5a8fe1a..ede14f2 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "charger"
#define KLOG_LEVEL 6
+#include <health2/Health.h>
#include <healthd/healthd.h>
#include <stdlib.h>
@@ -62,7 +63,9 @@
};
#endif
-static void healthd_mode_nop_init(struct healthd_config* /*config*/) {
+static void healthd_mode_nop_init(struct healthd_config* config) {
+ using android::hardware::health::V2_0::implementation::Health;
+ Health::initInstance(config);
}
static int healthd_mode_nop_preparetowait(void) {
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
deleted file mode 100644
index ed1971a..0000000
--- a/healthd/healthd.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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 "healthd"
-#define KLOG_LEVEL 6
-
-#include <healthd/healthd.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <cutils/klog.h>
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <hal_conversion.h>
-
-using namespace android;
-
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// device specific hal interface;
-static sp<IHealth> gHealth;
-
-// main healthd loop
-extern int healthd_main(void);
-
-// Android mode
-extern void healthd_mode_android_init(struct healthd_config *config);
-extern int healthd_mode_android_preparetowait(void);
-extern void healthd_mode_android_heartbeat(void);
-extern void healthd_mode_android_battery_update(
- struct android::BatteryProperties *props);
-
-static struct healthd_mode_ops android_ops = {
- .init = healthd_mode_android_init,
- .preparetowait = healthd_mode_android_preparetowait,
- .heartbeat = healthd_mode_android_heartbeat,
- .battery_update = healthd_mode_android_battery_update,
-};
-
-// default energy counter property redirect to talk to device
-// HAL
-static int healthd_board_get_energy_counter(int64_t *energy) {
-
- if (gHealth == nullptr) {
- return NAME_NOT_FOUND;
- }
-
- Result result = Result::NOT_SUPPORTED;
- gHealth->energyCounter([=, &result] (Result ret, int64_t energyOut) {
- result = ret;
- *energy = energyOut;
- });
-
- return result == Result::SUCCESS ? OK : NAME_NOT_FOUND;
-}
-
-void healthd_board_init(struct healthd_config *config) {
-
- // Initialize the board HAL - Equivalent of healthd_board_init(config)
- // in charger/recovery mode.
-
- gHealth = IHealth::getService();
- if (gHealth == nullptr) {
- KLOG_WARNING(LOG_TAG, "unable to get HAL interface, using defaults\n");
- return;
- }
-
- HealthConfig halConfig;
- convertToHealthConfig(config, halConfig);
- gHealth->init(halConfig, [=] (const auto &halConfigOut) {
- convertFromHealthConfig(halConfigOut, config);
- // always redirect energy counter queries
- config->energyCounter = healthd_board_get_energy_counter;
- });
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties *props) {
- int logthis = 0;
-
- if (gHealth == nullptr) {
- return logthis;
- }
-
- HealthInfo info;
- convertToHealthInfo(props, info);
- gHealth->update(info,
- [=, &logthis] (int32_t ret, const auto &infoOut) {
- logthis = ret;
- convertFromHealthInfo(infoOut, props);
- });
-
- return logthis;
-}
-
-int main(int /*argc*/, char ** /*argv*/) {
-
- healthd_mode_ops = &android_ops;
-
- return healthd_main();
-}
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
index 6599919..140c49d 100644
--- a/healthd/healthd_common.cpp
+++ b/healthd/healthd_common.cpp
@@ -33,6 +33,10 @@
#include <sys/timerfd.h>
#include <utils/Errors.h>
+#ifdef HEALTHD_USE_HEALTH_2_0
+#include <health2/Health.h>
+#endif
+
using namespace android;
#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
@@ -84,9 +88,13 @@
static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-static BatteryMonitor* gBatteryMonitor;
+#ifndef HEALTHD_USE_HEALTH_2_0
+static BatteryMonitor* gBatteryMonitor = nullptr;
+#else
+using ::android::hardware::health::V2_0::implementation::Health;
+#endif
-struct healthd_mode_ops *healthd_mode_ops;
+struct healthd_mode_ops *healthd_mode_ops = nullptr;
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
struct epoll_event ev;
@@ -127,17 +135,87 @@
KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
}
+#ifdef HEALTHD_USE_HEALTH_2_0
+status_t convertStatus(android::hardware::health::V2_0::Result r) {
+ using android::hardware::health::V2_0::Result;
+ switch(r) {
+ case Result::SUCCESS: return OK;
+ case Result::NOT_SUPPORTED: return BAD_VALUE;
+ case Result::NOT_FOUND: return NAME_NOT_FOUND;
+ case Result::CALLBACK_DIED: return DEAD_OBJECT;
+ case Result::UNKNOWN: // fallthrough
+ default:
+ return UNKNOWN_ERROR;
+ }
+}
+#endif
+
status_t healthd_get_property(int id, struct BatteryProperty *val) {
+#ifndef HEALTHD_USE_HEALTH_2_0
return gBatteryMonitor->getProperty(id, val);
+#else
+ using android::hardware::health::V1_0::BatteryStatus;
+ using android::hardware::health::V2_0::Result;
+ val->valueInt64 = INT64_MIN;
+ status_t err = UNKNOWN_ERROR;
+ switch (id) {
+ case BATTERY_PROP_CHARGE_COUNTER: {
+ Health::getImplementation()->getChargeCounter([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CURRENT_NOW: {
+ Health::getImplementation()->getCurrentNow([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CURRENT_AVG: {
+ Health::getImplementation()->getCurrentAverage([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CAPACITY: {
+ Health::getImplementation()->getCapacity([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_ENERGY_COUNTER: {
+ Health::getImplementation()->getEnergyCounter([&](Result r, int64_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_BATTERY_STATUS: {
+ Health::getImplementation()->getChargeStatus([&](Result r, BatteryStatus v) {
+ err = convertStatus(r);
+ val->valueInt64 = static_cast<int64_t>(v);
+ });
+ break;
+ }
+ default: {
+ err = BAD_VALUE;
+ break;
+ }
+ }
+ return err;
+#endif
}
-void healthd_battery_update(void) {
+void healthd_battery_update_internal(bool charger_online) {
// Fast wake interval when on charger (watch for overheat);
// slow wake interval when on battery (watch for drained battery).
- int new_wake_interval = gBatteryMonitor->update() ?
- healthd_config.periodic_chores_interval_fast :
- healthd_config.periodic_chores_interval_slow;
+ int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast
+ : healthd_config.periodic_chores_interval_slow;
if (new_wake_interval != wakealarm_wake_interval)
wakealarm_set_interval(new_wake_interval);
@@ -155,8 +233,25 @@
-1 : healthd_config.periodic_chores_interval_fast * 1000;
}
+void healthd_battery_update(void) {
+#ifndef HEALTHD_USE_HEALTH_2_0
+ healthd_battery_update_internal(gBatteryMonitor->update());
+#else
+ Health::getImplementation()->update();
+#endif
+}
+
void healthd_dump_battery_state(int fd) {
+#ifndef HEALTHD_USE_HEALTH_2_0
gBatteryMonitor->dumpState(fd);
+#else
+ native_handle_t* nativeHandle = native_handle_create(1, 0);
+ nativeHandle->data[0] = fd;
+ ::android::hardware::hidl_handle handle;
+ handle.setTo(nativeHandle, true /* shouldOwn */);
+ Health::getImplementation()->debug(handle, {} /* options */);
+#endif
+
fsync(fd);
}
@@ -273,12 +368,21 @@
return -1;
}
+#ifndef HEALTHD_USE_HEALTH_2_0
healthd_board_init(&healthd_config);
+#else
+ // healthd_board_* functions are removed in health@2.0
+#endif
+
healthd_mode_ops->init(&healthd_config);
wakealarm_init();
uevent_init();
+
+#ifndef HEALTHD_USE_HEALTH_2_0
gBatteryMonitor = new BatteryMonitor();
gBatteryMonitor->init(&healthd_config);
+#endif
+
return 0;
}
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
deleted file mode 100644
index c612313..0000000
--- a/healthd/healthd_mode_android.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2013 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 "healthd-android"
-
-#include <healthd/healthd.h>
-#include "BatteryPropertiesRegistrar.h"
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <cutils/klog.h>
-#include <sys/epoll.h>
-
-using namespace android;
-
-static int gBinderFd;
-static sp<BatteryPropertiesRegistrar> gBatteryPropertiesRegistrar;
-
-void healthd_mode_android_battery_update(
- struct android::BatteryProperties *props) {
- if (gBatteryPropertiesRegistrar != NULL)
- gBatteryPropertiesRegistrar->notifyListeners(*props);
-
- return;
-}
-
-int healthd_mode_android_preparetowait(void) {
- IPCThreadState::self()->flushCommands();
- return -1;
-}
-
-void healthd_mode_android_heartbeat(void) {
-}
-
-static void binder_event(uint32_t /*epevents*/) {
- IPCThreadState::self()->handlePolledCommands();
-}
-
-void healthd_mode_android_init(struct healthd_config* /*config*/) {
- ProcessState::self()->setThreadPoolMaxThreadCount(0);
- IPCThreadState::self()->disableBackgroundScheduling(true);
- IPCThreadState::self()->setupPolling(&gBinderFd);
-
- if (gBinderFd >= 0) {
- if (healthd_register_event(gBinderFd, binder_event))
- KLOG_ERROR(LOG_TAG,
- "Register for binder events failed\n");
- }
-
- gBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
- gBatteryPropertiesRegistrar->publish(gBatteryPropertiesRegistrar);
-}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 4f77e7a..61e7465 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -49,6 +49,7 @@
#include "AnimationParser.h"
#include "healthd_draw.h"
+#include <health2/Health.h>
#include <healthd/healthd.h>
using namespace android;
@@ -612,6 +613,8 @@
}
void healthd_mode_charger_init(struct healthd_config* config) {
+ using android::hardware::health::V2_0::implementation::Health;
+
int ret;
charger* charger = &charger_state;
int i;
@@ -666,6 +669,10 @@
charger->next_screen_transition = -1;
charger->next_key_check = -1;
charger->next_pwr_check = -1;
+
+ // Initialize Health implementation (which initializes the internal BatteryMonitor).
+ Health::initInstance(config);
+
healthd_config = config;
charger->boot_min_cap = config->boot_min_cap;
}
diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h
new file mode 100644
index 0000000..012b95b
--- /dev/null
+++ b/healthd/include/health2/Health.h
@@ -0,0 +1,70 @@
+#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/1.0/types.h>
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthd/BatteryMonitor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+using V1_0::BatteryStatus;
+using V1_0::HealthInfo;
+
+using ::android::hidl::base::V1_0::IBase;
+
+struct Health : public IHealth, hidl_death_recipient {
+ public:
+ static sp<IHealth> initInstance(struct healthd_config* c);
+ // Should only be called by implementation itself (-impl, -service).
+ // Clients should not call this function. Instead, initInstance() initializes and returns the
+ // global instance that has fewer functions.
+ // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
+ static sp<Health> getImplementation();
+
+ Health(struct healthd_config* c);
+
+ // TODO(b/62229583): clean up and hide these functions after update() logic is simplified.
+ void notifyListeners(const HealthInfo& info);
+
+ // Methods from IHealth follow.
+ Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override;
+ Return<Result> unregisterCallback(const sp<IHealthInfoCallback>& callback) override;
+ Return<Result> update() override;
+ Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
+ Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
+ Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
+ Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
+ Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
+ Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+ void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override;
+
+ private:
+ static sp<Health> instance_;
+
+ std::mutex callbacks_lock_;
+ std::vector<sp<IHealthInfoCallback>> callbacks_;
+ std::unique_ptr<BatteryMonitor> battery_monitor_;
+
+ bool unregisterCallbackInternal(const sp<IBase>& cb);
+
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace health
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8865a7d..f9067cc 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
#define HEALTHD_BATTERYMONITOR_H
#include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
#include <utils/String8.h>
#include <utils/Vector.h>
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 17efbd6..97c7a8c 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -82,8 +82,13 @@
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
void healthd_battery_update();
+
+// deprecated.
+// TODO(b/62229583): This function should be removed since it is only used by
+// BatteryPropertiesRegistrar.
android::status_t healthd_get_property(int id,
struct android::BatteryProperty *val);
+
void healthd_dump_battery_state(int fd);
struct healthd_mode_ops {
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index a8dd673..86cc873 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -137,8 +137,15 @@
/* Returns the USB product ID from the device descriptor for the USB device */
uint16_t usb_device_get_product_id(struct usb_device *device);
+/* Returns a pointer to device descriptor */
const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
+/* Returns the length in bytes read into the raw descriptors array */
+size_t usb_device_get_descriptors_length(const struct usb_device* device);
+
+/* Returns a pointer to the raw descriptors array */
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device);
+
/* Returns a USB descriptor string for the given string ID.
* Used to implement usb_device_get_manufacturer_name,
* usb_device_get_product_name and usb_device_get_serial.
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 44b878d..77c264b 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -80,9 +80,11 @@
int wddbus;
};
+#define MAX_DESCRIPTORS_LENGTH 4096
+
struct usb_device {
char dev_name[64];
- unsigned char desc[4096];
+ unsigned char desc[MAX_DESCRIPTORS_LENGTH];
int desc_length;
int fd;
int writeable;
@@ -381,6 +383,8 @@
return device;
failed:
+ // TODO It would be more appropriate to have callers do this
+ // since this function doesn't "own" this file descriptor.
close(fd);
free(device);
return NULL;
@@ -449,11 +453,18 @@
return __le16_to_cpu(desc->idProduct);
}
-const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
-{
+const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device* device) {
return (struct usb_device_descriptor*)device->desc;
}
+size_t usb_device_get_descriptors_length(const struct usb_device* device) {
+ return device->desc_length;
+}
+
+const unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device) {
+ return device->desc;
+}
+
char* usb_device_get_string(struct usb_device *device, int id, int timeout)
{
char string[256];
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 fcf2cd8..14b45b1 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -105,6 +105,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 aa970d6..560092e 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -206,10 +206,6 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-vndk_lib_md5 := $(word 1, $(shell echo $(LLNDK_LIBRARIES) $(VNDK_SAMEPROCESS_LIBRARIES) | $(MD5SUM)))
-vndk_lib_dep := $(intermediates)/$(vndk_lib_md5).dep
-$(vndk_lib_dep):
- $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@
llndk_libraries := $(subst $(space),:,$(addsuffix .so,$(LLNDK_LIBRARIES)))
@@ -230,7 +226,7 @@
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_PRIVATE_LIBRARIES := $(llndk_private_libraries)
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(vndk_core_libraries)
$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in $(vndk_lib_dep)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in
@echo "Generate: $< -> $@"
@mkdir -p $(dir $@)
$(hide) sed -e 's?%LLNDK_LIBRARIES%?$(PRIVATE_LLNDK_LIBRARIES)?g' $< >$@
@@ -238,8 +234,6 @@
$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $@
$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
-vndk_lib_md5 :=
-vndk_lib_dep :=
llndk_libraries :=
vndk_sameprocess_libraries :=
vndk_core_libraries :=
@@ -266,13 +260,8 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-llndk_md5 = $(word 1, $(shell echo $(LLNDK_LIBRARIES) | $(MD5SUM)))
-llndk_dep = $(intermediates)/$(llndk_md5).dep
-$(llndk_dep):
- $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@
-
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
-$(LOCAL_BUILT_MODULE): $(llndk_dep)
+$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
$(hide) echo -n > $@
@@ -287,13 +276,8 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-vndksp_md5 = $(word 1, $(shell echo $(LLNDK_LIBRARIES) | $(MD5SUM)))
-vndksp_dep = $(intermediates)/$(vndksp_md5).dep
-$(vndksp_dep):
- $(hide) mkdir -p $(dir $@) && rm -rf $(dir $@)*.dep && touch $@
-
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
-$(LOCAL_BUILT_MODULE): $(vndksp_dep)
+$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
$(hide) echo -n > $@
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index 2c73056..1b274d6 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -30,10 +30,10 @@
namespace.default.search.paths = /system/${LIB}
# /vendor/app, /vendor/framework were added since libart should be able to dlopen
# the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
+namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
###############################################################################
# "sphal" namespace
@@ -51,7 +51,7 @@
namespace.sphal.isolated = true
namespace.sphal.visible = true
namespace.sphal.search.paths = /vendor/${LIB}/egl:/vendor/${LIB}/hw:/vendor/${LIB}
-namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp/hw
+namespace.sphal.permitted.paths = /vendor/${LIB}:/system/${LIB}/vndk-sp${VNDK_VER}/hw
namespace.sphal.asan.search.paths = /data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}:/vendor/${LIB}
namespace.sphal.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}
@@ -79,10 +79,10 @@
###############################################################################
namespace.rs.isolated = true
namespace.rs.visible = true
-namespace.rs.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/vendor/${LIB}
+namespace.rs.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}
namespace.rs.permitted.paths = /vendor/${LIB}:/data
-namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/vendor/${LIB}:/vendor/${LIB}
+namespace.rs.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}:/data/asan/vendor/${LIB}:/vendor/${LIB}
namespace.rs.asan.permitted.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data
namespace.rs.links = default,vndk
@@ -96,10 +96,10 @@
###############################################################################
namespace.vndk.isolated = true
namespace.vndk.visible = true
-namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
-namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.vndk.asan.search.paths = /data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
namespace.vndk.asan.permitted.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl
# When these NDK libs are required inside this namespace, then it is redirected
@@ -125,11 +125,11 @@
namespace.default.isolated = true
namespace.default.visible = true
-namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
-namespace.default.permitted.paths = /vendor:/system/${LIB}/vndk:/system/${LIB}/vndk-sp
+namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/vendor/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.permitted.paths = /vendor:/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk:/vendor/${LIB}/vndk:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
-namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
+namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk${VNDK_VER}:/vendor/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/vendor/${LIB}/vndk-sp${VNDK_VER}:/vendor/${LIB}/vndk-sp${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk${VNDK_VER}:/system/${LIB}/vndk${VNDK_VER}:/data/asan/system/${LIB}/vndk-sp${VNDK_VER}:/system/${LIB}/vndk-sp${VNDK_VER}
namespace.default.links = system
namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2b7d11f..a3213cd 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -712,7 +712,7 @@
shutdown critical
service healthd /system/bin/healthd
- class core
+ class hal
critical
group root system wakelock
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
index 95559d7..10d0f04 100644
--- a/sdcard/fuse.cpp
+++ b/sdcard/fuse.cpp
@@ -323,7 +323,7 @@
/* Root always has access; access for any other UIDs should always
* be controlled through packages.list. */
- if (hdr->uid == 0) {
+ if (hdr->uid == AID_ROOT) {
return true;
}
diff --git a/storaged/Android.bp b/storaged/Android.bp
new file mode 100644
index 0000000..35868bb
--- /dev/null
+++ b/storaged/Android.bp
@@ -0,0 +1,102 @@
+/*
+ * 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: [
+ "android.hardware.health@1.0",
+ "android.hardware.health@2.0",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libsysutils",
+ "libutils",
+ "libz",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wno-unused-parameter"
+ ],
+}
+
+cc_library_static {
+ name: "libstoraged",
+
+ defaults: ["storaged_defaults"],
+
+ aidl: {
+ export_aidl_headers: true,
+ local_include_dirs: ["binder"],
+ include_dirs: ["frameworks/native/aidl/binder"],
+ },
+
+ srcs: [
+ "storaged.cpp",
+ "storaged_diskstats.cpp",
+ "storaged_info.cpp",
+ "storaged_service.cpp",
+ "storaged_utils.cpp",
+ "storaged_uid_monitor.cpp",
+ "uid_info.cpp",
+ "storaged.proto",
+ "binder/android/os/IStoraged.aidl",
+ "binder/android/os/storaged/IStoragedPrivate.aidl",
+ ],
+
+ 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"],
+}
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/binder/android/os/IStoraged.aidl b/storaged/binder/android/os/IStoraged.aidl
new file mode 100644
index 0000000..f81e904
--- /dev/null
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IStoraged {
+ void onUserStarted(int userId);
+ void onUserStopped(int userId);
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/IStoragedPrivate.aidl b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
new file mode 100644
index 0000000..9c888e3
--- /dev/null
+++ b/storaged/binder/android/os/storaged/IStoragedPrivate.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package android.os.storaged;
+
+import android.os.storaged.UidInfo;
+
+/** {@hide} */
+interface IStoragedPrivate {
+ UidInfo[] dumpUids();
+ int[] dumpPerfHistory();
+}
\ No newline at end of file
diff --git a/storaged/binder/android/os/storaged/UidInfo.aidl b/storaged/binder/android/os/storaged/UidInfo.aidl
new file mode 100644
index 0000000..440f386
--- /dev/null
+++ b/storaged/binder/android/os/storaged/UidInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.os.storaged;
+
+parcelable UidInfo cpp_header "include/uid_info.h";
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index fa68406..e5dd70d 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -27,25 +27,13 @@
#include <vector>
#include <batteryservice/IBatteryPropertiesListener.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
+#include <utils/Mutex.h>
-#include "storaged_info.h"
-#include "storaged_uid_monitor.h"
-
-using namespace android;
+#include <android/hardware/health/2.0/IHealth.h>
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
-/* For debug */
-#ifdef DEBUG
-#define debuginfo(fmt, ...) \
- do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
- while(0)
-#else
-#define debuginfo(...)
-#endif
-
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define SECTOR_SIZE ( 512 )
@@ -55,184 +43,24 @@
#define SEC_TO_USEC ( 1000000 )
#define HOUR_TO_SEC ( 3600 )
#define DAY_TO_SEC ( 3600 * 24 )
+#define WEEK_TO_DAYS ( 7 )
+#define YEAR_TO_WEEKS ( 52 )
-// number of attributes diskstats has
-#define DISK_STATS_SIZE ( 11 )
-// maximum size limit of a stats file
-#define DISK_STATS_FILE_MAX_SIZE ( 256 )
-#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
-struct disk_stats {
- /* It will be extremely unlikely for any of the following entries to overflow.
- * For read_bytes(which will be greater than any of the following entries), it
- * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
- * is the peak memory transfer rate for current memory.
- * The diskstats entries (first 11) need to be at top in this structure _after_
- * compiler's optimization.
- */
- uint64_t read_ios; // number of read I/Os processed
- uint64_t read_merges; // number of read I/Os merged with in-queue I/Os
- uint64_t read_sectors; // number of sectors read
- uint64_t read_ticks; // total wait time for read requests
- uint64_t write_ios; // number of write I/Os processed
- uint64_t write_merges; // number of write I/Os merged with in-queue I/Os
- uint64_t write_sectors; // number of sectors written
- uint64_t write_ticks; // total wait time for write requests
- uint64_t io_in_flight; // number of I/Os currently in flight
- uint64_t io_ticks; // total time this block device has been active
- uint64_t io_in_queue; // total wait time for all requests
+#include "storaged_diskstats.h"
+#include "storaged_info.h"
+#include "storaged_uid_monitor.h"
+#include "storaged.pb.h"
+#include "uid_info.h"
- uint64_t start_time; // monotonic time accounting starts
- uint64_t end_time; // monotonic time accounting ends
- uint32_t counter; // private counter for accumulate calculations
- double io_avg; // average io_in_flight for accumulate calculations
-};
-
-
-
-struct disk_perf {
- uint32_t read_perf; // read speed (kbytes/s)
- uint32_t read_ios; // read I/Os per second
- uint32_t write_perf; // write speed (kbytes/s)
- uint32_t write_ios; // write I/Os per second
- uint32_t queue; // I/Os in queue
-};
-
-#define CMD_MAX_LEN ( 64 )
-struct task_info {
- uint32_t pid; // task id
- uint64_t rchar; // characters read
- uint64_t wchar; // characters written
- uint64_t syscr; // read syscalls
- uint64_t syscw; // write syscalls
- uint64_t read_bytes; // bytes read (from storage layer)
- uint64_t write_bytes; // bytes written (to storage layer)
- uint64_t cancelled_write_bytes; // cancelled write byte by truncate
-
- uint64_t starttime; // start time of task
-
- char cmd[CMD_MAX_LEN]; // filename of the executable
-};
-
-class lock_t {
- sem_t* mSem;
-public:
- lock_t(sem_t* sem) {
- mSem = sem;
- sem_wait(mSem);
- }
- ~lock_t() {
- sem_post(mSem);
- }
-};
-
-class stream_stats {
-private:
- double mSum;
- double mSquareSum;
- uint32_t mCnt;
-public:
- stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
- ~stream_stats() {};
- double get_mean() {
- return mSum / mCnt;
- }
- double get_std() {
- return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
- }
- void add(uint32_t num) {
- mSum += (double)num;
- mSquareSum += (double)num * (double)num;
- mCnt++;
- }
- void evict(uint32_t num) {
- if (mSum < num || mSquareSum < (double)num * (double)num) return;
- mSum -= (double)num;
- mSquareSum -= (double)num * (double)num;
- mCnt--;
- }
-};
-
-#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
-#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
-#define UID_IO_STATS_PATH "/proc/uid_io/stats"
-
-class disk_stats_monitor {
-private:
- FRIEND_TEST(storaged_test, disk_stats_monitor);
- const char* DISK_STATS_PATH;
- struct disk_stats mPrevious;
- struct disk_stats mAccumulate;
- bool mStall;
- std::queue<struct disk_perf> mBuffer;
- struct {
- stream_stats read_perf; // read speed (bytes/s)
- stream_stats read_ios; // read I/Os per second
- stream_stats write_perf; // write speed (bytes/s)
- stream_stats write_ios; // write I/O per second
- stream_stats queue; // I/Os in queue
- } mStats;
- bool mValid;
- const uint32_t mWindow;
- const double mSigma;
- struct disk_perf mMean;
- struct disk_perf mStd;
-
- void update_mean();
- void update_std();
- void add(struct disk_perf* perf);
- void evict(struct disk_perf* perf);
- bool detect(struct disk_perf* perf);
-
- void update(struct disk_stats* stats);
-
-public:
- disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
- mStall(false),
- mValid(false),
- mWindow(window_size),
- mSigma(sigma) {
- memset(&mPrevious, 0, sizeof(mPrevious));
- memset(&mMean, 0, sizeof(mMean));
- memset(&mStd, 0, sizeof(mStd));
-
- if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
- DISK_STATS_PATH = MMC_DISK_STATS_PATH;
- } else {
- DISK_STATS_PATH = SDA_DISK_STATS_PATH;
- }
- }
- void update(void);
-};
-
-class disk_stats_publisher {
-private:
- FRIEND_TEST(storaged_test, disk_stats_publisher);
- const char* DISK_STATS_PATH;
- struct disk_stats mAccumulate;
- struct disk_stats mPrevious;
-public:
- disk_stats_publisher(void) {
- memset(&mAccumulate, 0, sizeof(struct disk_stats));
- memset(&mPrevious, 0, sizeof(struct disk_stats));
-
- if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
- DISK_STATS_PATH = MMC_DISK_STATS_PATH;
- } else {
- DISK_STATS_PATH = SDA_DISK_STATS_PATH;
- }
- }
-
- ~disk_stats_publisher(void) {}
- void publish(void);
- void update(void);
-};
+using namespace std;
+using namespace android;
// Periodic chores intervals in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT ( 300 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO ( 3600 )
// UID IO threshold in bytes
#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
@@ -241,25 +69,32 @@
int periodic_chores_interval_unit;
int periodic_chores_interval_disk_stats_publish;
int periodic_chores_interval_uid_io;
- bool proc_uid_io_available; // whether uid_io is accessible
- bool diskstats_available; // whether diskstats is accessible
+ int periodic_chores_interval_flush_proto;
int event_time_check_usec; // check how much cputime spent in event loop
};
-class storaged_t : public BnBatteryPropertiesListener,
- public IBinder::DeathRecipient {
-private:
+class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
+ public android::hardware::hidl_death_recipient {
+ private:
time_t mTimer;
storaged_config mConfig;
- disk_stats_publisher mDiskStats;
disk_stats_monitor mDsm;
uid_monitor mUidm;
time_t mStarttime;
- sp<IBatteryPropertiesRegistrar> battery_properties;
- std::unique_ptr<storage_info_t> storage_info;
+ sp<android::hardware::health::V2_0::IHealth> health;
+ unique_ptr<storage_info_t> storage_info;
+ static const uint32_t crc_init;
+ unordered_map<userid_t, bool> proto_loaded;
+ void load_proto(userid_t user_id);
+ void prepare_proto(userid_t user_id, StoragedProto* proto);
+ void flush_proto(userid_t user_id, StoragedProto* proto);
+ void flush_proto_user_system(StoragedProto* proto);
+ string proto_path(userid_t user_id) {
+ return string("/data/misc_ce/") + to_string(user_id) +
+ "/storaged/storaged.proto";
+ }
public:
storaged_t(void);
- ~storaged_t() {}
void event(void);
void event_checked(void);
void pause(void) {
@@ -270,24 +105,36 @@
return mStarttime;
}
- std::unordered_map<uint32_t, struct uid_info> get_uids(void) {
+ unordered_map<uint32_t, uid_info> get_uids(void) {
return mUidm.get_uid_io_stats();
}
- std::map<uint64_t, struct uid_records> get_uid_records(
+
+ vector<int> get_perf_history(void) {
+ return storage_info->get_perf_history();
+ }
+
+ map<uint64_t, struct uid_records> get_uid_records(
double hours, uint64_t threshold, bool force_report) {
return mUidm.dump(hours, threshold, force_report);
}
+
void update_uid_io_interval(int interval) {
if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
mConfig.periodic_chores_interval_uid_io = interval;
}
}
- void init_battery_service();
- virtual void batteryPropertiesChanged(struct BatteryProperties props);
- void binderDied(const wp<IBinder>& who);
+ void add_user_ce(userid_t user_id);
+ void remove_user_ce(userid_t user_id);
+
+ void init_health_service();
+ virtual ::android::hardware::Return<void> healthInfoChanged(
+ const ::android::hardware::health::V1_0::HealthInfo& info);
+ void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
void report_storage_info();
+
+ void flush_protos(unordered_map<int, StoragedProto>* protos);
};
// Eventlog tag
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
new file mode 100644
index 0000000..ff030f6
--- /dev/null
+++ b/storaged/include/storaged_diskstats.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STORAGED_DISKSTATS_H_
+#define _STORAGED_DISKSTATS_H_
+
+#include <stdint.h>
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+
+struct disk_stats {
+ /* It will be extremely unlikely for any of the following entries to overflow.
+ * For read_bytes(which will be greater than any of the following entries), it
+ * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+ * is the peak memory transfer rate for current memory.
+ * The diskstats entries (first 11) need to be at top in this structure _after_
+ * compiler's optimization.
+ */
+ uint64_t read_ios; // number of read I/Os processed
+ uint64_t read_merges; // number of read I/Os merged with in-queue I/Os
+ uint64_t read_sectors; // number of sectors read
+ uint64_t read_ticks; // total wait time for read requests
+ uint64_t write_ios; // number of write I/Os processed
+ uint64_t write_merges; // number of write I/Os merged with in-queue I/Os
+ uint64_t write_sectors; // number of sectors written
+ uint64_t write_ticks; // total wait time for write requests
+ uint64_t io_in_flight; // number of I/Os currently in flight
+ uint64_t io_ticks; // total time this block device has been active
+ uint64_t io_in_queue; // total wait time for all requests
+
+ uint64_t start_time; // monotonic time accounting starts
+ uint64_t end_time; // monotonic time accounting ends
+ uint32_t counter; // private counter for accumulate calculations
+ double io_avg; // average io_in_flight for accumulate calculations
+
+ bool is_zero() {
+ return read_ios == 0 && write_ios == 0 &&
+ io_in_flight == 0 && io_ticks == 0 && io_in_queue == 0;
+ }
+
+ friend disk_stats operator- (disk_stats curr, const disk_stats& prev) {
+ curr.read_ios -= prev.read_ios;
+ curr.read_merges -= prev.read_merges;
+ curr.read_sectors -= prev.read_sectors;
+ curr.read_ticks -= prev.read_ticks;
+ curr.write_ios -= prev.write_ios;
+ curr.write_merges -= prev.write_merges;
+ curr.write_sectors -= prev.write_sectors;
+ curr.write_ticks -= prev.write_ticks;
+ /* skips io_in_flight, use current value */
+ curr.io_ticks -= prev.io_ticks;
+ curr.io_in_queue -= prev.io_in_queue;
+ return curr;
+ }
+
+ friend bool operator== (const disk_stats& a, const disk_stats& b) {
+ return a.read_ios == b.read_ios &&
+ a.read_merges == b.read_merges &&
+ a.read_sectors == b.read_sectors &&
+ a.read_ticks == b.read_ticks &&
+ a.write_ios == b.write_ios &&
+ a.write_merges == b.write_merges &&
+ a.write_sectors == b.write_sectors &&
+ a.write_ticks == b.write_ticks &&
+ /* skips io_in_flight */
+ a.io_ticks == b.io_ticks &&
+ a.io_in_queue == b.io_in_queue;
+ }
+
+ disk_stats& operator+= (const disk_stats& stats) {
+ read_ios += stats.read_ios;
+ read_merges += stats.read_merges;
+ read_sectors += stats.read_sectors;
+ read_ticks += stats.read_ticks;
+ write_ios += stats.write_ios;
+ write_merges += stats.write_merges;
+ write_sectors += stats.write_sectors;
+ write_ticks += stats.write_ticks;
+ /* skips io_in_flight, use current value */
+ io_ticks += stats.io_ticks;
+ io_in_queue += stats.io_in_queue;
+ return *this;
+ }
+};
+
+struct disk_perf {
+ uint32_t read_perf; // read speed (kbytes/s)
+ uint32_t read_ios; // read I/Os per second
+ uint32_t write_perf; // write speed (kbytes/s)
+ uint32_t write_ios; // write I/Os per second
+ uint32_t queue; // I/Os in queue
+ bool is_zero() {
+ return read_perf == 0 && read_ios == 0 &&
+ write_perf == 0 && write_ios == 0 && queue == 0;
+ }
+};
+
+class stream_stats {
+private:
+ double mSum;
+ double mSquareSum;
+ uint32_t mCnt;
+public:
+ stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+ ~stream_stats() {};
+ double get_mean() {
+ return mSum / mCnt;
+ }
+ double get_std() {
+ return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+ }
+ void add(uint32_t num) {
+ mSum += (double)num;
+ mSquareSum += (double)num * (double)num;
+ mCnt++;
+ }
+ void evict(uint32_t num) {
+ if (mSum < num || mSquareSum < (double)num * (double)num) return;
+ mSum -= (double)num;
+ mSquareSum -= (double)num * (double)num;
+ mCnt--;
+ }
+};
+
+class disk_stats_monitor {
+private:
+ FRIEND_TEST(storaged_test, disk_stats_monitor);
+ const char* const DISK_STATS_PATH;
+ struct disk_stats mPrevious;
+ struct disk_stats mAccumulate; /* reset after stall */
+ struct disk_stats mAccumulate_pub; /* reset after publish */
+ bool mStall;
+ std::queue<struct disk_perf> mBuffer;
+ struct {
+ stream_stats read_perf; // read speed (bytes/s)
+ stream_stats read_ios; // read I/Os per second
+ stream_stats write_perf; // write speed (bytes/s)
+ stream_stats write_ios; // write I/O per second
+ stream_stats queue; // I/Os in queue
+ } mStats;
+ bool mValid;
+ const uint32_t mWindow;
+ const double mSigma;
+ struct disk_perf mMean;
+ struct disk_perf mStd;
+
+ void update_mean();
+ void update_std();
+ void add(struct disk_perf* perf);
+ void evict(struct disk_perf* perf);
+ bool detect(struct disk_perf* perf);
+
+ void update(struct disk_stats* stats);
+
+public:
+ disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
+ DISK_STATS_PATH(access(MMC_DISK_STATS_PATH, R_OK) ?
+ (access(SDA_DISK_STATS_PATH, R_OK) ?
+ nullptr :
+ SDA_DISK_STATS_PATH) :
+ MMC_DISK_STATS_PATH),
+ mPrevious(), mAccumulate(), mAccumulate_pub(),
+ mStall(false), mValid(false),
+ mWindow(window_size), mSigma(sigma),
+ mMean(), mStd() {}
+ bool enabled() {
+ return DISK_STATS_PATH != nullptr;
+ }
+ void update(void);
+ void publish(void);
+};
+
+#endif /* _STORAGED_DISKSTATS_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 7d04c7a..b1efac2 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -19,10 +19,20 @@
#include <string.h>
+#include <chrono>
+
+#include <utils/Mutex.h>
+
+#include "storaged.h"
+#include "storaged.pb.h"
+
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
using namespace std;
+using namespace android;
+using namespace chrono;
+using namespace storaged_proto;
class storage_info_t {
protected:
@@ -36,16 +46,35 @@
const string userdata_path = "/data";
uint64_t userdata_total_kb;
uint64_t userdata_free_kb;
+ // io perf history
+ time_point<system_clock> day_start_tp;
+ vector<uint32_t> recent_perf;
+ uint32_t nr_samples;
+ vector<uint32_t> daily_perf;
+ uint32_t nr_days;
+ vector<uint32_t> weekly_perf;
+ uint32_t nr_weeks;
+ Mutex si_mutex;
storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),
- userdata_total_kb(0), userdata_free_kb(0) {}
+ userdata_total_kb(0), userdata_free_kb(0), nr_samples(0),
+ daily_perf(WEEK_TO_DAYS, 0), nr_days(0),
+ weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) {
+ day_start_tp = system_clock::now();
+ day_start_tp -= chrono::seconds(duration_cast<chrono::seconds>(
+ day_start_tp.time_since_epoch()).count() % DAY_TO_SEC);
+ }
void publish();
storage_info_t* s_info;
public:
static storage_info_t* get_storage_info();
- virtual ~storage_info_t() {}
+ virtual ~storage_info_t() {};
virtual void report() {};
- void refresh();
+ void load_perf_history_proto(const IOPerfHistory& perf_history);
+ void refresh(IOPerfHistory* perf_history);
+ void update_perf_history(uint32_t bw,
+ const time_point<system_clock>& tp);
+ vector<int> get_perf_history();
};
class emmc_info_t : public storage_info_t {
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index a8ddf4c..05c3b94 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -19,42 +19,37 @@
#include <vector>
-#include <binder/IInterface.h>
-#include <binder/IBinder.h>
+#include <binder/BinderService.h>
-#include "storaged.h"
+#include "android/os/BnStoraged.h"
+#include "android/os/storaged/BnStoragedPrivate.h"
-using namespace android;
+using namespace std;
+using namespace android::os;
+using namespace android::os::storaged;
-// Interface
-class IStoraged : public IInterface {
+class StoragedService : public BinderService<StoragedService>, public BnStoraged {
+private:
+ void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
+ void dumpUidRecords(int fd, const vector<struct uid_record>& entries);
public:
- enum {
- DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
- };
- // Request the service to run the test function
- virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
+ static status_t start();
+ static char const* getServiceName() { return "storaged"; }
+ virtual status_t dump(int fd, const Vector<String16> &args) override;
- DECLARE_META_INTERFACE(Storaged);
+ binder::Status onUserStarted(int32_t userId);
+ binder::Status onUserStopped(int32_t userId);
};
-// Client
-class BpStoraged : public BpInterface<IStoraged> {
+class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
public:
- BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
- virtual std::vector<struct uid_info> dump_uids(const char* option);
+ static status_t start();
+ static char const* getServiceName() { return "storaged_pri"; }
+
+ binder::Status dumpUids(vector<UidInfo>* _aidl_return);
+ binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);
};
-// Server
-class BnStoraged : public BnInterface<IStoraged> {
- virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
-};
-
-class Storaged : public BnStoraged {
- virtual std::vector<struct uid_info> dump_uids(const char* option);
- virtual status_t dump(int fd, const Vector<String16>& args);
-};
-
-sp<IStoraged> get_storaged_service();
+sp<IStoragedPrivate> get_storaged_pri_service();
#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_uid_monitor.h b/storaged/include/storaged_uid_monitor.h
index 901a872..3a718fa 100644
--- a/storaged/include/storaged_uid_monitor.h
+++ b/storaged/include/storaged_uid_monitor.h
@@ -23,88 +23,103 @@
#include <unordered_map>
#include <vector>
-enum uid_stat_t {
- FOREGROUND = 0,
- BACKGROUND = 1,
- UID_STATS = 2
+#include <cutils/multiuser.h>
+#include <utils/Mutex.h>
+
+#include "storaged.pb.h"
+#include "uid_info.h"
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+using namespace std;
+using namespace storaged_proto;
+using namespace android;
+using namespace android::os::storaged;
+
+class uid_info : public UidInfo {
+public:
+ bool parse_uid_io_stats(string&& s);
};
-enum charger_stat_t {
- CHARGER_OFF = 0,
- CHARGER_ON = 1,
- CHARGER_STATS = 2
-};
-
-enum io_type_t {
- READ = 0,
- WRITE = 1,
- IO_TYPES = 2
-};
-
-struct uid_io_stats {
- uint64_t rchar; // characters read
- uint64_t wchar; // characters written
- uint64_t read_bytes; // bytes read (from storage layer)
- uint64_t write_bytes; // bytes written (to storage layer)
- uint64_t fsync; // number of fsync syscalls
-};
-
-struct uid_info {
- uint32_t uid; // user id
- std::string name; // package name
- struct uid_io_stats io[UID_STATS]; // [0]:foreground [1]:background
+class io_usage {
+public:
+ io_usage() : bytes{{{0}}} {};
+ uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+ bool is_zero() const;
+ io_usage& operator+= (const io_usage& stats) {
+ for (int i = 0; i < IO_TYPES; i++) {
+ for (int j = 0; j < UID_STATS; j++) {
+ for (int k = 0; k < CHARGER_STATS; k++) {
+ bytes[i][j][k] += stats.bytes[i][j][k];
+ }
+ }
+ }
+ return *this;
+ }
};
struct uid_io_usage {
- uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
+ userid_t user_id;
+ io_usage uid_ios;
+ // mapped from task comm to task io usage
+ map<string, io_usage> task_ios;
};
struct uid_record {
- std::string name;
+ string name;
struct uid_io_usage ios;
};
struct uid_records {
uint64_t start_ts;
- std::vector<struct uid_record> entries;
+ vector<struct uid_record> entries;
};
class uid_monitor {
private:
+ FRIEND_TEST(storaged_test, uid_monitor);
// last dump from /proc/uid_io/stats, uid -> uid_info
- std::unordered_map<uint32_t, struct uid_info> last_uid_io_stats;
+ unordered_map<uint32_t, uid_info> last_uid_io_stats;
// current io usage for next report, app name -> uid_io_usage
- std::unordered_map<std::string, struct uid_io_usage> curr_io_stats;
+ unordered_map<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;
+ 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;
+ Mutex uidm_mutex;
// start time for IO records
uint64_t start_ts;
+ // true if UID_IO_STATS_PATH is accessible
+ const bool enable;
// reads from /proc/uid_io/stats
- std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats_locked();
+ unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();
// flushes curr_io_stats to records
void add_records_locked(uint64_t curr_ts);
// updates curr_io_stats and set last_uid_io_stats
void update_curr_io_stats_locked();
+ // writes io_history to protobuf
+ void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
public:
uid_monitor();
- ~uid_monitor();
// called by storaged main thread
void init(charger_stat_t stat);
// called by storaged -u
- std::unordered_map<uint32_t, struct uid_info> get_uid_io_stats();
+ unordered_map<uint32_t, uid_info> get_uid_io_stats();
// called by dumpsys
- std::map<uint64_t, struct uid_records> dump(
+ map<uint64_t, struct uid_records> dump(
double hours, uint64_t threshold, bool force_report);
// called by battery properties listener
void set_charger_state(charger_stat_t stat);
// called by storaged periodic_chore or dump with force_report
- void report();
+ bool enabled() { return enable; };
+ void report(unordered_map<int, StoragedProto>* protos);
+ // restores io_history from protobuf
+ void load_uid_io_proto(const UidIOUsage& proto);
+ void clear_user_history(userid_t user_id);
};
#endif /* _STORAGED_UID_MONITOR_H_ */
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
index 2161c40..62cb12d 100644
--- a/storaged/include/storaged_utils.h
+++ b/storaged/include/storaged_utils.h
@@ -24,21 +24,20 @@
#include "storaged.h"
+using namespace android::os::storaged;
+
// Diskstats
bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
struct disk_perf get_disk_perf(struct disk_stats* stats);
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, struct disk_stats* inc);
void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
-bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
// UID I/O
-void sort_running_uids_info(std::vector<struct uid_info> &uids);
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries);
+void sort_running_uids_info(std::vector<UidInfo> &uids);
// Logging
-void log_console_running_uids_info(std::vector<struct uid_info> uids);
+void log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task);
+void log_console_perf_history(const vector<int>& perf_history);
-void log_debug_disk_perf(struct disk_perf* perf, const char* type);
-
-void log_event_disk_stats(struct disk_stats* stats, const char* type);
-void log_event_emmc_info(struct emmc_info* info_);
#endif /* _STORAGED_UTILS_H_ */
diff --git a/storaged/include/uid_info.h b/storaged/include/uid_info.h
new file mode 100644
index 0000000..4398a0d
--- /dev/null
+++ b/storaged/include/uid_info.h
@@ -0,0 +1,75 @@
+/*
+ * 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 _UID_INFO_H_
+#define _UID_INFO_H_
+
+#include <string>
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace storaged {
+
+enum uid_stat_t {
+ FOREGROUND = 0,
+ BACKGROUND = 1,
+ UID_STATS = 2
+};
+
+enum charger_stat_t {
+ CHARGER_OFF = 0,
+ CHARGER_ON = 1,
+ CHARGER_STATS = 2
+};
+
+enum io_type_t {
+ READ = 0,
+ WRITE = 1,
+ IO_TYPES = 2
+};
+
+struct io_stats {
+ uint64_t rchar; // characters read
+ uint64_t wchar; // characters written
+ uint64_t read_bytes; // bytes read (from storage layer)
+ uint64_t write_bytes; // bytes written (to storage layer)
+ uint64_t fsync; // number of fsync syscalls
+};
+
+class task_info {
+public:
+ std::string comm;
+ pid_t pid;
+ io_stats io[UID_STATS];
+ bool parse_task_io_stats(std::string&& s);
+};
+
+class UidInfo : public Parcelable {
+public:
+ uint32_t uid; // user id
+ std::string name; // package name
+ io_stats io[UID_STATS]; // [0]:foreground [1]:background
+ std::unordered_map<uint32_t, task_info> tasks; // mapped from pid
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+};
+
+} // namespace storaged
+} // namespace os
+} // namespace android
+
+#endif /* _UID_INFO_H_ */
\ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
index 6b82904..c1b1329 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -42,72 +42,81 @@
#include <storaged_service.h>
#include <storaged_utils.h>
-sp<storaged_t> storaged;
+using namespace std;
+using namespace android;
+
+sp<storaged_t> storaged_sp;
// Function of storaged's main thread
void* storaged_main(void* /* unused */) {
- storaged = new storaged_t();
+ storaged_sp = new storaged_t();
- storaged->init_battery_service();
- storaged->report_storage_info();
+ storaged_sp->init_health_service();
+ storaged_sp->report_storage_info();
LOG_TO(SYSTEM, INFO) << "storaged: Start";
for (;;) {
- storaged->event_checked();
- storaged->pause();
+ storaged_sp->event_checked();
+ storaged_sp->pause();
}
return NULL;
}
-static void help_message(void) {
+void help_message(void) {
printf("usage: storaged [OPTION]\n");
printf(" -u --uid Dump uid I/O usage to stdout\n");
+ printf(" -t --task Dump task I/O usage to stdout\n");
+ printf(" -p --perf Dump I/O perf history to stdout\n");
printf(" -s --start Start storaged (default)\n");
fflush(stdout);
}
int main(int argc, char** argv) {
- int flag_main_service = 0;
- int flag_dump_uid = 0;
+ bool flag_main_service = false;
+ bool flag_dump_uid = false;
+ bool flag_dump_task = false;
+ bool flag_dump_perf = false;
int opt;
for (;;) {
int opt_idx = 0;
static struct option long_options[] = {
- {"start", no_argument, 0, 's'},
- {"kill", no_argument, 0, 'k'},
- {"uid", no_argument, 0, 'u'},
- {"help", no_argument, 0, 'h'}
+ {"perf", no_argument, nullptr, 'p'},
+ {"start", no_argument, nullptr, 's'},
+ {"task", no_argument, nullptr, 't'},
+ {"uid", no_argument, nullptr, 'u'},
+ {nullptr, 0, nullptr, 0}
};
- opt = getopt_long(argc, argv, ":skdhu0", long_options, &opt_idx);
+ opt = getopt_long(argc, argv, ":pstu", long_options, &opt_idx);
if (opt == -1) {
break;
}
switch (opt) {
+ case 'p':
+ flag_dump_perf = true;
+ break;
case 's':
- flag_main_service = 1;
+ flag_main_service = true;
+ break;
+ case 't':
+ flag_dump_task = true;
break;
case 'u':
- flag_dump_uid = 1;
+ flag_dump_uid = true;
break;
- case 'h':
+ default:
help_message();
return 0;
- case '?':
- default:
- fprintf(stderr, "no supported option\n");
- help_message();
- return -1;
}
}
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;
@@ -122,7 +131,12 @@
return -1;
}
- defaultServiceManager()->addService(String16("storaged"), new Storaged());
+ if (StoragedService::start() != android::OK ||
+ StoragedPrivateService::start() != android::OK) {
+ PLOG_TO(SYSTEM, ERROR) << "Failed to start storaged service";
+ return -1;
+ }
+
android::ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
pthread_join(storaged_main_thread, NULL);
@@ -130,23 +144,33 @@
return 0;
}
- if (flag_dump_uid) {
- sp<IStoraged> storaged_service = get_storaged_service();
- if (storaged_service == NULL) {
- fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
- return -1;
- }
- std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
+ sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();
+ if (storaged_service == NULL) {
+ fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+ return -1;
+ }
- if (res.size() == 0) {
- fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
+ if (flag_dump_uid || flag_dump_task) {
+ vector<UidInfo> uid_io;
+ binder::Status status = storaged_service->dumpUids(&uid_io);
+ if (!status.isOk() || uid_io.size() == 0) {
+ fprintf(stderr, "UID I/O info is not available.\n");
return 0;
}
- sort_running_uids_info(res);
- log_console_running_uids_info(res);
+ sort_running_uids_info(uid_io);
+ log_console_running_uids_info(uid_io, flag_dump_task);
+ }
- return 0;
+ if (flag_dump_perf) {
+ vector<int> perf_history;
+ binder::Status status = storaged_service->dumpPerfHistory(&perf_history);
+ if (!status.isOk() || perf_history.size() == 0) {
+ fprintf(stderr, "I/O perf history is not available.\n");
+ return 0;
+ }
+
+ log_console_perf_history(perf_history);
}
return 0;
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 06afea6..915c095 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -16,184 +16,117 @@
#define LOG_TAG "storaged"
+#include <dirent.h>
#include <stdlib.h>
+#include <stdio.h>
#include <time.h>
#include <unistd.h>
+#include <zlib.h>
+#include <chrono>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <batteryservice/BatteryServiceConstants.h>
-#include <batteryservice/IBatteryPropertiesRegistrar.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <cutils/properties.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hwbinder/IPCThreadState.h>
#include <log/log.h>
#include <storaged.h>
#include <storaged_utils.h>
-/* disk_stats_publisher */
-void disk_stats_publisher::publish(void) {
- // Logging
- struct disk_perf perf = get_disk_perf(&mAccumulate);
- log_debug_disk_perf(&perf, "regular");
- log_event_disk_stats(&mAccumulate, "regular");
- // Reset global structures
- memset(&mAccumulate, 0, sizeof(struct disk_stats));
-}
+using namespace android::base;
+using namespace chrono;
+using namespace google::protobuf::io;
+using namespace storaged_proto;
-void disk_stats_publisher::update(void) {
- struct disk_stats curr;
- if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
- struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
- add_disk_stats(&inc, &mAccumulate);
-#ifdef DEBUG
-// log_kernel_disk_stats(&mPrevious, "prev stats");
-// log_kernel_disk_stats(&curr, "curr stats");
-// log_kernel_disk_stats(&inc, "inc stats");
-// log_kernel_disk_stats(&mAccumulate, "accumulated stats");
-#endif
- mPrevious = curr;
- }
-}
+namespace {
-/* disk_stats_monitor */
-void disk_stats_monitor::update_mean() {
- CHECK(mValid);
- mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
- mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
- mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
- mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
- mMean.queue = (uint32_t)mStats.queue.get_mean();
-}
+/*
+ * The system user is the initial user that is implicitly created on first boot
+ * and hosts most of the system services. Keep this in sync with
+ * frameworks/base/core/java/android/os/UserManager.java
+ */
+constexpr int USER_SYSTEM = 0;
-void disk_stats_monitor::update_std() {
- CHECK(mValid);
- mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
- mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
- mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
- mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
- mStd.queue = (uint32_t)mStats.queue.get_std();
-}
+constexpr uint32_t benchmark_unit_size = 16 * 1024; // 16KB
-void disk_stats_monitor::add(struct disk_perf* perf) {
- mStats.read_perf.add(perf->read_perf);
- mStats.read_ios.add(perf->read_ios);
- mStats.write_perf.add(perf->write_perf);
- mStats.write_ios.add(perf->write_ios);
- mStats.queue.add(perf->queue);
-}
+} // namespace
-void disk_stats_monitor::evict(struct disk_perf* perf) {
- mStats.read_perf.evict(perf->read_perf);
- mStats.read_ios.evict(perf->read_ios);
- mStats.write_perf.evict(perf->write_perf);
- mStats.write_ios.evict(perf->write_ios);
- mStats.queue.evict(perf->queue);
-}
+const uint32_t storaged_t::crc_init = 0x5108A4ED; /* STORAGED */
-bool disk_stats_monitor::detect(struct disk_perf* perf) {
- return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
- ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
- ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
-}
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::interfacesEqual;
+using android::hardware::Return;
+using android::hidl::manager::V1_0::IServiceManager;
-void disk_stats_monitor::update(struct disk_stats* stats) {
- struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
- struct disk_perf perf = get_disk_perf(&inc);
- // Update internal data structures
- if (LIKELY(mValid)) {
- CHECK_EQ(mBuffer.size(), mWindow);
-
- if (UNLIKELY(detect(&perf))) {
- mStall = true;
- add_disk_stats(&inc, &mAccumulate);
- log_debug_disk_perf(&mMean, "stalled_mean");
- log_debug_disk_perf(&mStd, "stalled_std");
- } else {
- if (mStall) {
- struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
- log_debug_disk_perf(&acc_perf, "stalled");
- log_event_disk_stats(&mAccumulate, "stalled");
- mStall = false;
- memset(&mAccumulate, 0, sizeof(mAccumulate));
- }
+static sp<IHealth> get_health_service() {
+ for (auto&& instanceName : {"default", "backup"}) {
+ auto ret = IHealth::getService(instanceName);
+ if (ret != nullptr) {
+ return ret;
}
-
- evict(&mBuffer.front());
- mBuffer.pop();
- add(&perf);
- mBuffer.push(perf);
-
- update_mean();
- update_std();
-
- } else { /* mValid == false */
- CHECK_LT(mBuffer.size(), mWindow);
- add(&perf);
- mBuffer.push(perf);
- if (mBuffer.size() == mWindow) {
- mValid = true;
- update_mean();
- update_std();
- }
+ LOG_TO(SYSTEM, INFO) << "health: storaged: cannot get " << instanceName << " service";
}
-
- mPrevious = *stats;
+ return nullptr;
}
-void disk_stats_monitor::update(void) {
- struct disk_stats curr;
- if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
- update(&curr);
- }
-}
-
-static sp<IBatteryPropertiesRegistrar> get_battery_properties_service() {
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == NULL) return NULL;
-
- sp<IBinder> binder = sm->getService(String16("batteryproperties"));
- if (binder == NULL) return NULL;
-
- sp<IBatteryPropertiesRegistrar> battery_properties =
- interface_cast<IBatteryPropertiesRegistrar>(binder);
-
- return battery_properties;
-}
-
-static inline charger_stat_t is_charger_on(int64_t prop) {
- return (prop == BATTERY_STATUS_CHARGING || prop == BATTERY_STATUS_FULL) ?
+inline charger_stat_t is_charger_on(BatteryStatus prop) {
+ return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
CHARGER_ON : CHARGER_OFF;
}
-void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
+Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+ return android::hardware::Void();
}
-void storaged_t::init_battery_service() {
- if (!mConfig.proc_uid_io_available)
+void storaged_t::init_health_service() {
+ if (!mUidm.enabled())
return;
- battery_properties = get_battery_properties_service();
- if (battery_properties == NULL) {
- LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
+ health = get_health_service();
+ if (health == NULL) {
+ LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
return;
}
- struct BatteryProperty val;
- battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
- mUidm.init(is_charger_on(val.valueInt64));
+ BatteryStatus status = BatteryStatus::UNKNOWN;
+ auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
+ if (r != Result::SUCCESS) {
+ LOG_TO(SYSTEM, WARNING)
+ << "health: cannot get battery status " << toString(r);
+ return;
+ }
+ if (v == BatteryStatus::UNKNOWN) {
+ LOG_TO(SYSTEM, WARNING) << "health: invalid battery status";
+ }
+ status = v;
+ });
+ if (!ret.isOk()) {
+ LOG_TO(SYSTEM, WARNING) << "health: get charge status transaction error "
+ << ret.description();
+ }
+ mUidm.init(is_charger_on(status));
// register listener after init uid_monitor
- battery_properties->registerListener(this);
- IInterface::asBinder(battery_properties)->linkToDeath(this);
+ health->registerCallback(this);
+ health->linkToDeath(this, 0 /* cookie */);
}
-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";
- IPCThreadState::self()->stopProcess();
+void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
+ if (health != NULL && interfacesEqual(health, who.promote())) {
+ LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
+ android::hardware::IPCThreadState::self()->stopProcess();
exit(1);
} else {
LOG_TO(SYSTEM, ERROR) << "unknown service died";
@@ -206,44 +139,187 @@
/* storaged_t */
storaged_t::storaged_t(void) {
- if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
- mConfig.diskstats_available = false;
- } else {
- mConfig.diskstats_available = true;
- }
-
- mConfig.proc_uid_io_available = (access(UID_IO_STATS_PATH, R_OK) == 0);
-
mConfig.periodic_chores_interval_unit =
- property_get_int32("ro.storaged.event.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
+ property_get_int32("ro.storaged.event.interval",
+ DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);
mConfig.event_time_check_usec =
property_get_int32("ro.storaged.event.perf_check", 0);
mConfig.periodic_chores_interval_disk_stats_publish =
- property_get_int32("ro.storaged.disk_stats_pub", DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
+ property_get_int32("ro.storaged.disk_stats_pub",
+ DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);
mConfig.periodic_chores_interval_uid_io =
- property_get_int32("ro.storaged.uid_io.interval", DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+ property_get_int32("ro.storaged.uid_io.interval",
+ DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);
+
+ mConfig.periodic_chores_interval_flush_proto =
+ property_get_int32("ro.storaged.flush_proto.interval",
+ DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
storage_info.reset(storage_info_t::get_storage_info());
mStarttime = time(NULL);
+ mTimer = 0;
}
-void storaged_t::event(void) {
- if (mConfig.diskstats_available) {
- mDiskStats.update();
- mDsm.update();
- storage_info->refresh();
- if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
- mDiskStats.publish();
+void storaged_t::add_user_ce(userid_t user_id) {
+ load_proto(user_id);
+ proto_loaded[user_id] = true;
+}
+
+void storaged_t::remove_user_ce(userid_t user_id) {
+ proto_loaded[user_id] = false;
+ mUidm.clear_user_history(user_id);
+ RemoveFileIfExists(proto_path(user_id), nullptr);
+}
+
+void storaged_t::load_proto(userid_t user_id) {
+ string proto_file = proto_path(user_id);
+ ifstream in(proto_file, ofstream::in | ofstream::binary);
+
+ if (!in.good()) return;
+
+ stringstream ss;
+ ss << in.rdbuf();
+ StoragedProto proto;
+ proto.ParseFromString(ss.str());
+
+ uint32_t crc = proto.crc();
+ proto.set_crc(crc_init);
+ string proto_str = proto.SerializeAsString();
+ uint32_t computed_crc = crc32(crc_init,
+ reinterpret_cast<const Bytef*>(proto_str.c_str()),
+ proto_str.size());
+
+ if (crc != computed_crc) {
+ LOG_TO(SYSTEM, WARNING) << "CRC mismatch in " << proto_file;
+ return;
+ }
+
+ mUidm.load_uid_io_proto(proto.uid_io_usage());
+
+ if (user_id == USER_SYSTEM) {
+ storage_info->load_perf_history_proto(proto.perf_history());
+ }
+}
+
+void storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
+ proto->set_version(3);
+ proto->set_crc(crc_init);
+
+ if (user_id == USER_SYSTEM) {
+ while (proto->ByteSize() < 128 * 1024) {
+ proto->add_padding(0xFEEDBABE);
}
}
- if (mConfig.proc_uid_io_available && mTimer &&
- (mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
- mUidm.report();
+ string proto_str = proto->SerializeAsString();
+ proto->set_crc(crc32(crc_init,
+ reinterpret_cast<const Bytef*>(proto_str.c_str()),
+ proto_str.size()));
+}
+
+void storaged_t::flush_proto_user_system(StoragedProto* proto) {
+ string proto_str = proto->SerializeAsString();
+ const char* data = proto_str.data();
+ uint32_t size = proto_str.size();
+ ssize_t ret;
+ time_point<steady_clock> start, end;
+
+ string proto_file = proto_path(USER_SYSTEM);
+ string tmp_file = proto_file + "_tmp";
+ unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
+ O_DIRECT | O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC,
+ S_IRUSR | S_IWUSR)));
+ if (fd == -1) {
+ PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
+ return;
+ }
+
+ uint32_t benchmark_size = 0;
+ uint64_t benchmark_time_ns = 0;
+ while (size > 0) {
+ start = steady_clock::now();
+ ret = write(fd, data, MIN(benchmark_unit_size, size));
+ if (ret <= 0) {
+ PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
+ return;
+ }
+ end = steady_clock::now();
+ /*
+ * compute bandwidth after the first write and if write returns
+ * exactly unit size.
+ */
+ if (size != proto_str.size() && ret == benchmark_unit_size) {
+ benchmark_size += benchmark_unit_size;
+ benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
+ }
+ size -= ret;
+ data += ret;
+ }
+
+ if (benchmark_size) {
+ int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
+ storage_info->update_perf_history(perf, system_clock::now());
+ }
+
+ fd.reset(-1);
+ /* Atomically replace existing proto file to reduce chance of data loss. */
+ rename(tmp_file.c_str(), proto_file.c_str());
+}
+
+void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
+ prepare_proto(user_id, proto);
+
+ if (user_id == USER_SYSTEM) {
+ flush_proto_user_system(proto);
+ return;
+ }
+
+ string proto_file = proto_path(user_id);
+ string tmp_file = proto_file + "_tmp";
+ if (!WriteStringToFile(proto->SerializeAsString(), tmp_file,
+ S_IRUSR | S_IWUSR)) {
+ return;
+ }
+
+ /* Atomically replace existing proto file to reduce chance of data loss. */
+ rename(tmp_file.c_str(), proto_file.c_str());
+}
+
+void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
+ for (auto& it : *protos) {
+ /*
+ * Don't flush proto if we haven't attempted to load it from file.
+ */
+ if (proto_loaded[it.first]) {
+ flush_proto(it.first, &it.second);
+ }
+ }
+}
+
+void storaged_t::event(void) {
+ unordered_map<int, StoragedProto> protos;
+
+ if (mDsm.enabled()) {
+ mDsm.update();
+ if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
+ mDsm.publish();
+ }
+ }
+
+ if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {
+ mUidm.report(&protos);
+ }
+
+ if (storage_info) {
+ storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());
+ }
+
+ if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {
+ flush_protos(&protos);
}
mTimer += mConfig.periodic_chores_interval_unit;
diff --git a/storaged/storaged.proto b/storaged/storaged.proto
new file mode 100644
index 0000000..9dcd79e
--- /dev/null
+++ b/storaged/storaged.proto
@@ -0,0 +1,60 @@
+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 uint32 user_id = 2;
+ optional IOUsage uid_io = 3;
+ repeated TaskIOUsage task_io = 4;
+}
+
+message UidIORecords {
+ optional uint64 start_ts = 1;
+ repeated UidRecord entries = 2;
+}
+
+message UidIOItem {
+ optional uint64 end_ts = 1;
+ optional UidIORecords records = 2;
+}
+
+message UidIOUsage {
+ repeated UidIOItem uid_io_items = 2;
+}
+
+message IOPerfHistory {
+ optional uint64 day_start_sec = 1;
+ repeated uint32 recent_perf = 2;
+ optional uint32 nr_samples = 3;
+ repeated uint32 daily_perf = 4;
+ optional uint32 nr_days = 5;
+ repeated uint32 weekly_perf = 6;
+ optional uint32 nr_weeks = 7;
+}
+
+message StoragedProto {
+ optional uint32 crc = 1;
+ optional uint32 version = 2;
+ optional UidIOUsage uid_io_usage = 3;
+ optional IOPerfHistory perf_history = 4;
+ repeated uint32 padding = 5;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index a24c7fb..1840d05 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -4,4 +4,4 @@
file /d/mmc0/mmc0:0001/ext_csd r
writepid /dev/cpuset/system-background/tasks
user root
- group package_info
\ No newline at end of file
+ group package_info
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
new file mode 100644
index 0000000..0604e0a
--- /dev/null
+++ b/storaged/storaged_diskstats.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sstream>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <log/log_event_list.h>
+
+#include "storaged.h"
+#include "storaged_diskstats.h"
+
+namespace {
+
+#ifdef DEBUG
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
+ // skip if the input structure are all zeros
+ if (perf == NULL || perf->is_zero()) return;
+
+ LOG_TO(SYSTEM, INFO) << "disk_perf " << type
+ << " rd: " << perf->read_perf << " kbps, " << perf->read_ios << " iops"
+ << " wr: " << perf->write_perf << " kbps, " << perf->write_ios << " iops"
+ << " q: " << perf->queue;
+}
+#else
+void log_debug_disk_perf(struct disk_perf* perf, const char* type) {}
+#endif
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+ // skip if the input structure are all zeros
+ if (stats == NULL || stats->is_zero()) return;
+
+ android_log_event_list(EVENTLOGTAG_DISKSTATS)
+ << type << stats->start_time << stats->end_time
+ << stats->read_ios << stats->read_merges
+ << stats->read_sectors << stats->read_ticks
+ << stats->write_ios << stats->write_merges
+ << stats->write_sectors << stats->write_ticks
+ << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
+ << LOG_ID_EVENTS;
+}
+
+} // namespace
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats)
+{
+ // Get time
+ struct timespec ts;
+ // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+ // when system is running.
+ int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (ret < 0) {
+ PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
+ return false;
+ }
+
+ std::string buffer;
+ if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+ PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
+ return false;
+ }
+
+ // Regular diskstats entries
+ std::stringstream ss(buffer);
+ for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+ ss >> *((uint64_t*)stats + i);
+ }
+ // Other entries
+ stats->start_time = 0;
+ stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
+ ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+ stats->counter = 1;
+ stats->io_avg = (double)stats->io_in_flight;
+ return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats)
+{
+ struct disk_perf perf = {};
+
+ if (stats->io_ticks) {
+ if (stats->read_ticks) {
+ unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+ perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+ stats->read_sectors * stats->io_in_queue +
+ (divisor >> 1)) / divisor;
+ perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+ stats->read_ios * stats->io_in_queue +
+ (divisor >> 1)) / divisor;
+ }
+ if (stats->write_ticks) {
+ unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+ perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+ stats->write_sectors * stats->io_in_queue +
+ (divisor >> 1)) / divisor;
+ perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+ stats->write_ios * stats->io_in_queue +
+ (divisor >> 1)) / divisor;
+ }
+ perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+ stats->io_ticks;
+ }
+ return perf;
+}
+
+void get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,
+ struct disk_stats* inc)
+{
+ *inc = *curr - *prev;
+ inc->start_time = prev->end_time;
+ inc->end_time = curr->end_time;
+ inc->io_avg = curr->io_avg;
+ inc->counter = 1;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst)
+{
+ if (dst->end_time != 0 && dst->end_time != src->start_time) {
+ LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
+ << " are added. dst end with " << dst->end_time
+ << ", src start with " << src->start_time;
+ }
+
+ *dst += *src;
+
+ dst->io_in_flight = src->io_in_flight;
+ if (dst->counter + src->counter) {
+ dst->io_avg =
+ ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+ (dst->counter + src->counter);
+ }
+ dst->counter += src->counter;
+ dst->end_time = src->end_time;
+ if (dst->start_time == 0) {
+ dst->start_time = src->start_time;
+ }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean()
+{
+ CHECK(mValid);
+ mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+ mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+ mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+ mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+ mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std()
+{
+ CHECK(mValid);
+ mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+ mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+ mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+ mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+ mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf)
+{
+ mStats.read_perf.add(perf->read_perf);
+ mStats.read_ios.add(perf->read_ios);
+ mStats.write_perf.add(perf->write_perf);
+ mStats.write_ios.add(perf->write_ios);
+ mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+ mStats.read_perf.evict(perf->read_perf);
+ mStats.read_ios.evict(perf->read_ios);
+ mStats.write_perf.evict(perf->write_perf);
+ mStats.write_ios.evict(perf->write_ios);
+ mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf)
+{
+ return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+ ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+ ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* curr)
+{
+ disk_stats inc;
+ get_inc_disk_stats(&mPrevious, curr, &inc);
+ add_disk_stats(&inc, &mAccumulate_pub);
+
+ struct disk_perf perf = get_disk_perf(&inc);
+ log_debug_disk_perf(&perf, "regular");
+
+ add(&perf);
+ mBuffer.push(perf);
+ if (mBuffer.size() > mWindow) {
+ evict(&mBuffer.front());
+ mBuffer.pop();
+ mValid = true;
+ }
+
+ // Update internal data structures
+ if (LIKELY(mValid)) {
+ CHECK_EQ(mBuffer.size(), mWindow);
+ update_mean();
+ update_std();
+ if (UNLIKELY(detect(&perf))) {
+ mStall = true;
+ add_disk_stats(&inc, &mAccumulate);
+ log_debug_disk_perf(&mMean, "stalled_mean");
+ log_debug_disk_perf(&mStd, "stalled_std");
+ } else {
+ if (mStall) {
+ struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+ log_debug_disk_perf(&acc_perf, "stalled");
+ log_event_disk_stats(&mAccumulate, "stalled");
+ mStall = false;
+ memset(&mAccumulate, 0, sizeof(mAccumulate));
+ }
+ }
+ }
+
+ mPrevious = *curr;
+}
+
+void disk_stats_monitor::update() {
+ disk_stats curr;
+ if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
+ return;
+ }
+
+ update(&curr);
+}
+
+void disk_stats_monitor::publish(void)
+{
+ struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
+ log_debug_disk_perf(&perf, "regular");
+ log_event_disk_stats(&mAccumulate, "regular");
+ // Reset global structures
+ memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
+}
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index b5fb13e..036d7e1 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -20,6 +20,8 @@
#include <string.h>
#include <sys/statvfs.h>
+#include <numeric>
+
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/logging.h>
@@ -27,9 +29,12 @@
#include <log/log_event_list.h>
#include "storaged.h"
+#include "storaged_info.h"
using namespace std;
+using namespace chrono;
using namespace android::base;
+using namespace storaged_proto;
const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
@@ -39,12 +44,16 @@
const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health";
-static bool FileExists(const std::string& filename)
+namespace {
+
+bool FileExists(const std::string& filename)
{
struct stat buffer;
return stat(filename.c_str(), &buffer) == 0;
}
+} // namespace
+
storage_info_t* storage_info_t::get_storage_info()
{
if (FileExists(emmc_info_t::emmc_sysfs) ||
@@ -57,7 +66,39 @@
return new storage_info_t;
}
-void storage_info_t::refresh()
+void storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)
+{
+ Mutex::Autolock _l(si_mutex);
+
+ if (!perf_history.has_day_start_sec() ||
+ perf_history.daily_perf_size() > (int)daily_perf.size() ||
+ perf_history.weekly_perf_size() > (int)weekly_perf.size()) {
+ LOG_TO(SYSTEM, ERROR) << "Invalid IOPerfHistory proto";
+ return;
+ }
+
+ day_start_tp = {};
+ day_start_tp += chrono::seconds(perf_history.day_start_sec());
+
+ nr_samples = perf_history.nr_samples();
+ for (auto bw : perf_history.recent_perf()) {
+ recent_perf.push_back(bw);
+ }
+
+ nr_days = perf_history.nr_days();
+ int i = 0;
+ for (auto bw : perf_history.daily_perf()) {
+ daily_perf[i++] = bw;
+ }
+
+ nr_weeks = perf_history.nr_weeks();
+ i = 0;
+ for (auto bw : perf_history.weekly_perf()) {
+ weekly_perf[i++] = bw;
+ }
+}
+
+void storage_info_t::refresh(IOPerfHistory* perf_history)
{
struct statvfs buf;
if (statvfs(userdata_path.c_str(), &buf) != 0) {
@@ -67,6 +108,24 @@
userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;
userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;
+
+ Mutex::Autolock _l(si_mutex);
+
+ perf_history->Clear();
+ perf_history->set_day_start_sec(
+ duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());
+ for (const uint32_t& bw : recent_perf) {
+ perf_history->add_recent_perf(bw);
+ }
+ perf_history->set_nr_samples(nr_samples);
+ for (const uint32_t& bw : daily_perf) {
+ perf_history->add_daily_perf(bw);
+ }
+ perf_history->set_nr_days(nr_days);
+ for (const uint32_t& bw : weekly_perf) {
+ perf_history->add_weekly_perf(bw);
+ }
+ perf_history->set_nr_weeks(nr_weeks);
}
void storage_info_t::publish()
@@ -76,6 +135,84 @@
<< LOG_ID_EVENTS;
}
+void storage_info_t::update_perf_history(uint32_t bw,
+ const time_point<system_clock>& tp)
+{
+ Mutex::Autolock _l(si_mutex);
+
+ if (tp > day_start_tp &&
+ duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {
+ if (nr_samples >= recent_perf.size()) {
+ recent_perf.push_back(bw);
+ } else {
+ recent_perf[nr_samples] = bw;
+ }
+ nr_samples++;
+ return;
+ }
+
+ recent_perf.erase(recent_perf.begin() + nr_samples,
+ recent_perf.end());
+
+ uint32_t daily_avg_bw = accumulate(recent_perf.begin(),
+ recent_perf.begin() + nr_samples, 0) / nr_samples;
+
+ day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(
+ tp.time_since_epoch()).count() % DAY_TO_SEC);
+
+ nr_samples = 0;
+ if (recent_perf.empty())
+ recent_perf.resize(1);
+ recent_perf[nr_samples++] = bw;
+
+ if (nr_days < WEEK_TO_DAYS) {
+ daily_perf[nr_days++] = daily_avg_bw;
+ return;
+ }
+
+ uint32_t week_avg_bw = accumulate(daily_perf.begin(),
+ daily_perf.begin() + nr_days, 0) / nr_days;
+
+ nr_days = 0;
+ daily_perf[nr_days++] = daily_avg_bw;
+
+ if (nr_weeks >= YEAR_TO_WEEKS) {
+ nr_weeks = 0;
+ }
+ weekly_perf[nr_weeks++] = week_avg_bw;
+}
+
+vector<int> storage_info_t::get_perf_history()
+{
+ Mutex::Autolock _l(si_mutex);
+
+ vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());
+
+ ret[0] = recent_perf.size();
+ ret[1] = daily_perf.size();
+ ret[2] = weekly_perf.size();
+
+ int start = 3;
+ for (size_t i = 0; i < recent_perf.size(); i++) {
+ int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();
+ ret[start + i] = recent_perf[idx];
+ }
+
+ start += recent_perf.size();
+ for (size_t i = 0; i < daily_perf.size(); i++) {
+ int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();
+ ret[start + i] = daily_perf[idx];
+ }
+
+ start += daily_perf.size();
+ for (size_t i = 0; i < weekly_perf.size(); i++) {
+ int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();
+ ret[start + i] = weekly_perf[idx];
+ }
+
+ return ret;
+}
+
void emmc_info_t::report()
{
if (!report_sysfs() && !report_debugfs())
@@ -121,6 +258,8 @@
return true;
}
+namespace {
+
const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
/* 2 characters in string for each byte */
const size_t EXT_CSD_REV_IDX = 192 * 2;
@@ -128,6 +267,8 @@
const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
+} // namespace
+
bool emmc_info_t::report_debugfs()
{
string buffer;
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index b1d3bfd..3c790e6 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>
@@ -29,60 +30,69 @@
#include <private/android_filesystem_config.h>
#include <storaged.h>
+#include <storaged_utils.h>
#include <storaged_service.h>
+using namespace std;
using namespace android::base;
-extern sp<storaged_t> storaged;
+extern sp<storaged_t> storaged_sp;
-std::vector<struct uid_info> BpStoraged::dump_uids(const char* /*option*/) {
- Parcel data, reply;
- data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
-
- remote()->transact(DUMPUIDS, data, &reply);
-
- uint32_t res_size = reply.readInt32();
- std::vector<struct uid_info> res(res_size);
- for (auto&& uid : res) {
- uid.uid = reply.readInt32();
- uid.name = reply.readCString();
- reply.read(&uid.io, sizeof(uid.io));
- }
- return res;
+status_t StoragedService::start() {
+ return BinderService<StoragedService>::publish();
}
-IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
-status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- switch(code) {
- case DUMPUIDS: {
- if (!data.checkInterface(this))
- return BAD_TYPE;
- std::vector<struct uid_info> res = dump_uids(NULL);
- reply->writeInt32(res.size());
- for (auto uid : res) {
- reply->writeInt32(uid.uid);
- reply->writeCString(uid.name.c_str());
- reply->write(&uid.io, sizeof(uid.io));
- }
- return NO_ERROR;
- }
- break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
+void StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {
+ map<string, io_usage> merged_entries = merge_io_usage(entries);
+ for (const auto& rec : merged_entries) {
+ dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ rec.first.c_str(),
+ rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],
+ rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+ rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],
+ rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+ rec.second.bytes[READ][FOREGROUND][CHARGER_ON],
+ rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],
+ rec.second.bytes[READ][BACKGROUND][CHARGER_ON],
+ rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);
}
}
-std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
- std::vector<struct uid_info> uids_v;
- std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
+void StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {
+ for (const auto& record : entries) {
+ const io_usage& uid_usage = record.ios.uid_ios;
+ dprintf(fd, "%s_%d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ record.name.c_str(), record.ios.user_id,
+ 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]);
- for (const auto& it : uids_m) {
- uids_v.push_back(it.second);
+ for (const auto& task_it : record.ios.task_ios) {
+ const io_usage& task_usage = task_it.second;
+ const string& comm = task_it.first;
+ dprintf(fd, "-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ comm.c_str(),
+ task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],
+ task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],
+ task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],
+ task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],
+ task_usage.bytes[READ][FOREGROUND][CHARGER_ON],
+ task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],
+ task_usage.bytes[READ][BACKGROUND][CHARGER_ON],
+ task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ }
}
- return uids_v;
}
-status_t Storaged::dump(int fd, const Vector<String16>& args) {
+status_t StoragedService::dump(int fd, const Vector<String16>& args) {
IPCThreadState* self = IPCThreadState::self();
const int pid = self->getCallingPid();
const int uid = self->getCallingUid();
@@ -96,6 +106,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,47 +134,77 @@
force_report = true;
continue;
}
+ if (arg == String16("--debug")) {
+ debug = true;
+ continue;
+ }
}
uint64_t last_ts = 0;
- const std::map<uint64_t, struct uid_records>& records =
- storaged->get_uid_records(hours, threshold, force_report);
+ map<uint64_t, struct uid_records> records =
+ storaged_sp->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",
- 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]);
+ if (!debug) {
+ dumpUidRecords(fd, it.second.entries);
+ } else {
+ dumpUidRecordsDebug(fd, it.second.entries);
}
}
if (time_window) {
- storaged->update_uid_io_interval(time_window);
+ storaged_sp->update_uid_io_interval(time_window);
}
return NO_ERROR;
}
-sp<IStoraged> get_storaged_service() {
+binder::Status StoragedService::onUserStarted(int32_t userId) {
+ storaged_sp->add_user_ce(userId);
+ return binder::Status::ok();
+}
+
+binder::Status StoragedService::onUserStopped(int32_t userId) {
+ storaged_sp->remove_user_ce(userId);
+ return binder::Status::ok();
+}
+
+status_t StoragedPrivateService::start() {
+ return BinderService<StoragedPrivateService>::publish();
+}
+
+binder::Status StoragedPrivateService::dumpUids(
+ vector<::android::os::storaged::UidInfo>* _aidl_return) {
+ unordered_map<uint32_t, uid_info> uids_m = storaged_sp->get_uids();
+
+ for (const auto& it : uids_m) {
+ UidInfo uinfo;
+ uinfo.uid = it.second.uid;
+ uinfo.name = it.second.name;
+ uinfo.tasks = it.second.tasks;
+ memcpy(&uinfo.io, &it.second.io, sizeof(uinfo.io));
+ _aidl_return->push_back(uinfo);
+ }
+ return binder::Status::ok();
+}
+
+binder::Status StoragedPrivateService::dumpPerfHistory(
+ vector<int32_t>* _aidl_return) {
+ *_aidl_return = storaged_sp->get_perf_history();
+ return binder::Status::ok();
+}
+
+sp<IStoragedPrivate> get_storaged_pri_service() {
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) return NULL;
- sp<IBinder> binder = sm->getService(String16("storaged"));
+ sp<IBinder> binder = sm->getService(String16("storaged_pri"));
if (binder == NULL) return NULL;
- sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
-
- return storaged;
+ return interface_cast<IStoragedPrivate>(binder);
}
diff --git a/storaged/storaged_uid_monitor.cpp b/storaged/storaged_uid_monitor.cpp
index dd8bdd6..5745782 100644
--- a/storaged/storaged_uid_monitor.cpp
+++ b/storaged/storaged_uid_monitor.cpp
@@ -38,16 +38,87 @@
using namespace android;
using namespace android::base;
using namespace android::content::pm;
+using namespace android::os::storaged;
+using namespace storaged_proto;
-static bool refresh_uid_names;
+namespace {
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats()
+bool refresh_uid_names;
+const char* UID_IO_STATS_PATH = "/proc/uid_io/stats";
+
+} // namepsace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()
{
- std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+ Mutex::Autolock _l(uidm_mutex);
return get_uid_io_stats_locked();
};
-static void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
+/* return true on parse success and false on failure */
+bool uid_info::parse_uid_io_stats(std::string&& s)
+{
+ std::vector<std::string> fields = Split(s, " ");
+ if (fields.size() < 11 ||
+ !ParseUint(fields[0], &uid) ||
+ !ParseUint(fields[1], &io[FOREGROUND].rchar) ||
+ !ParseUint(fields[2], &io[FOREGROUND].wchar) ||
+ !ParseUint(fields[3], &io[FOREGROUND].read_bytes) ||
+ !ParseUint(fields[4], &io[FOREGROUND].write_bytes) ||
+ !ParseUint(fields[5], &io[BACKGROUND].rchar) ||
+ !ParseUint(fields[6], &io[BACKGROUND].wchar) ||
+ !ParseUint(fields[7], &io[BACKGROUND].read_bytes) ||
+ !ParseUint(fields[8], &io[BACKGROUND].write_bytes) ||
+ !ParseUint(fields[9], &io[FOREGROUND].fsync) ||
+ !ParseUint(fields[10], &io[BACKGROUND].fsync)) {
+ LOG_TO(SYSTEM, WARNING) << "Invalid uid I/O stats: \""
+ << s << "\"";
+ return false;
+ }
+ return true;
+}
+
+/* return true on parse success and false on failure */
+bool task_info::parse_task_io_stats(std::string&& s)
+{
+ std::vector<std::string> fields = Split(s, ",");
+ size_t size = fields.size();
+ if (size < 13 ||
+ !ParseInt(fields[size - 11], &pid) ||
+ !ParseUint(fields[size - 10], &io[FOREGROUND].rchar) ||
+ !ParseUint(fields[size - 9], &io[FOREGROUND].wchar) ||
+ !ParseUint(fields[size - 8], &io[FOREGROUND].read_bytes) ||
+ !ParseUint(fields[size - 7], &io[FOREGROUND].write_bytes) ||
+ !ParseUint(fields[size - 6], &io[BACKGROUND].rchar) ||
+ !ParseUint(fields[size - 5], &io[BACKGROUND].wchar) ||
+ !ParseUint(fields[size - 4], &io[BACKGROUND].read_bytes) ||
+ !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||
+ !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||
+ !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {
+ LOG_TO(SYSTEM, WARNING) << "Invalid task I/O stats: \""
+ << s << "\"";
+ return false;
+ }
+ comm = Join(std::vector<std::string>(
+ fields.begin() + 1, fields.end() - 11), ',');
+ return true;
+}
+
+bool io_usage::is_zero() const
+{
+ for (int i = 0; i < IO_TYPES; i++) {
+ for (int j = 0; j < UID_STATS; j++) {
+ for (int k = 0; k < CHARGER_STATS; k++) {
+ if (bytes[i][j][k])
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+namespace {
+
+void get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)
{
sp<IServiceManager> sm = defaultServiceManager();
if (sm == NULL) {
@@ -79,17 +150,19 @@
refresh_uid_names = false;
}
-std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_locked()
+} // namespace
+
+std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()
{
- std::unordered_map<uint32_t, struct uid_info> uid_io_stats;
+ std::unordered_map<uint32_t, uid_info> uid_io_stats;
std::string buffer;
if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
return uid_io_stats;
}
- std::vector<std::string> io_stats = Split(buffer, "\n");
- struct uid_info u;
+ std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
+ uid_info u;
vector<int> uids;
vector<std::string*> uid_names;
@@ -97,32 +170,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;
+ task_info t;
+ if (!t.parse_task_io_stats(std::move(io_stats[i])))
+ continue;
+ uid_io_stats[u.uid].tasks[t.pid] = t;
}
}
@@ -133,34 +198,41 @@
return uid_io_stats;
}
-static const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+namespace {
-static inline int records_size(
- const std::map<uint64_t, struct uid_records>& curr_records)
+const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
+
+inline size_t history_size(
+ const std::map<uint64_t, struct uid_records>& history)
{
- int count = 0;
- for (auto const& it : curr_records) {
+ size_t count = 0;
+ for (auto const& it : history) {
count += it.second.entries.size();
}
return count;
}
-static struct uid_io_usage zero_io_usage;
+} // namespace
void uid_monitor::add_records_locked(uint64_t curr_ts)
{
// remove records more than 5 days old
if (curr_ts > 5 * DAY_TO_SEC) {
- auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
- records.erase(records.begin(), it);
+ auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
+ io_history.erase(io_history.begin(), it);
}
struct uid_records new_records;
for (const auto& p : curr_io_stats) {
struct uid_record record = {};
record.name = p.first;
- record.ios = p.second;
- if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
+ if (!p.second.uid_ios.is_zero()) {
+ record.ios.user_id = p.second.user_id;
+ 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,25 +245,25 @@
return;
// make some room for new records
- int overflow = records_size(records) +
+ ssize_t overflow = history_size(io_history) +
new_records.entries.size() - MAX_UID_RECORDS_SIZE;
- while (overflow > 0 && records.size() > 0) {
- auto del_it = records.begin();
+ while (overflow > 0 && io_history.size() > 0) {
+ auto del_it = io_history.begin();
overflow -= del_it->second.entries.size();
- records.erase(records.begin());
+ io_history.erase(io_history.begin());
}
- records[curr_ts] = new_records;
+ io_history[curr_ts] = new_records;
}
std::map<uint64_t, struct uid_records> uid_monitor::dump(
double hours, uint64_t threshold, bool force_report)
{
if (force_report) {
- report();
+ report(nullptr);
}
- std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+ Mutex::Autolock _l(uidm_mutex);
std::map<uint64_t, struct uid_records> dump_records;
uint64_t first_ts = 0;
@@ -200,19 +272,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);
}
}
@@ -230,20 +303,21 @@
void uid_monitor::update_curr_io_stats_locked()
{
- std::unordered_map<uint32_t, struct uid_info> uid_io_stats =
+ std::unordered_map<uint32_t, uid_info> uid_io_stats =
get_uid_io_stats_locked();
if (uid_io_stats.empty()) {
return;
}
for (const auto& it : uid_io_stats) {
- const struct uid_info& uid = it.second;
-
+ const uid_info& uid = it.second;
if (curr_io_stats.find(uid.name) == curr_io_stats.end()) {
- curr_io_stats[uid.name] = {};
+ curr_io_stats[uid.name] = {};
}
struct uid_io_usage& usage = curr_io_stats[uid.name];
+ usage.user_id = multiuser_get_user_id(uid.uid);
+
int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
last_uid_io_stats[uid.uid].io[FOREGROUND].read_bytes;
int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -
@@ -253,30 +327,177 @@
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 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;
+
+ io_usage& task_usage = usage.task_ios[comm];
+ task_usage.bytes[READ][FOREGROUND][charger_stat] +=
+ (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;
+ task_usage.bytes[READ][BACKGROUND][charger_stat] +=
+ (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;
+ task_usage.bytes[WRITE][FOREGROUND][charger_stat] +=
+ (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;
+ task_usage.bytes[WRITE][BACKGROUND][charger_stat] +=
+ (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;
+ }
}
last_uid_io_stats = uid_io_stats;
}
-void uid_monitor::report()
+void uid_monitor::report(unordered_map<int, StoragedProto>* protos)
{
- std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+ if (!enabled()) return;
+
+ Mutex::Autolock _l(uidm_mutex);
update_curr_io_stats_locked();
add_records_locked(time(NULL));
+
+ if (protos) {
+ update_uid_io_proto(protos);
+ }
+}
+
+namespace {
+
+void set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage)
+{
+ usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);
+ usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);
+ usage_proto->set_rd_bg_chg_on(usage.bytes[READ][BACKGROUND][CHARGER_ON]);
+ usage_proto->set_rd_bg_chg_off(usage.bytes[READ][BACKGROUND][CHARGER_OFF]);
+ usage_proto->set_wr_fg_chg_on(usage.bytes[WRITE][FOREGROUND][CHARGER_ON]);
+ usage_proto->set_wr_fg_chg_off(usage.bytes[WRITE][FOREGROUND][CHARGER_OFF]);
+ usage_proto->set_wr_bg_chg_on(usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);
+ usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);
+}
+
+void get_io_usage_proto(io_usage* usage, const IOUsage& io_proto)
+{
+ usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();
+ usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();
+ usage->bytes[READ][BACKGROUND][CHARGER_ON] = io_proto.rd_bg_chg_on();
+ usage->bytes[READ][BACKGROUND][CHARGER_OFF] = io_proto.rd_bg_chg_off();
+ usage->bytes[WRITE][FOREGROUND][CHARGER_ON] = io_proto.wr_fg_chg_on();
+ usage->bytes[WRITE][FOREGROUND][CHARGER_OFF] = io_proto.wr_fg_chg_off();
+ usage->bytes[WRITE][BACKGROUND][CHARGER_ON] = io_proto.wr_bg_chg_on();
+ usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off();
+}
+
+} // namespace
+
+void uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)
+{
+ for (const auto& item : io_history) {
+ const uint64_t& end_ts = item.first;
+ const struct uid_records& recs = item.second;
+ unordered_map<userid_t, UidIOItem*> user_items;
+
+ for (const auto& entry : recs.entries) {
+ userid_t user_id = entry.ios.user_id;
+ UidIOItem* item_proto = user_items[user_id];
+ if (item_proto == nullptr) {
+ item_proto = (*protos)[user_id].mutable_uid_io_usage()
+ ->add_uid_io_items();
+ user_items[user_id] = item_proto;
+ }
+ item_proto->set_end_ts(end_ts);
+
+ UidIORecords* recs_proto = item_proto->mutable_records();
+ recs_proto->set_start_ts(recs.start_ts);
+
+ UidRecord* rec_proto = recs_proto->add_entries();
+ rec_proto->set_uid_name(entry.name);
+ rec_proto->set_user_id(user_id);
+
+ IOUsage* uid_io_proto = rec_proto->mutable_uid_io();
+ const 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 io_usage& task_ios = task_io.second;
+
+ TaskIOUsage* task_io_proto = rec_proto->add_task_io();
+ task_io_proto->set_task_name(task_name);
+ set_io_usage_proto(task_io_proto->mutable_ios(), task_ios);
+ }
+ }
+ }
+}
+
+void uid_monitor::clear_user_history(userid_t user_id)
+{
+ Mutex::Autolock _l(uidm_mutex);
+
+ for (auto& item : io_history) {
+ vector<uid_record>* entries = &item.second.entries;
+ entries->erase(
+ remove_if(entries->begin(), entries->end(),
+ [user_id](const uid_record& rec) {
+ return rec.ios.user_id == user_id;}),
+ entries->end());
+ }
+
+ for (auto it = io_history.begin(); it != io_history.end(); ) {
+ if (it->second.entries.empty()) {
+ it = io_history.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void uid_monitor::load_uid_io_proto(const UidIOUsage& uid_io_proto)
+{
+ if (!enabled()) return;
+
+ Mutex::Autolock _l(uidm_mutex);
+
+ for (const auto& item_proto : uid_io_proto.uid_io_items()) {
+ const UidIORecords& records_proto = item_proto.records();
+ struct uid_records* recs = &io_history[item_proto.end_ts()];
+
+ recs->start_ts = records_proto.start_ts();
+ for (const auto& rec_proto : records_proto.entries()) {
+ struct uid_record record;
+ record.name = rec_proto.uid_name();
+ record.ios.user_id = rec_proto.user_id();
+ 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)
{
- std::unique_ptr<lock_t> lock(new lock_t(&um_lock));
+ Mutex::Autolock _l(uidm_mutex);
if (charger_stat == stat) {
return;
@@ -289,16 +510,11 @@
void uid_monitor::init(charger_stat_t stat)
{
charger_stat = stat;
+
start_ts = time(NULL);
last_uid_io_stats = get_uid_io_stats();
}
uid_monitor::uid_monitor()
-{
- sem_init(&um_lock, 0, 1);
-}
-
-uid_monitor::~uid_monitor()
-{
- sem_destroy(&um_lock);
+ : enable(!access(UID_IO_STATS_PATH, R_OK)) {
}
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
index 74b7436..4fd4bc9 100644
--- a/storaged/storaged_utils.cpp
+++ b/storaged/storaged_utils.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
#include <fcntl.h>
+#include <inttypes.h>
#include <linux/time.h>
#include <stdint.h>
#include <stdio.h>
@@ -41,124 +42,7 @@
#include <storaged.h>
#include <storaged_utils.h>
-bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
- // Get time
- struct timespec ts;
- // Use monotonic to exclude suspend time so that we measure IO bytes/sec
- // when system is running.
- int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
- return false;
- }
-
- std::string buffer;
- if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
- PLOG_TO(SYSTEM, ERROR) << disk_stats_path << ": ReadFileToString failed.";
- return false;
- }
-
- // Regular diskstats entries
- std::stringstream ss(buffer);
- for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
- ss >> *((uint64_t*)stats + i);
- }
- // Other entries
- stats->start_time = 0;
- stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
- ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
- stats->counter = 1;
- stats->io_avg = (double)stats->io_in_flight;
- return true;
-}
-
-struct disk_perf get_disk_perf(struct disk_stats* stats) {
- struct disk_perf perf;
- memset(&perf, 0, sizeof(struct disk_perf)); // initialize
-
- if (stats->io_ticks) {
- if (stats->read_ticks) {
- unsigned long long divisor = stats->read_ticks * stats->io_ticks;
- perf.read_perf = ((unsigned long long)SECTOR_SIZE *
- stats->read_sectors *
- stats->io_in_queue +
- (divisor >> 1)) /
- divisor;
- perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
- stats->read_ios *
- stats->io_in_queue +
- (divisor >> 1)) /
- divisor;
- }
- if (stats->write_ticks) {
- unsigned long long divisor = stats->write_ticks * stats->io_ticks;
- perf.write_perf = ((unsigned long long)SECTOR_SIZE *
- stats->write_sectors *
- stats->io_in_queue +
- (divisor >> 1)) /
- divisor;
- perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
- stats->write_ios *
- stats->io_in_queue +
- (divisor >> 1)) /
- divisor;
- }
- perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
- stats->io_ticks;
- }
- return perf;
-}
-
-struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
- struct disk_stats inc;
- for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
- if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
- continue;
- }
-
- *((uint64_t*)&inc + i) =
- *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
- }
- // io_in_flight is exception
- inc.io_in_flight = curr->io_in_flight;
-
- inc.start_time = prev->end_time;
- inc.end_time = curr->end_time;
- inc.io_avg = curr->io_avg;
- inc.counter = 1;
-
- return inc;
-}
-
-// Add src to dst
-void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
- if (dst->end_time != 0 && dst->end_time != src->start_time) {
- LOG_TO(SYSTEM, WARNING) << "Two dis-continuous periods of diskstats"
- << " are added. dst end with " << dst->end_time
- << ", src start with " << src->start_time;
- }
-
- for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
- if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
- continue;
- }
-
- *((uint64_t*)dst + i) += *((uint64_t*)src + i);
- }
-
- dst->io_in_flight = src->io_in_flight;
- if (dst->counter + src->counter) {
- dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
- (dst->counter + src->counter);
- }
- dst->counter += src->counter;
- dst->end_time = src->end_time;
- if (dst->start_time == 0) {
- dst->start_time = src->start_time;
- }
-}
-
-static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
+bool cmp_uid_info(const UidInfo& l, const UidInfo& r) {
// Compare background I/O first.
for (int i = UID_STATS - 1; i >= 0; i--) {
uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
@@ -177,56 +61,72 @@
return l.name < r.name;
}
-void sort_running_uids_info(std::vector<struct uid_info> &uids) {
+void sort_running_uids_info(std::vector<UidInfo> &uids) {
std::sort(uids.begin(), uids.end(), cmp_uid_info);
}
// Logging functions
-void log_console_running_uids_info(std::vector<struct uid_info> uids) {
+void log_console_running_uids_info(const std::vector<UidInfo>& 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 task_info& task = task_it.second;
+ printf("-> %s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
+ " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
+ task.comm.c_str(),
+ task.io[0].rchar, task.io[0].wchar, task.io[0].read_bytes, task.io[0].write_bytes,
+ task.io[1].rchar, task.io[1].wchar, task.io[1].read_bytes, task.io[1].write_bytes,
+ task.io[0].fsync, task.io[1].fsync);
+ }
+ }
}
fflush(stdout);
}
-#if DEBUG
-void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
- // skip if the input structure are all zeros
- if (perf == NULL) return;
- struct disk_perf zero_cmp;
- memset(&zero_cmp, 0, sizeof(zero_cmp));
- if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+void log_console_perf_history(const vector<int>& perf_history) {
+ if (perf_history.size() < 3 ||
+ perf_history.size() != perf_history[0] +
+ perf_history[1] +
+ perf_history[2] + (size_t)3) {
+ return;
+ }
- LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
- << " rd:" << perf->read_perf << "KB/s(" << perf->read_ios << "/s)"
- << " wr:" << perf->write_perf << "KB/s(" << perf->write_ios << "/s)"
- << " q:" << perf->queue;
-}
-#else
-void log_debug_disk_perf(struct disk_perf* /* perf */, const char* /* type */) {}
-#endif
+ printf("\nI/O perf history (KB/s) : most_recent <--------- least_recent \n");
-void log_event_disk_stats(struct disk_stats* stats, const char* type) {
- // skip if the input structure are all zeros
- if (stats == NULL) return;
- struct disk_stats zero_cmp;
- memset(&zero_cmp, 0, sizeof(zero_cmp));
- // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
- if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+ std::stringstream line;
+ int start = 3;
+ int end = 3 + perf_history[0];
+ std::copy(perf_history.begin() + start, perf_history.begin() + end,
+ std::ostream_iterator<int>(line, " "));
+ printf("last 24 hours : %s\n", line.str().c_str());
- android_log_event_list(EVENTLOGTAG_DISKSTATS)
- << type << stats->start_time << stats->end_time
- << stats->read_ios << stats->read_merges
- << stats->read_sectors << stats->read_ticks
- << stats->write_ios << stats->write_merges
- << stats->write_sectors << stats->write_ticks
- << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
- << LOG_ID_EVENTS;
+ line.str("");
+ start = end;
+ end += perf_history[1];
+ std::copy(perf_history.begin() + start, perf_history.begin() + end,
+ std::ostream_iterator<int>(line, " "));
+ printf("last 7 days : %s\n", line.str().c_str());
+
+ line.str("");
+ start = end;
+ std::copy(perf_history.begin() + start, perf_history.end(),
+ std::ostream_iterator<int>(line, " "));
+ printf("last 52 weeks : %s\n", line.str().c_str());
}
+map<string, io_usage> merge_io_usage(const vector<uid_record>& entries) {
+ map<string, io_usage> merged_entries;
+ for (const auto& record : entries) {
+ merged_entries[record.name] += record.ios.uid_ios;
+ }
+ return merged_entries;
+}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
deleted file mode 100644
index 26d04b1..0000000
--- a/storaged/tests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-test_module_prefix := storaged-
-test_tags := tests
-
-# -----------------------------------------------------------------------------
-# Unit tests.
-# -----------------------------------------------------------------------------
-
-test_c_flags := \
- -fstack-protector-all \
- -g \
- -Wall -Wextra \
- -Werror \
- -fno-builtin \
-
-test_src_files := \
- storaged_test.cpp \
-
-# Build tests for the logger. Run with:
-# adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
-include $(CLEAR_VARS)
-LOCAL_MODULE := $(test_module_prefix)unit-tests
-LOCAL_MODULE_TAGS := $(test_tags)
-LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_STATIC_LIBRARIES := libstoraged
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libpackagelistparser
-LOCAL_SRC_FILES := $(test_src_files)
-include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index b103ac1..6a5fc61 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <chrono>
#include <deque>
#include <fcntl.h>
#include <random>
@@ -30,7 +31,13 @@
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
-static void pause(uint32_t sec) {
+using namespace std;
+using namespace chrono;
+using namespace storaged_proto;
+
+namespace {
+
+void write_and_pause(uint32_t sec) {
const char* path = "/cache/test";
int fd = open(path, O_WRONLY | O_CREAT, 0600);
ASSERT_LT(-1, fd);
@@ -53,6 +60,8 @@
sleep(sec);
}
+} // namespace
+
// the return values of the tested functions should be the expected ones
const char* DISK_STATS_PATH;
TEST(storaged_test, retvals) {
@@ -77,13 +86,11 @@
EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
// reading a wrong path should not damage the output structure
- EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+ EXPECT_EQ(stats, old_stats);
}
TEST(storaged_test, disk_stats) {
- struct disk_stats stats;
- memset(&stats, 0, sizeof(struct disk_stats));
-
+ struct disk_stats stats = {};
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
// every entry of stats (except io_in_flight) should all be greater than 0
@@ -93,11 +100,7 @@
}
// accumulation of the increments should be the same with the overall increment
- struct disk_stats base, tmp, curr, acc, inc[5];
- memset(&base, 0, sizeof(struct disk_stats));
- memset(&tmp, 0, sizeof(struct disk_stats));
- memset(&acc, 0, sizeof(struct disk_stats));
-
+ struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5];
for (uint i = 0; i < 5; ++i) {
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
if (i == 0) {
@@ -106,22 +109,18 @@
sleep(5);
continue;
}
- inc[i] = get_inc_disk_stats(&tmp, &curr);
+ get_inc_disk_stats(&tmp, &curr, &inc[i]);
add_disk_stats(&inc[i], &acc);
tmp = curr;
- pause(5);
+ write_and_pause(5);
}
- struct disk_stats overall_inc;
- memset(&overall_inc, 0, sizeof(disk_stats));
- overall_inc= get_inc_disk_stats(&base, &curr);
+ struct disk_stats overall_inc = {};
+ get_inc_disk_stats(&base, &curr, &overall_inc);
- for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
- if (i == 8) continue; // skip io_in_flight which can be 0
- EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
- }
+ EXPECT_EQ(overall_inc, acc);
}
-static double mean(std::deque<uint32_t> nums) {
+double mean(std::deque<uint32_t> nums) {
double sum = 0.0;
for (uint32_t i : nums) {
sum += i;
@@ -129,7 +128,7 @@
return sum / nums.size();
}
-static double standard_deviation(std::deque<uint32_t> nums) {
+double standard_deviation(std::deque<uint32_t> nums) {
double sum = 0.0;
double avg = mean(nums);
for (uint32_t i : nums) {
@@ -181,7 +180,7 @@
}
}
-static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
struct disk_perf retval;
retval.read_perf = (double)perf.read_perf * mul;
retval.read_ios = (double)perf.read_ios * mul;
@@ -192,7 +191,7 @@
return retval;
}
-static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
struct disk_stats retval;
retval.read_ios = stats1.read_ios + stats2.read_ios;
retval.read_merges = stats1.read_merges + stats2.read_merges;
@@ -210,6 +209,30 @@
return retval;
}
+void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+ EXPECT_LE(stats1.read_ios, stats2.read_ios);
+ EXPECT_LE(stats1.read_merges, stats2.read_merges);
+ EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+ EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+ EXPECT_LE(stats1.write_ios, stats2.write_ios);
+ EXPECT_LE(stats1.write_merges, stats2.write_merges);
+ EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+ EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+ EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+ EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+
+ EXPECT_TRUE(stats1.read_ios < stats2.read_ios ||
+ stats1.read_merges < stats2.read_merges ||
+ stats1.read_sectors < stats2.read_sectors ||
+ stats1.read_ticks < stats2.read_ticks ||
+ stats1.write_ios < stats2.write_ios ||
+ stats1.write_merges < stats2.write_merges ||
+ stats1.write_sectors < stats2.write_sectors ||
+ stats1.write_ticks < stats2.write_ticks ||
+ stats1.io_ticks < stats2.io_ticks ||
+ stats1.io_in_queue < stats2.io_in_queue);
+}
+
TEST(storaged_test, disk_stats_monitor) {
// asserting that there is one file for diskstats
ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
@@ -294,14 +317,12 @@
.io_avg = 0
};
- struct disk_stats stats_base;
- memset(&stats_base, 0, sizeof(stats_base));
-
+ struct disk_stats stats_base = {};
int loop_size = 100;
for (int i = 0; i < loop_size; ++i) {
stats_base = disk_stats_add(stats_base, norm_inc);
dsm_acc.update(&stats_base);
- EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+ EXPECT_EQ(dsm_acc.mValid, (uint32_t)i >= dsm_acc.mWindow);
EXPECT_FALSE(dsm_acc.mStall);
}
@@ -316,36 +337,259 @@
EXPECT_TRUE(dsm_acc.mValid);
EXPECT_FALSE(dsm_acc.mStall);
}
-}
-static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
- EXPECT_LE(stats1.read_ios, stats2.read_ios);
- EXPECT_LE(stats1.read_merges, stats2.read_merges);
- EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
- EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
-
- EXPECT_LE(stats1.write_ios, stats2.write_ios);
- EXPECT_LE(stats1.write_merges, stats2.write_merges);
- EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
- EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
-
- EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
- EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
-}
-
-#define TEST_LOOPS 20
-TEST(storaged_test, disk_stats_publisher) {
- // asserting that there is one file for diskstats
- ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
- disk_stats_publisher dsp;
- struct disk_stats prev;
- memset(&prev, 0, sizeof(prev));
-
- for (int i = 0; i < TEST_LOOPS; ++i) {
- dsp.update();
- expect_increasing(prev, dsp.mPrevious);
- prev = dsp.mPrevious;
- pause(10);
+ struct disk_stats stats_prev = {};
+ loop_size = 10;
+ write_and_pause(5);
+ for (int i = 0; i < loop_size; ++i) {
+ dsm_detect.update();
+ expect_increasing(stats_prev, dsm_detect.mPrevious);
+ stats_prev = dsm_detect.mPrevious;
+ write_and_pause(5);
}
}
+TEST(storaged_test, storage_info_t) {
+ storage_info_t si;
+ time_point<steady_clock> tp;
+ time_point<system_clock> stp;
+
+ // generate perf history [least_recent ------> most recent]
+ // day 1: 5, 10, 15, 20 | daily average 12
+ // day 2: 25, 30, 35, 40, 45 | daily average 35
+ // day 3: 50, 55, 60, 65, 70 | daily average 60
+ // day 4: 75, 80, 85, 90, 95 | daily average 85
+ // day 5: 100, 105, 110, 115, | daily average 107
+ // day 6: 120, 125, 130, 135, 140 | daily average 130
+ // day 7: 145, 150, 155, 160, 165 | daily average 155
+ // end of week 1: | weekly average 83
+ // day 1: 170, 175, 180, 185, 190 | daily average 180
+ // day 2: 195, 200, 205, 210, 215 | daily average 205
+ // day 3: 220, 225, 230, 235 | daily average 227
+ // day 4: 240, 245, 250, 255, 260 | daily average 250
+ // day 5: 265, 270, 275, 280, 285 | daily average 275
+ // day 6: 290, 295, 300, 305, 310 | daily average 300
+ // day 7: 315, 320, 325, 330, 335 | daily average 325
+ // end of week 2: | weekly average 251
+ // day 1: 340, 345, 350, 355 | daily average 347
+ // day 2: 360, 365, 370, 375
+ si.day_start_tp = {};
+ for (int i = 0; i < 75; i++) {
+ tp += hours(5);
+ stp = {};
+ stp += duration_cast<chrono::seconds>(tp.time_since_epoch());
+ si.update_perf_history((i + 1) * 5, stp);
+ }
+
+ vector<int> history = si.get_perf_history();
+ EXPECT_EQ(history.size(), 66UL);
+ size_t i = 0;
+ EXPECT_EQ(history[i++], 4);
+ EXPECT_EQ(history[i++], 7); // 7 days
+ EXPECT_EQ(history[i++], 52); // 52 weeks
+ // last 24 hours
+ EXPECT_EQ(history[i++], 375);
+ EXPECT_EQ(history[i++], 370);
+ EXPECT_EQ(history[i++], 365);
+ EXPECT_EQ(history[i++], 360);
+ // daily average of last 7 days
+ EXPECT_EQ(history[i++], 347);
+ EXPECT_EQ(history[i++], 325);
+ EXPECT_EQ(history[i++], 300);
+ EXPECT_EQ(history[i++], 275);
+ EXPECT_EQ(history[i++], 250);
+ EXPECT_EQ(history[i++], 227);
+ EXPECT_EQ(history[i++], 205);
+ // weekly average of last 52 weeks
+ EXPECT_EQ(history[i++], 251);
+ EXPECT_EQ(history[i++], 83);
+ for (; i < history.size(); i++) {
+ EXPECT_EQ(history[i], 0);
+ }
+}
+
+TEST(storaged_test, uid_monitor) {
+ uid_monitor uidm;
+
+ uidm.io_history[200] = {
+ .start_ts = 100,
+ .entries = {
+ { "app1", {
+ .user_id = 0,
+ .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+ }
+ },
+ { "app2", {
+ .user_id = 0,
+ .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 1000,
+ }
+ },
+ { "app1", {
+ .user_id = 1,
+ .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+ .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000,
+ }
+ },
+ },
+ };
+
+ uidm.io_history[300] = {
+ .start_ts = 200,
+ .entries = {
+ { "app1", {
+ .user_id = 1,
+ .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000,
+ }
+ },
+ { "app3", {
+ .user_id = 0,
+ .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000,
+ }
+ },
+ },
+ };
+
+ unordered_map<int, StoragedProto> protos;
+
+ uidm.update_uid_io_proto(&protos);
+
+ EXPECT_EQ(protos.size(), 2U);
+ EXPECT_EQ(protos.count(0), 1UL);
+ EXPECT_EQ(protos.count(1), 1UL);
+
+ EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2);
+ const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0);
+ EXPECT_EQ(user_0_item_0.end_ts(), 200UL);
+ EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL);
+ EXPECT_EQ(user_0_item_0.records().entries_size(), 2);
+ EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), "app1");
+ EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL);
+ EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+ EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), "app2");
+ EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL);
+ EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL);
+ const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1);
+ EXPECT_EQ(user_0_item_1.end_ts(), 300UL);
+ EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL);
+ EXPECT_EQ(user_0_item_1.records().entries_size(), 1);
+ EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), "app3");
+ EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL);
+ EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL);
+
+ EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2);
+ const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0);
+ EXPECT_EQ(user_1_item_0.end_ts(), 200UL);
+ EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL);
+ EXPECT_EQ(user_1_item_0.records().entries_size(), 1);
+ EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), "app1");
+ EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL);
+ EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL);
+ EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);
+ const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1);
+ EXPECT_EQ(user_1_item_1.end_ts(), 300UL);
+ EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL);
+ EXPECT_EQ(user_1_item_1.records().entries_size(), 1);
+ EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), "app1");
+ EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);
+ EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);
+
+ uidm.io_history.clear();
+
+ uidm.io_history[300] = {
+ .start_ts = 200,
+ .entries = {
+ { "app1", {
+ .user_id = 0,
+ .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+ }
+ },
+ },
+ };
+
+ uidm.io_history[400] = {
+ .start_ts = 300,
+ .entries = {
+ { "app1", {
+ .user_id = 0,
+ .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,
+ }
+ },
+ },
+ };
+
+ uidm.load_uid_io_proto(protos[0].uid_io_usage());
+ uidm.load_uid_io_proto(protos[1].uid_io_usage());
+
+ EXPECT_EQ(uidm.io_history.size(), 3UL);
+ EXPECT_EQ(uidm.io_history.count(200), 1UL);
+ EXPECT_EQ(uidm.io_history.count(300), 1UL);
+ EXPECT_EQ(uidm.io_history.count(400), 1UL);
+
+ EXPECT_EQ(uidm.io_history[200].start_ts, 100UL);
+ const vector<struct uid_record>& entries_0 = uidm.io_history[200].entries;
+ EXPECT_EQ(entries_0.size(), 3UL);
+ EXPECT_EQ(entries_0[0].name, "app1");
+ EXPECT_EQ(entries_0[0].ios.user_id, 0UL);
+ EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+ EXPECT_EQ(entries_0[1].name, "app2");
+ EXPECT_EQ(entries_0[1].ios.user_id, 0UL);
+ EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+ EXPECT_EQ(entries_0[2].name, "app1");
+ EXPECT_EQ(entries_0[2].ios.user_id, 1UL);
+ EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+ EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+
+ EXPECT_EQ(uidm.io_history[300].start_ts, 200UL);
+ const vector<struct uid_record>& entries_1 = uidm.io_history[300].entries;
+ EXPECT_EQ(entries_1.size(), 3UL);
+ EXPECT_EQ(entries_1[0].name, "app1");
+ EXPECT_EQ(entries_1[0].ios.user_id, 0UL);
+ EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+ EXPECT_EQ(entries_1[1].name, "app3");
+ EXPECT_EQ(entries_1[1].ios.user_id, 0UL);
+ EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+ EXPECT_EQ(entries_1[2].name, "app1");
+ EXPECT_EQ(entries_1[2].ios.user_id, 1UL);
+ EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+
+ EXPECT_EQ(uidm.io_history[400].start_ts, 300UL);
+ const vector<struct uid_record>& entries_2 = uidm.io_history[400].entries;
+ EXPECT_EQ(entries_2.size(), 1UL);
+ EXPECT_EQ(entries_2[0].name, "app1");
+ EXPECT_EQ(entries_2[0].ios.user_id, 0UL);
+ EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+ map<string, io_usage> merged_entries_0 = merge_io_usage(entries_0);
+ EXPECT_EQ(merged_entries_0.size(), 2UL);
+ EXPECT_EQ(merged_entries_0.count("app1"), 1UL);
+ EXPECT_EQ(merged_entries_0.count("app2"), 1UL);
+ EXPECT_EQ(merged_entries_0["app1"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);
+ EXPECT_EQ(merged_entries_0["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL);
+ EXPECT_EQ(merged_entries_0["app2"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);
+
+ map<string, io_usage> merged_entries_1 = merge_io_usage(entries_1);
+ EXPECT_EQ(merged_entries_1.size(), 2UL);
+ EXPECT_EQ(merged_entries_1.count("app1"), 1UL);
+ EXPECT_EQ(merged_entries_1.count("app3"), 1UL);
+ EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);
+ EXPECT_EQ(merged_entries_1["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+ EXPECT_EQ(merged_entries_1["app3"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);
+
+ map<string, io_usage> merged_entries_2 = merge_io_usage(entries_2);
+ EXPECT_EQ(merged_entries_2.size(), 1UL);
+ EXPECT_EQ(merged_entries_2.count("app1"), 1UL);
+ EXPECT_EQ(merged_entries_2["app1"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);
+
+ uidm.clear_user_history(0);
+
+ EXPECT_EQ(uidm.io_history.size(), 2UL);
+ EXPECT_EQ(uidm.io_history.count(200), 1UL);
+ EXPECT_EQ(uidm.io_history.count(300), 1UL);
+
+ EXPECT_EQ(uidm.io_history[200].entries.size(), 1UL);
+ EXPECT_EQ(uidm.io_history[300].entries.size(), 1UL);
+
+ uidm.clear_user_history(1);
+
+ EXPECT_EQ(uidm.io_history.size(), 0UL);
+}
diff --git a/storaged/tools/ranker.py b/storaged/tools/ranker.py
new file mode 100644
index 0000000..d8096b7
--- /dev/null
+++ b/storaged/tools/ranker.py
@@ -0,0 +1,181 @@
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Parser and ranker for dumpsys storaged output.
+
+This module parses output from dumpsys storaged by ranking uids based on
+their io usage measured in 8 different stats. It must be provided the input
+file through command line argument -i/--input.
+
+For more details, see:
+ $ python ranker.py -h
+
+Example:
+ $ python ranker.py -i io.txt -o output.txt -u 20 -cnt
+"""
+
+import argparse
+import sys
+
+IO_NAMES = ["[READ][FOREGROUND][CHARGER_OFF]",
+ "[WRITE][FOREGROUND][CHARGER_OFF]",
+ "[READ][BACKGROUND][CHARGER_OFF]",
+ "[WRITE][BACKGROUND][CHARGER_OFF]",
+ "[READ][FOREGROUND][CHARGER_ON]",
+ "[WRITE][FOREGROUND][CHARGER_ON]",
+ "[READ][BACKGROUND][CHARGER_ON]",
+ "[WRITE][BACKGROUND][CHARGER_ON]"]
+
+
+def get_args():
+ """Get arguments from command line.
+
+ The only required argument is input file.
+
+ Returns:
+ Args containing cmdline arguments
+ """
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--input", dest="input", required="true",
+ help="input io FILE, must provide", metavar="FILE")
+ parser.add_argument("-o", "--output", dest="output", default="stdout",
+ help="output FILE, default to stdout", metavar="FILE")
+ parser.add_argument("-u", "--uidcnt", dest="uidcnt", type=int, default=10,
+ help="set number of uids to display for each rank, "
+ "default 10")
+ parser.add_argument("-c", "--combine", dest="combine", default=False,
+ action="store_true", help="add io stats for same uids, "
+ "default to take io stats of last appearing uids")
+ parser.add_argument("-n", "--native", dest="native", default=False,
+ action="store_true", help="only include native apps in "
+ "ranking, default to include all apps")
+ parser.add_argument("-t", "--task", dest="task", default=False,
+ action="store_true", help="display task io under uids, "
+ "default to not display tasks")
+ return parser.parse_args()
+
+
+def is_number(word):
+ try:
+ int(word)
+ return True
+ except ValueError:
+ return False
+
+
+def combine_or_filter(args):
+ """Parser for io input.
+
+ Either args.combine io stats for the same uids
+ or take the io stats for the last uid and ignore
+ the same uids before it.
+
+ If task is required, store task ios along with uid
+ for later display.
+
+ Returns:
+ The structure for the return value uids is as follows:
+ uids: {uid -> [UID_STATS, TASK_STATS(optional)]}
+ UID_STATS: [io1, io2, ..., io8]
+ TASK_STATS: {task_name -> [io1, io2, ..., io8]}
+ """
+ fin = open(args.input, "r")
+ uids = {}
+ cur_uid = 0
+ task_enabled = args.task
+ for line in fin:
+ words = line.split()
+ if words[0] == "->":
+ # task io
+ if not task_enabled:
+ continue
+ # get task command line
+ i = len(words) - 8
+ task = " ".join(words[1:i])
+ if task in uids[cur_uid][1]:
+ task_io = uids[cur_uid][1][task]
+ for j in range(8):
+ task_io[j] += long(words[i+j])
+ else:
+ task_io = []
+ for j in range(8):
+ task_io.append(long(words[i+j]))
+ uids[cur_uid][1][task] = task_io
+
+ elif len(words) > 8:
+ if not is_number(words[0]) and args.native:
+ # uid not requested, ignore its tasks as well
+ task_enabled = False
+ continue
+ task_enabled = args.task
+ i = len(words) - 8
+ uid = " ".join(words[:i])
+ if uid in uids and args.combine:
+ uid_io = uids[uid][0]
+ for j in range(8):
+ uid_io[j] += long(words[i+j])
+ uids[uid][0] = uid_io
+ else:
+ uid_io = [long(words[i+j]) for j in range(8)]
+ uids[uid] = [uid_io]
+ if task_enabled:
+ uids[uid].append({})
+ cur_uid = uid
+
+ return uids
+
+
+def rank_uids(uids):
+ """Sort uids based on eight different io stats.
+
+ Returns:
+ uid_rank is a 2d list of tuples:
+ The first dimension represent the 8 different io stats.
+ The second dimension is a sorted list of tuples by tup[0],
+ each tuple is a uid's perticular stat at the first dimension and the uid.
+ """
+ uid_rank = [[(uids[uid][0][i], uid) for uid in uids] for i in range(8)]
+ for i in range(8):
+ uid_rank[i].sort(key=lambda tup: tup[0], reverse=True)
+ return uid_rank
+
+
+def display_uids(uid_rank, uids, args):
+ """Display ranked uid io, along with task io if specified."""
+ fout = sys.stdout
+ if args.output != "stdout":
+ fout = open(args.output, "w")
+
+ for i in range(8):
+ fout.write("RANKING BY " + IO_NAMES[i] + "\n")
+ for j in range(min(args.uidcnt, len(uid_rank[0]))):
+ uid = uid_rank[i][j][1]
+ uid_stat = " ".join([str(uid_io) for uid_io in uids[uid][0]])
+ fout.write(uid + " " + uid_stat + "\n")
+ if args.task:
+ for task in uids[uid][1]:
+ task_stat = " ".join([str(task_io) for task_io in uids[uid][1][task]])
+ fout.write("-> " + task + " " + task_stat + "\n")
+ fout.write("\n")
+
+
+def main():
+ args = get_args()
+ uids = combine_or_filter(args)
+ uid_rank = rank_uids(uids)
+ display_uids(uid_rank, uids, args)
+
+if __name__ == "__main__":
+ main()
diff --git a/storaged/uid_info.cpp b/storaged/uid_info.cpp
new file mode 100644
index 0000000..58e3fd2
--- /dev/null
+++ b/storaged/uid_info.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <binder/Parcel.h>
+
+#include "uid_info.h"
+
+using namespace android;
+using namespace android::os::storaged;
+
+status_t UidInfo::writeToParcel(Parcel* parcel) const {
+ parcel->writeInt32(uid);
+ parcel->writeCString(name.c_str());
+ parcel->write(&io, sizeof(io));
+
+ parcel->writeInt32(tasks.size());
+ for (const auto& task_it : tasks) {
+ parcel->writeInt32(task_it.first);
+ parcel->writeCString(task_it.second.comm.c_str());
+ parcel->write(&task_it.second.io, sizeof(task_it.second.io));
+ }
+ return NO_ERROR;
+}
+
+status_t UidInfo::readFromParcel(const Parcel* parcel) {
+ uid = parcel->readInt32();
+ name = parcel->readCString();
+ parcel->read(&io, sizeof(io));
+
+ uint32_t tasks_size = parcel->readInt32();
+ for (uint32_t i = 0; i < tasks_size; i++) {
+ task_info task;
+ task.pid = parcel->readInt32();
+ task.comm = parcel->readCString();
+ parcel->read(&task.io, sizeof(task.io));
+ tasks[task.pid] = task;
+ }
+ return NO_ERROR;
+}