Merge "Merge Android Pie into master"

This commit is contained in:
Xin Li 2018-08-07 16:51:24 +00:00 committed by Gerrit Code Review
commit 5d707816ac
80 changed files with 4002 additions and 1943 deletions

View File

@ -132,8 +132,11 @@ int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data)
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 @@ ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev,
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 @@ ssize_t adf_overlay_engines_for_interface(struct adf_device *dev,
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 @@ int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data)
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 @@ bool adf_overlay_engine_supports_format(int fd, __u32 format)
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 @@ static bool adf_find_simple_post_overlay_engine(struct adf_device *dev,
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 @@ int adf_find_simple_post_configuration(struct adf_device *dev,
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) {

View File

@ -63,6 +63,9 @@ cc_binary {
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
shared_libs: [
"libstatslog"
],
init_rc: ["bootstat.rc"],
product_variables: {
pdk: {

View File

@ -45,6 +45,7 @@
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
#include <metricslogger/metrics_logger.h>
#include <statslog.h>
#include "boot_event_record_store.h"
@ -1026,6 +1027,16 @@ const BootloaderTimingMap GetBootLoaderTimings() {
return timings;
}
// Returns the total bootloader boot time from the ro.boot.boottime system property.
int32_t GetBootloaderTime(const BootloaderTimingMap& bootloader_timings) {
int32_t total_time = 0;
for (const auto& timing : bootloader_timings) {
total_time += timing.second;
}
return total_time;
}
// Parses and records the set of bootloader stages and associated boot times
// from the ro.boot.boottime system property.
void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
@ -1039,11 +1050,10 @@ void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
boot_event_store->AddBootEventWithValue("boottime.bootloader.total", total_time);
}
// Records the closest estimation to the absolute device boot time, i.e.,
// Returns the closest estimation to the absolute device boot time, i.e.,
// from power on to boot_complete, including bootloader times.
void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
const BootloaderTimingMap& bootloader_timings,
std::chrono::milliseconds uptime) {
std::chrono::milliseconds GetAbsoluteBootTime(const BootloaderTimingMap& bootloader_timings,
std::chrono::milliseconds uptime) {
int32_t bootloader_time_ms = 0;
for (const auto& timing : bootloader_timings) {
@ -1053,23 +1063,36 @@ void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
}
auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
auto absolute_total =
std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
return bootloader_duration + uptime;
}
// Gets the boot time offset. This is useful when Android is running in a
// container, because the boot_clock is not reset when Android reboots.
std::chrono::nanoseconds GetBootTimeOffset() {
static const int64_t boottime_offset =
android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
return std::chrono::nanoseconds(boottime_offset);
// Records the closest estimation to the absolute device boot time in seconds.
// i.e. from power on to boot_complete, including bootloader times.
void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
std::chrono::milliseconds absolute_total) {
auto absolute_total_sec = std::chrono::duration_cast<std::chrono::seconds>(absolute_total);
boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total_sec.count());
}
// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
// clock.
android::base::boot_clock::duration GetUptime() {
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
// Logs the total boot time and reason to statsd.
void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
double time_since_last_boot_sec) {
const std::string reason(GetProperty(bootloader_reboot_reason_property));
if (reason.empty()) {
android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, "<EMPTY>", "<EMPTY>",
end_time.count(), total_duration.count(),
(int64_t)bootloader_duration_ms,
(int64_t)time_since_last_boot_sec * 1000);
return;
}
const std::string system_reason(GetProperty(system_reboot_reason_property));
android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),
system_reason.c_str(), end_time.count(), total_duration.count(),
(int64_t)bootloader_duration_ms,
(int64_t)time_since_last_boot_sec * 1000);
}
void SetSystemBootReason() {
@ -1088,6 +1111,20 @@ void SetSystemBootReason() {
SetProperty(last_reboot_reason_property, "");
}
// Gets the boot time offset. This is useful when Android is running in a
// container, because the boot_clock is not reset when Android reboots.
std::chrono::nanoseconds GetBootTimeOffset() {
static const int64_t boottime_offset =
android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
return std::chrono::nanoseconds(boottime_offset);
}
// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
// clock.
android::base::boot_clock::duration GetUptime() {
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
}
// Records several metrics related to the time it takes to boot the device,
// including disambiguating boot time on encrypted or non-encrypted devices.
void RecordBootComplete() {
@ -1097,10 +1134,11 @@ void RecordBootComplete() {
auto uptime_ns = GetUptime();
auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);
time_t current_time_utc = time(nullptr);
time_t time_since_last_boot = 0;
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
time_t last_boot_time_utc = record.second;
time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
}
@ -1140,10 +1178,18 @@ void RecordBootComplete() {
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
int32_t bootloader_boot_duration = GetBootloaderTime(bootloader_timings);
RecordBootloaderTimings(&boot_event_store, bootloader_timings);
auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(uptime_ns);
RecordAbsoluteBootTime(&boot_event_store, bootloader_timings, uptime_ms);
auto absolute_boot_time = GetAbsoluteBootTime(bootloader_timings, uptime_ms);
RecordAbsoluteBootTime(&boot_event_store, absolute_boot_time);
auto boot_end_time_point = std::chrono::system_clock::now().time_since_epoch();
auto boot_end_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_end_time_point);
LogBootInfoToStatsd(boot_end_time, absolute_boot_time, bootloader_boot_duration,
time_since_last_boot);
}
// Records the boot_reason metric by querying the ro.boot.bootreason system

View File

@ -177,6 +177,8 @@ static int generate_f2fs_image(const char* fileName, long long partSize, const s
mkf2fs_args.push_back("encrypt");
mkf2fs_args.push_back("-O");
mkf2fs_args.push_back("quota");
mkf2fs_args.push_back("-O");
mkf2fs_args.push_back("verity");
mkf2fs_args.push_back(fileName);
mkf2fs_args.push_back(nullptr);

View File

@ -121,6 +121,7 @@ static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
"-f",
"-O", "encrypt",
"-O", "quota",
"-O", "verity",
"-w", "4096",
fs_blkdev,
size_str.c_str(),

View File

@ -45,7 +45,7 @@
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
#define PERROR PLOG(ERROR) << FS_MGR_TAG
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
#define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
/* fstab has the following format:
*

View File

@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
libbase \
libutils \
libcrypto \
libkeystore_aidl \
libkeystore_binder \
libhidlbase \
libhidltransport \

View File

@ -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>
@ -317,11 +318,15 @@ public:
// 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");

View File

@ -1,6 +1,7 @@
cc_library_headers {
name: "libhealthd_headers",
vendor_available: true,
recovery_available: true,
export_include_dirs: ["include"],
header_libs: ["libbatteryservice_headers"],
export_header_lib_headers: ["libbatteryservice_headers"],
@ -9,7 +10,9 @@ cc_library_headers {
cc_library_static {
name: "libbatterymonitor",
srcs: ["BatteryMonitor.cpp"],
cflags: ["-Wall", "-Werror"],
vendor_available: true,
recovery_available: true,
export_include_dirs: ["include"],
shared_libs: [
"libutils",
@ -18,3 +21,66 @@ cc_library_static {
header_libs: ["libhealthd_headers"],
export_header_lib_headers: ["libhealthd_headers"],
}
cc_defaults {
name: "android.hardware.health@2.0-service_defaults",
cflags: [
"-Wall",
"-Werror",
],
static_libs: [
"android.hardware.health@2.0-impl",
"android.hardware.health@1.0-convert",
"libhealthservice",
"libhealthstoragedefault",
"libbatterymonitor",
],
shared_libs: [
"libbase",
"libcutils",
"libhidlbase",
"libhidltransport",
"libhwbinder",
"liblog",
"libutils",
"android.hardware.health@2.0",
],
}
cc_binary {
name: "android.hardware.health@2.0-service",
defaults: ["android.hardware.health@2.0-service_defaults"],
vendor: true,
relative_install_path: "hw",
init_rc: ["android.hardware.health@2.0-service.rc"],
srcs: [
"HealthServiceDefault.cpp",
],
overrides: [
"healthd",
]
}
cc_binary {
name: "healthd",
defaults: ["android.hardware.health@2.0-service_defaults"],
init_rc: ["healthd.rc"],
srcs: [
"HealthServiceHealthd.cpp",
],
local_include_dirs: ["include"],
shared_libs: [
"android.hardware.health@1.0",
],
vintf_fragments: [
"manifest_healthd.xml"
],
}

View File

@ -2,27 +2,6 @@
LOCAL_PATH := $(call my-dir)
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
@ -45,6 +24,8 @@ else
LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
endif
LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
@ -68,6 +49,11 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES := \
android.hardware.health@2.0 \
android.hardware.health@2.0-impl \
android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
libhealthstoragedefault \
libminui \
libpng \
libz \
@ -92,7 +78,6 @@ LOCAL_CHARGER_NO_UI := true
endif
LOCAL_SRC_FILES := \
healthd_common.cpp \
charger.cpp \
LOCAL_MODULE := charger
@ -106,14 +91,17 @@ LOCAL_CFLAGS := -Werror
ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
LOCAL_CFLAGS += -DCHARGER_NO_UI
endif
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 := \
CHARGER_STATIC_LIBRARIES := \
android.hardware.health@2.0-impl \
android.hardware.health@2.0 \
android.hardware.health@1.0 \
android.hardware.health@1.0-convert \
libhidltransport \
libhidlbase \
libhwbinder_noltopgo \
libhealthstoragedefault \
libvndksupport \
libhealthd_charger \
libhealthd_draw \
libbatterymonitor \
@ -124,6 +112,8 @@ LOCAL_STATIC_LIBRARIES := \
libm \
libc \
LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
LOCAL_STATIC_LIBRARIES += \
libminui \
@ -144,6 +134,21 @@ LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := charger_test
LOCAL_MODULE_TAGS := optional
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_CFLAGS := -Wall -Werror -DCHARGER_TEST -DCHARGER_NO_UI
LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
LOCAL_SRC_FILES := \
charger.cpp \
charger_test.cpp \
include $(BUILD_EXECUTABLE)
CHARGER_STATIC_LIBRARIES :=
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
define _add-charger-image
include $$(CLEAR_VARS)
@ -171,41 +176,3 @@ include $(BUILD_PHONY_PACKAGE)
_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)

View File

@ -88,6 +88,10 @@ BatteryMonitor::BatteryMonitor()
initBatteryProperties(&props);
}
struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
return batteryMonitor->props;
}
int BatteryMonitor::getBatteryStatus(const char* status) {
int ret;
struct sysfsStringEnumMap batteryStatusMap[] = {
@ -531,12 +535,6 @@ void BatteryMonitor::init(struct healthd_config *hc) {
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_vol",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryVoltagePath = path;
}
}
@ -586,12 +584,6 @@ void BatteryMonitor::init(struct healthd_config *hc) {
name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_temp",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTemperaturePath = path;
}
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,39 @@
/*
* 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 <health2/service.h>
#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;
}
int main() {
return health_service_main();
}

View File

@ -0,0 +1,93 @@
/*
* 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 <health2/service.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;
}
int main() {
return health_service_main("backup");
}

View File

@ -0,0 +1,5 @@
service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
class hal
user system
group system
file /dev/kmsg w

View File

@ -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 @@ static struct healthd_mode_ops charger_ops = {
};
#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) {
@ -76,7 +79,7 @@ static void healthd_mode_nop_battery_update(
struct android::BatteryProperties* /*props*/) {
}
int main(int argc, char **argv) {
int healthd_charger_main(int argc, char** argv) {
int ch;
healthd_mode_ops = &charger_ops;
@ -100,3 +103,9 @@ int main(int argc, char **argv) {
return healthd_main();
}
#ifndef CHARGER_TEST
int main(int argc, char** argv) {
return healthd_charger_main(argc, argv);
}
#endif

181
healthd/charger_test.cpp Normal file
View File

@ -0,0 +1,181 @@
/*
* 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 "charger_test"
#include <android/log.h>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <mutex>
#include <streambuf>
#include <string>
#include <thread>
#include <vector>
#include <health2/Health.h>
#define LOG_THIS(fmt, ...) \
ALOGE(fmt, ##__VA_ARGS__); \
printf(fmt "\n", ##__VA_ARGS__);
template <typename T>
class Atomic {
public:
Atomic(T&& init) : mValue(std::move(init)) {}
void set(T&& newVal) {
{
std::lock_guard<std::mutex> lock(mMutex);
mValue = std::move(newVal);
}
mChanged.notify_all();
}
bool waitFor(long ms, const T& expectVal) {
std::unique_lock<std::mutex> lock(mMutex);
return mChanged.wait_for(lock, std::chrono::milliseconds(ms),
[this, &expectVal] { return mValue == expectVal; });
}
private:
std::mutex mMutex;
std::condition_variable mChanged;
T mValue;
};
Atomic<bool>& getUpdateNotifier() {
static Atomic<bool> val(false);
return val;
}
int energyCounter(int64_t* counter) {
*counter = 0xEC12345;
return 0;
}
const char* createFile(const char* path, const char* content) {
std::ofstream stream(path);
if (!stream.is_open()) {
LOG_THIS("Cannot create file %s", path);
return NULL;
}
stream << content << std::endl;
stream.close();
return path;
}
std::string openToString(const char* path) {
std::ifstream stream(path);
if (!stream.is_open()) {
LOG_THIS("Cannot open file %s", path);
return "";
}
return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
}
int expectContains(const std::string& content, const std::vector<std::string>& fields) {
int status = 0;
for (const auto& field : fields) {
auto pos = content.find(field);
if (pos == std::string::npos) {
LOG_THIS("Cannot find substr '%s'", field.c_str());
status = 1;
}
}
return status;
}
::android::hardware::hidl_handle createHidlHandle(const char* filepath) {
int fd = creat(filepath, S_IRUSR | S_IWUSR);
if (fd < 0) return {};
native_handle_t* nativeHandle = native_handle_create(1, 0);
nativeHandle->data[0] = fd;
::android::hardware::hidl_handle handle;
handle.setTo(nativeHandle, true /* shouldOwn */);
return handle;
}
void healthd_board_init(struct healthd_config* config) {
config->periodic_chores_interval_fast = 60;
config->periodic_chores_interval_slow = 600;
config->batteryStatusPath = createFile("/data/local/tmp/batteryStatus", "Not charging");
config->batteryHealthPath = createFile("/data/local/tmp/batteryHealth", "Unspecified failure");
config->batteryPresentPath = createFile("/data/local/tmp/batteryPresent", "1");
config->batteryCapacityPath = createFile("/data/local/tmp/batteryCapacity", "47");
config->batteryVoltagePath = createFile("/data/local/tmp/batteryVoltage", "45000");
config->batteryTemperaturePath = createFile("/data/local/tmp/batteryTemperature", "987");
config->batteryTechnologyPath = createFile("/data/local/tmp/batteryTechnology", "NiCd");
config->batteryCurrentNowPath = createFile("/data/local/tmp/batteryCurrentNow", "99000");
config->batteryCurrentAvgPath = createFile("/data/local/tmp/batteryCurrentAvg", "98000");
config->batteryChargeCounterPath = createFile("/data/local/tmp/batteryChargeCounter", "600");
config->batteryFullChargePath = createFile("/data/local/tmp/batteryFullCharge", "3515547");
config->batteryCycleCountPath = createFile("/data/local/tmp/batteryCycleCount", "77");
config->energyCounter = energyCounter;
config->boot_min_cap = 50;
config->screen_on = NULL;
}
int healthd_board_battery_update(struct android::BatteryProperties*) {
getUpdateNotifier().set(true /* updated */);
// return 0 to log periodic polled battery status to kernel log
return 0;
}
extern int healthd_charger_main(int argc, char** argv);
int main(int argc, char** argv) {
using android::hardware::health::V2_0::implementation::Health;
const char* dumpFile = "/data/local/tmp/dump.txt";
std::thread bgThread([=] {
healthd_charger_main(argc, argv);
});
// wait for healthd_init to finish
if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {
LOG_THIS("Time out.");
exit(1);
}
Health::getImplementation()->debug(createHidlHandle(dumpFile), {} /* options */);
std::string content = openToString(dumpFile);
int status = expectContains(content, {
"status: 4",
"health: 6",
"present: 1",
"level: 47",
"voltage: 45",
"temp: 987",
"current now: 99000",
"current avg: 98000",
"charge counter: 600",
"current now: 99",
"cycle count: 77",
"Full charge: 3515547"
});
if (status == 0) {
LOG_THIS("Test success.");
} else {
LOG_THIS("Actual dump:\n%s", content.c_str());
}
exit(status); // force bgThread to exit
}

View File

@ -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();
}

4
healthd/healthd.rc Normal file
View File

@ -0,0 +1,4 @@
service healthd /system/bin/healthd
class hal
critical
group root system wakelock

View File

@ -1,304 +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-common"
#define KLOG_LEVEL 6
#include <healthd/healthd.h>
#include <healthd/BatteryMonitor.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/uevent.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <utils/Errors.h>
using namespace android;
#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
// Periodic chores fast interval in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
#else
#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
#endif
#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
// Periodic chores fast interval in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
#else
#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
#endif
static struct healthd_config healthd_config = {
.periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
.periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
.batteryStatusPath = String8(String8::kEmptyString),
.batteryHealthPath = String8(String8::kEmptyString),
.batteryPresentPath = String8(String8::kEmptyString),
.batteryCapacityPath = String8(String8::kEmptyString),
.batteryVoltagePath = String8(String8::kEmptyString),
.batteryTemperaturePath = String8(String8::kEmptyString),
.batteryTechnologyPath = String8(String8::kEmptyString),
.batteryCurrentNowPath = String8(String8::kEmptyString),
.batteryCurrentAvgPath = String8(String8::kEmptyString),
.batteryChargeCounterPath = String8(String8::kEmptyString),
.batteryFullChargePath = String8(String8::kEmptyString),
.batteryCycleCountPath = String8(String8::kEmptyString),
.energyCounter = NULL,
.boot_min_cap = 0,
.screen_on = NULL,
};
static int eventct;
static int epollfd;
#define POWER_SUPPLY_SUBSYSTEM "power_supply"
// epoll_create() parameter is actually unused
#define MAX_EPOLL_EVENTS 40
static int uevent_fd;
static int wakealarm_fd;
// -1 for no epoll timeout
static int awake_poll_interval = -1;
static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
static BatteryMonitor* gBatteryMonitor;
struct healthd_mode_ops *healthd_mode_ops;
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
struct epoll_event ev;
ev.events = EPOLLIN;
if (wakeup == EVENT_WAKEUP_FD)
ev.events |= EPOLLWAKEUP;
ev.data.ptr = (void *)handler;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
KLOG_ERROR(LOG_TAG,
"epoll_ctl failed; errno=%d\n", errno);
return -1;
}
eventct++;
return 0;
}
static void wakealarm_set_interval(int interval) {
struct itimerspec itval;
if (wakealarm_fd == -1)
return;
wakealarm_wake_interval = interval;
if (interval == -1)
interval = 0;
itval.it_interval.tv_sec = interval;
itval.it_interval.tv_nsec = 0;
itval.it_value.tv_sec = interval;
itval.it_value.tv_nsec = 0;
if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)
KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
}
status_t healthd_get_property(int id, struct BatteryProperty *val) {
return gBatteryMonitor->getProperty(id, val);
}
void healthd_battery_update(void) {
// 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;
if (new_wake_interval != wakealarm_wake_interval)
wakealarm_set_interval(new_wake_interval);
// During awake periods poll at fast rate. If wake alarm is set at fast
// rate then just use the alarm; if wake alarm is set at slow rate then
// poll at fast rate while awake and let alarm wake up at slow rate when
// asleep.
if (healthd_config.periodic_chores_interval_fast == -1)
awake_poll_interval = -1;
else
awake_poll_interval =
new_wake_interval == healthd_config.periodic_chores_interval_fast ?
-1 : healthd_config.periodic_chores_interval_fast * 1000;
}
void healthd_dump_battery_state(int fd) {
gBatteryMonitor->dumpState(fd);
fsync(fd);
}
static void periodic_chores() {
healthd_battery_update();
}
#define UEVENT_MSG_LEN 2048
static void uevent_event(uint32_t /*epevents*/) {
char msg[UEVENT_MSG_LEN+2];
char *cp;
int n;
n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);
if (n <= 0)
return;
if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
return;
msg[n] = '\0';
msg[n+1] = '\0';
cp = msg;
while (*cp) {
if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {
healthd_battery_update();
break;
}
/* advance to after the next \0 */
while (*cp++)
;
}
}
static void uevent_init(void) {
uevent_fd = uevent_open_socket(64*1024, true);
if (uevent_fd < 0) {
KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");
return;
}
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"register for uevent events failed\n");
}
static void wakealarm_event(uint32_t /*epevents*/) {
unsigned long long wakeups;
if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {
KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
return;
}
periodic_chores();
}
static void wakealarm_init(void) {
wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);
if (wakealarm_fd == -1) {
KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
return;
}
if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"Registration of wakealarm event failed\n");
wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
}
static void healthd_mainloop(void) {
int nevents = 0;
while (1) {
struct epoll_event events[eventct];
int timeout = awake_poll_interval;
int mode_timeout;
/* Don't wait for first timer timeout to run periodic chores */
if (!nevents)
periodic_chores();
healthd_mode_ops->heartbeat();
mode_timeout = healthd_mode_ops->preparetowait();
if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
timeout = mode_timeout;
nevents = epoll_wait(epollfd, events, eventct, timeout);
if (nevents == -1) {
if (errno == EINTR)
continue;
KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");
break;
}
for (int n = 0; n < nevents; ++n) {
if (events[n].data.ptr)
(*(void (*)(int))events[n].data.ptr)(events[n].events);
}
}
return;
}
static int healthd_init() {
epollfd = epoll_create(MAX_EPOLL_EVENTS);
if (epollfd == -1) {
KLOG_ERROR(LOG_TAG,
"epoll_create failed; errno=%d\n",
errno);
return -1;
}
healthd_board_init(&healthd_config);
healthd_mode_ops->init(&healthd_config);
wakealarm_init();
uevent_init();
gBatteryMonitor = new BatteryMonitor();
gBatteryMonitor->init(&healthd_config);
return 0;
}
int healthd_main() {
int ret;
klog_set_level(KLOG_LEVEL);
if (!healthd_mode_ops) {
KLOG_ERROR("healthd ops not set, exiting\n");
exit(1);
}
ret = healthd_init();
if (ret) {
KLOG_ERROR("Initialization failed, exiting\n");
exit(2);
}
healthd_mainloop();
KLOG_ERROR("Main loop terminated, exiting\n");
return 3;
}

View File

@ -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);
}

View File

@ -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 @@ animation* init_animation() {
}
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 @@ void healthd_mode_charger_init(struct healthd_config* config) {
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;
}

View File

@ -42,6 +42,7 @@ class BatteryMonitor {
int getChargeStatus();
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
private:
struct healthd_config *mHealthdConfig;

View File

@ -81,10 +81,6 @@ enum EventWakeup {
// Global helper functions
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
void healthd_battery_update();
android::status_t healthd_get_property(int id,
struct android::BatteryProperty *val);
void healthd_dump_battery_state(int fd);
struct healthd_mode_ops {
void (*init)(struct healthd_config *config);

View File

@ -0,0 +1,11 @@
<manifest version="1.0" type="framework">
<hal>
<name>android.hardware.health</name>
<transport>hwbinder</transport>
<version>2.0</version>
<interface>
<name>IHealth</name>
<instance>backup</instance>
</interface>
</hal>
</manifest>

View File

@ -36,6 +36,7 @@ static const std::set<std::string> kExportedActionableProperties = {
"init.svc.zygote",
"persist.bluetooth.btsnoopenable",
"persist.sys.crash_rcu",
"persist.sys.usb.usbradio.config",
"persist.sys.zram_enabled",
"ro.board.platform",
"ro.bootmode",

View File

@ -149,8 +149,8 @@ bool SetupMessageSockets(base::unique_fd (*result)[2]) {
}
constexpr int kMaxMessageSize = sizeof(FuseBuffer);
if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0 ||
setsockopt(fds[1], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0 ||
setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) {
PLOG(ERROR) << "Failed to update buffer size for socket";
return false;
}

View File

@ -25,7 +25,7 @@ namespace fuse {
// The numbers came from sdcard.c.
// Maximum number of bytes to write/read in one request/one reply.
constexpr size_t kFuseMaxWrite = 256 * 1024;
constexpr size_t kFuseMaxWrite = 128 * 1024;
constexpr size_t kFuseMaxRead = 128 * 1024;
constexpr int32_t kFuseSuccess = 0;

View File

@ -62,7 +62,7 @@ static inline uint64_t get8LE(const uint8_t* src) {
static const struct fs_path_config android_dirs[] = {
// clang-format off
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
{ 00500, AID_ROOT, AID_ROOT, 0, "config" },
{ 00555, AID_ROOT, AID_ROOT, 0, "config" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },

View File

@ -1 +1,2 @@
cwren@google.com
jhawkins@google.com

View File

@ -47,6 +47,8 @@ class ComplexEventLogger {
public:
// Create a complex event with category|category|.
explicit ComplexEventLogger(int category);
// Set the package name that this event originates from.
void SetPackageName(const std::string& package_name);
// Add tagged data to the event, with the given tag and integer value.
void AddTaggedData(int tag, int32_t value);
// Add tagged data to the event, with the given tag and string value.
@ -70,14 +72,49 @@ enum {
LOGBUILDER_VALUE = 802,
LOGBUILDER_COUNTER = 803,
LOGBUILDER_HISTOGRAM = 804,
LOGBUILDER_PACKAGENAME = 806,
ACTION_BOOT = 1098,
FIELD_PLATFORM_REASON = 1099,
FIELD_DURATION_MILLIS = 1304,
FIELD_END_BATTERY_PERCENT = 1308,
ACTION_HIDDEN_API_ACCESSED = 1391,
FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
FIELD_HIDDEN_API_SIGNATURE = 1394,
ACTION_USB_CONNECTOR_CONNECTED = 1422,
ACTION_USB_CONNECTOR_DISCONNECTED = 1423,
ACTION_USB_AUDIO_CONNECTED = 1424,
FIELD_USB_AUDIO_VIDPID = 1425,
ACTION_USB_AUDIO_DISCONNECTED = 1426,
ACTION_HARDWARE_FAILED = 1427,
FIELD_HARDWARE_TYPE = 1428,
FIELD_HARDWARE_FAILURE_CODE = 1429,
ACTION_PHYSICAL_DROP = 1430,
FIELD_CONFIDENCE_PERCENT = 1431,
FIELD_ACCEL_MILLI_G = 1432,
ACTION_BATTERY_HEALTH = 1433,
FIELD_BATTERY_HEALTH_SNAPSHOT_TYPE = 1434,
FIELD_BATTERY_TEMPERATURE_DECI_C = 1435,
FIELD_BATTERY_VOLTAGE_UV = 1436,
FIELD_BATTERY_OPEN_CIRCUIT_VOLTAGE_UV = 1437,
ACTION_BATTERY_CHARGE_CYCLES = 1438,
FIELD_BATTERY_CHARGE_CYCLES = 1439,
ACTION_SLOW_IO = 1442,
FIELD_IO_OPERATION_TYPE = 1443,
FIELD_IO_OPERATION_COUNT = 1444,
ACTION_SPEAKER_IMPEDANCE = 1445,
FIELD_SPEAKER_IMPEDANCE_MILLIOHMS = 1446,
FIELD_SPEAKER_LOCATION = 1447,
FIELD_BATTERY_RESISTANCE_UOHMS = 1448,
FIELD_BATTERY_CURRENT_UA = 1449,
FIELD_HARDWARE_LOCATION = 1450,
ACTION_BATTERY_CAUSED_SHUTDOWN = 1441,
};
enum {
@ -91,5 +128,30 @@ enum {
ACCESS_METHOD_LINKING = 3,
};
enum HardwareType {
HARDWARE_UNKNOWN = 0,
HARDWARE_MICROPHONE = 1,
HARDWARE_CODEC = 2,
HARDWARE_SPEAKER = 3,
HARDWARE_FINGERPRINT = 4,
};
enum HardwareFailureCode {
HARDWARE_FAILURE_UNKNOWN = 0,
HARDWARE_FAILURE_COMPLETE = 1,
HARDWARE_FAILURE_SPEAKER_HIGH_Z = 2,
HARDWARE_FAILURE_SPEAKER_SHORT = 3,
HARDWARE_FAILURE_FINGERPRINT_SENSOR_BROKEN = 4,
HARDWARE_FAILURE_FINGERPRINT_TOO_MANY_DEAD_PIXELS = 5,
};
enum IoOperation {
IOOP_UNKNOWN = 0,
IOOP_READ = 1,
IOOP_WRITE = 2,
IOOP_UNMAP = 3,
IOOP_SYNC = 4,
};
} // namespace metricslogger
} // namespace android

View File

@ -62,6 +62,10 @@ ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionT
logger << LOGBUILDER_CATEGORY << category;
}
void ComplexEventLogger::SetPackageName(const std::string& package_name) {
logger << LOGBUILDER_PACKAGENAME << package_name;
}
void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
logger << tag << value;
}

View File

@ -218,6 +218,20 @@ int receive_packet(int s, struct dhcp_msg *msg)
* to construct the pseudo header used in the checksum calculation.
*/
dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
/*
* check validity of dhcp_size.
* 1) cannot be negative or zero.
* 2) src buffer contains enough bytes to copy
* 3) cannot exceed destination buffer
*/
if ((dhcp_size <= 0) ||
((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
((int)sizeof(struct dhcp_msg) < dhcp_size)) {
#if VERBOSE
ALOGD("Malformed Packet");
#endif
return -1;
}
saddr = packet.ip.saddr;
daddr = packet.ip.daddr;
nread = ntohs(packet.ip.tot_len);

View File

@ -203,6 +203,15 @@ enum {
* (except disconnect and sending CAMERA_CMD_PING) after getting this.
*/
CAMERA_ERROR_RELEASED = 2,
/**
* Camera was released because device policy change or the client application
* is going to background. The client should call Camera::disconnect
* immediately after getting this notification. Otherwise, the camera will be
* released by camera service in a short time. The client should not call any
* method (except disconnect and sending CAMERA_CMD_PING) after getting this.
*/
CAMERA_ERROR_DISABLED = 3,
CAMERA_ERROR_SERVER_DIED = 100
};

View File

@ -0,0 +1,140 @@
// This file is autogenerated by hidl-gen. Do not edit manually.
// Source: android.hardware.graphics.common@1.0
// Location: hardware/interfaces/graphics/common/1.0/
#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
HAL_PIXEL_FORMAT_RGBA_8888 = 1,
HAL_PIXEL_FORMAT_RGBX_8888 = 2,
HAL_PIXEL_FORMAT_RGB_888 = 3,
HAL_PIXEL_FORMAT_RGB_565 = 4,
HAL_PIXEL_FORMAT_BGRA_8888 = 5,
HAL_PIXEL_FORMAT_YCBCR_422_SP = 16,
HAL_PIXEL_FORMAT_YCRCB_420_SP = 17,
HAL_PIXEL_FORMAT_YCBCR_422_I = 20,
HAL_PIXEL_FORMAT_RGBA_FP16 = 22,
HAL_PIXEL_FORMAT_RAW16 = 32,
HAL_PIXEL_FORMAT_BLOB = 33,
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34,
HAL_PIXEL_FORMAT_YCBCR_420_888 = 35,
HAL_PIXEL_FORMAT_RAW_OPAQUE = 36,
HAL_PIXEL_FORMAT_RAW10 = 37,
HAL_PIXEL_FORMAT_RAW12 = 38,
HAL_PIXEL_FORMAT_RGBA_1010102 = 43,
HAL_PIXEL_FORMAT_Y8 = 538982489,
HAL_PIXEL_FORMAT_Y16 = 540422489,
HAL_PIXEL_FORMAT_YV12 = 842094169,
} android_pixel_format_t;
typedef enum {
HAL_TRANSFORM_FLIP_H = 1, // (1 << 0)
HAL_TRANSFORM_FLIP_V = 2, // (1 << 1)
HAL_TRANSFORM_ROT_90 = 4, // (1 << 2)
HAL_TRANSFORM_ROT_180 = 3, // (FLIP_H | FLIP_V)
HAL_TRANSFORM_ROT_270 = 7, // ((FLIP_H | FLIP_V) | ROT_90)
} android_transform_t;
typedef enum {
HAL_DATASPACE_UNKNOWN = 0,
HAL_DATASPACE_ARBITRARY = 1,
HAL_DATASPACE_STANDARD_SHIFT = 16,
HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
HAL_DATASPACE_TRANSFER_SHIFT = 22,
HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
HAL_DATASPACE_RANGE_SHIFT = 27,
HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
HAL_DATASPACE_SRGB_LINEAR = 512,
HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_V0_SCRGB_LINEAR =
406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
HAL_DATASPACE_SRGB = 513,
HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
HAL_DATASPACE_JFIF = 257,
HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
HAL_DATASPACE_BT601_625 = 258,
HAL_DATASPACE_V0_BT601_625 =
281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_BT601_525 = 259,
HAL_DATASPACE_V0_BT601_525 =
281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_BT709 = 260,
HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
HAL_DATASPACE_DISPLAY_P3_LINEAR =
139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
HAL_DATASPACE_DEPTH = 4096,
HAL_DATASPACE_SENSOR = 4097,
} android_dataspace_t;
typedef enum {
HAL_COLOR_MODE_NATIVE = 0,
HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
HAL_COLOR_MODE_STANDARD_BT709 = 5,
HAL_COLOR_MODE_DCI_P3 = 6,
HAL_COLOR_MODE_SRGB = 7,
HAL_COLOR_MODE_ADOBE_RGB = 8,
HAL_COLOR_MODE_DISPLAY_P3 = 9,
} android_color_mode_t;
typedef enum {
HAL_COLOR_TRANSFORM_IDENTITY = 0,
HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
} android_color_transform_t;
typedef enum {
HAL_HDR_DOLBY_VISION = 1,
HAL_HDR_HDR10 = 2,
HAL_HDR_HLG = 3,
} android_hdr_t;
#ifdef __cplusplus
}
#endif
#endif // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_

View File

@ -0,0 +1,48 @@
// This file is autogenerated by hidl-gen. Do not edit manually.
// Source: android.hardware.graphics.common@1.1
// Location: hardware/interfaces/graphics/common/1.1/
#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
HAL_PIXEL_FORMAT_DEPTH_16 = 48,
HAL_PIXEL_FORMAT_DEPTH_24 = 49,
HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = 50,
HAL_PIXEL_FORMAT_DEPTH_32F = 51,
HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 = 52,
HAL_PIXEL_FORMAT_STENCIL_8 = 53,
HAL_PIXEL_FORMAT_YCBCR_P010 = 54,
} android_pixel_format_v1_1_t;
typedef enum {
HAL_DATASPACE_BT2020_ITU =
281411584, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_BT2020_ITU_PQ =
298188800, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_LIMITED)
HAL_DATASPACE_BT2020_ITU_HLG = 302383104, // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_LIMITED)
HAL_DATASPACE_BT2020_HLG = 168165376, // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_FULL)
} android_dataspace_v1_1_t;
typedef enum {
HAL_COLOR_MODE_BT2020 = 10,
HAL_COLOR_MODE_BT2100_PQ = 11,
HAL_COLOR_MODE_BT2100_HLG = 12,
} android_color_mode_v1_1_t;
typedef enum {
HAL_RENDER_INTENT_COLORIMETRIC = 0,
HAL_RENDER_INTENT_ENHANCE = 1,
HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC = 2,
HAL_RENDER_INTENT_TONE_MAP_ENHANCE = 3,
} android_render_intent_v1_1_t;
#ifdef __cplusplus
}
#endif
#endif // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_

View File

@ -1,141 +1,7 @@
// This file is autogenerated by hidl-gen. Do not edit manually.
// Source: android.hardware.graphics.common@1.0
// Root: android.hardware:hardware/interfaces
#ifndef SYSTEM_CORE_GRAPHICS_BASE_H_
#define SYSTEM_CORE_GRAPHICS_BASE_H_
#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
#include "graphics-base-v1.0.h"
#include "graphics-base-v1.1.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
HAL_PIXEL_FORMAT_RGBA_8888 = 1,
HAL_PIXEL_FORMAT_RGBX_8888 = 2,
HAL_PIXEL_FORMAT_RGB_888 = 3,
HAL_PIXEL_FORMAT_RGB_565 = 4,
HAL_PIXEL_FORMAT_BGRA_8888 = 5,
HAL_PIXEL_FORMAT_RGBA_1010102 = 43, // 0x2B
HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
HAL_PIXEL_FORMAT_RAW16 = 32, // 0x20
HAL_PIXEL_FORMAT_RAW10 = 37, // 0x25
HAL_PIXEL_FORMAT_RAW12 = 38, // 0x26
HAL_PIXEL_FORMAT_RAW_OPAQUE = 36, // 0x24
HAL_PIXEL_FORMAT_BLOB = 33, // 0x21
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34, // 0x22
HAL_PIXEL_FORMAT_YCBCR_420_888 = 35, // 0x23
HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
} android_pixel_format_t;
typedef enum {
HAL_TRANSFORM_FLIP_H = 1, // 0x01
HAL_TRANSFORM_FLIP_V = 2, // 0x02
HAL_TRANSFORM_ROT_90 = 4, // 0x04
HAL_TRANSFORM_ROT_180 = 3, // 0x03
HAL_TRANSFORM_ROT_270 = 7, // 0x07
} android_transform_t;
typedef enum {
HAL_DATASPACE_UNKNOWN = 0, // 0x0
HAL_DATASPACE_ARBITRARY = 1, // 0x1
HAL_DATASPACE_STANDARD_SHIFT = 16,
HAL_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
HAL_DATASPACE_TRANSFER_SHIFT = 22,
HAL_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
HAL_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
HAL_DATASPACE_RANGE_SHIFT = 27,
HAL_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
HAL_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
HAL_DATASPACE_SRGB_LINEAR = 512, // 0x200
HAL_DATASPACE_V0_SRGB_LINEAR = 138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_V0_SCRGB_LINEAR = 406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
HAL_DATASPACE_SRGB = 513, // 0x201
HAL_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
HAL_DATASPACE_V0_SCRGB = 411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
HAL_DATASPACE_JFIF = 257, // 0x101
HAL_DATASPACE_V0_JFIF = 146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
HAL_DATASPACE_BT601_625 = 258, // 0x102
HAL_DATASPACE_V0_BT601_625 = 281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_BT601_525 = 259, // 0x103
HAL_DATASPACE_V0_BT601_525 = 281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_BT709 = 260, // 0x104
HAL_DATASPACE_V0_BT709 = 281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
HAL_DATASPACE_DCI_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_DCI_P3 = 155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
HAL_DATASPACE_DISPLAY_P3_LINEAR = 139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_DISPLAY_P3 = 143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
HAL_DATASPACE_ADOBE_RGB = 151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
HAL_DATASPACE_BT2020_LINEAR = 138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
HAL_DATASPACE_BT2020 = 147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
HAL_DATASPACE_BT2020_PQ = 163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
HAL_DATASPACE_DEPTH = 4096, // 0x1000
HAL_DATASPACE_SENSOR = 4097, // 0x1001
} android_dataspace_t;
typedef enum {
HAL_COLOR_MODE_NATIVE = 0,
HAL_COLOR_MODE_STANDARD_BT601_625 = 1,
HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,
HAL_COLOR_MODE_STANDARD_BT601_525 = 3,
HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,
HAL_COLOR_MODE_STANDARD_BT709 = 5,
HAL_COLOR_MODE_DCI_P3 = 6,
HAL_COLOR_MODE_SRGB = 7,
HAL_COLOR_MODE_ADOBE_RGB = 8,
HAL_COLOR_MODE_DISPLAY_P3 = 9,
} android_color_mode_t;
typedef enum {
HAL_COLOR_TRANSFORM_IDENTITY = 0,
HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,
HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,
HAL_COLOR_TRANSFORM_GRAYSCALE = 3,
HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,
HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,
HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,
} android_color_transform_t;
typedef enum {
HAL_HDR_DOLBY_VISION = 1,
HAL_HDR_HDR10 = 2,
HAL_HDR_HLG = 3,
} android_hdr_t;
#ifdef __cplusplus
}
#endif
#endif // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
#endif // SYSTEM_CORE_GRAPHICS_BASE_H_

View File

@ -0,0 +1,16 @@
#ifndef SYSTEM_CORE_GRAPHICS_SW_H_
#define SYSTEM_CORE_GRAPHICS_SW_H_
/* Software formats not in the HAL definitions. */
typedef enum {
HAL_PIXEL_FORMAT_YCBCR_422_888 = 39, // 0x27
HAL_PIXEL_FORMAT_YCBCR_444_888 = 40, // 0x28
HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41, // 0x29
HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42, // 0x2A
} android_pixel_format_sw_t;
/* for compatibility */
#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
#endif // SYSTEM_CORE_GRAPHICS_SW_H_

View File

@ -25,6 +25,7 @@
* generated.
*/
#include "graphics-base.h"
#include "graphics-sw.h"
#ifdef __cplusplus
extern "C" {
@ -32,8 +33,6 @@ extern "C" {
/* for compatibility */
#define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888
#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888
#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888
#define HAL_PIXEL_FORMAT_YCbCr_422_SP HAL_PIXEL_FORMAT_YCBCR_422_SP
#define HAL_PIXEL_FORMAT_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP
#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
@ -257,6 +256,11 @@ struct android_smpte2086_metadata {
float minLuminance;
};
struct android_cta861_3_metadata {
float maxContentLightLevel;
float maxFrameAverageLightLevel;
};
#ifdef __cplusplus
}
#endif

View File

@ -137,6 +137,7 @@ uint16_t usb_device_get_vendor_id(struct usb_device *device);
/* 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 a USB descriptor string for the given string ID.
@ -156,6 +157,12 @@ const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_
int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
size_t* response_size);
/* 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.

View File

@ -76,9 +76,11 @@ struct usb_host_context {
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;
@ -387,6 +389,8 @@ struct usb_device *usb_device_new(const char *dev_name, int fd)
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;
@ -455,11 +459,18 @@ uint16_t usb_device_get_product_id(struct usb_device *device)
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;
}
/* Returns a USB descriptor string for the given string ID.
* Return value: < 0 on error. 0 on success.
* The string is returned in ucs2_out in USB-native UCS-2 encoding.

View File

@ -66,6 +66,23 @@ static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
return getEmptyString();
}
static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len) {
if (u16len >= SIZE_MAX / sizeof(char16_t)) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
SharedBuffer* buf = SharedBuffer::alloc((u16len + 1) * sizeof(char16_t));
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str, u16str, u16len * sizeof(char16_t));
str[u16len] = 0;
return str;
}
return getEmptyString();
}
// ---------------------------------------------------------------------------
String16::String16()
@ -98,35 +115,9 @@ String16::String16(const String16& o, size_t len, size_t begin)
setTo(o, len, begin);
}
String16::String16(const char16_t* o)
{
size_t len = strlen16(o);
SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (buf) {
char16_t* str = (char16_t*)buf->data();
strcpy16(str, o);
mString = str;
return;
}
String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {}
mString = getEmptyString();
}
String16::String16(const char16_t* o, size_t len)
{
SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
ALOG_ASSERT(buf, "Unable to allocate shared buffer");
if (buf) {
char16_t* str = (char16_t*)buf->data();
memcpy(str, o, len*sizeof(char16_t));
str[len] = 0;
mString = str;
return;
}
mString = getEmptyString();
}
String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
String16::String16(const String8& o)
: mString(allocFromUTF8(o.string(), o.size()))
@ -188,6 +179,11 @@ status_t String16::setTo(const char16_t* other)
status_t String16::setTo(const char16_t* other, size_t len)
{
if (len >= SIZE_MAX / sizeof(char16_t)) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((len+1)*sizeof(char16_t));
if (buf) {
@ -211,6 +207,11 @@ status_t String16::append(const String16& other)
return NO_ERROR;
}
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {
@ -232,6 +233,11 @@ status_t String16::append(const char16_t* chrs, size_t otherLen)
return NO_ERROR;
}
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
android_errorWriteLog(0x534e4554, "73826242");
abort();
}
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
->editResize((myLen+otherLen+1)*sizeof(char16_t));
if (buf) {

View File

@ -643,6 +643,55 @@ TEST(ziparchive, ErrorCodeString) {
ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
}
// A zip file whose local file header at offset zero is corrupted.
//
// ---------------
// cat foo > a.txt
// zip a.zip a.txt
// cat a.zip | xxd -i
//
// Manual changes :
// [2] = 0xff // Corrupt the LFH signature of entry 0.
// [3] = 0xff // Corrupt the LFH signature of entry 0.
static const std::vector<uint8_t> kZipFileWithBrokenLfhSignature{
//[lfh-sig-----------], [lfh contents---------------------------------
0x50, 0x4b, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80,
//--------------------------------------------------------------------
0x09, 0x4b, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00,
//-------------------------------] [file-name-----------------], [---
0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55,
// entry-contents------------------------------------------------------
0x54, 0x09, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x51, 0x24, 0x8b, 0x59,
//--------------------------------------------------------------------
0x75, 0x78, 0x0b, 0x00, 0x01, 0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88,
//-------------------------------------], [cd-record-sig-------], [---
0x13, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x0a, 0x50, 0x4b, 0x01, 0x02, 0x1e,
// cd-record-----------------------------------------------------------
0x03, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x09, 0x4b, 0xa8,
//--------------------------------------------------------------------
0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05,
//--------------------------------------------------------------------
0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0,
//-] [lfh-file-header-off-], [file-name-----------------], [extra----
0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x2e, 0x74, 0x78, 0x74, 0x55, 0x54,
//--------------------------------------------------------------------
0x05, 0x00, 0x03, 0x51, 0x24, 0x8b, 0x59, 0x75, 0x78, 0x0b, 0x00, 0x01,
//-------------------------------------------------------], [eocd-sig-
0x04, 0x89, 0x42, 0x00, 0x00, 0x04, 0x88, 0x13, 0x00, 0x00, 0x50, 0x4b,
//-------], [---------------------------------------------------------
0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x4b, 0x00,
//-------------------------------------------]
0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00};
TEST(ziparchive, BrokenLfhSignature) {
TemporaryFile tmp_file;
ASSERT_NE(-1, tmp_file.fd);
ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, &kZipFileWithBrokenLfhSignature[0],
kZipFileWithBrokenLfhSignature.size()));
ZipArchiveHandle handle;
ASSERT_EQ(-1, OpenArchiveFd(tmp_file.fd, "LeadingNonZipBytes", &handle));
}
class VectorReader : public zip_archive::Reader {
public:
VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}

View File

@ -6,9 +6,33 @@ cc_binary {
"libcutils",
"liblog",
],
static_libs: [
"libstatslogc",
"libstatssocket",
],
local_include_dirs: ["include"],
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
init_rc: ["lmkd.rc"],
product_variables: {
use_lmkd_stats_log: {
cflags: [
"-DLMKD_LOG_STATS"
],
},
},
}
cc_library_static {
name: "libstatslogc",
srcs: ["statslog.c"],
cflags: [
"-Wall",
"-Werror",
],
shared_libs: [
"liblog",
],
static_libs: ["libstatssocket",],
}
cc_library_static {

View File

@ -38,6 +38,10 @@
#include <lmkd.h>
#include <log/log.h>
#ifdef LMKD_LOG_STATS
#include "statslog.h"
#endif
/*
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
* to profile and correlate with OOM kills
@ -246,6 +250,11 @@ struct reread_data {
int fd;
};
#ifdef LMKD_LOG_STATS
static bool enable_stats_log;
static android_log_context log_ctx;
#endif
#define PIDHASH_SZ 1024
static struct proc *pidhash[PIDHASH_SZ];
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@ -719,6 +728,51 @@ static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
maxevents++;
}
#ifdef LMKD_LOG_STATS
static void memory_stat_parse_line(char *line, struct memory_stat *mem_st) {
char key[LINE_MAX];
int64_t value;
sscanf(line,"%s %" SCNd64 "", key, &value);
if (strcmp(key, "total_") < 0) {
return;
}
if (!strcmp(key, "total_pgfault"))
mem_st->pgfault = value;
else if (!strcmp(key, "total_pgmajfault"))
mem_st->pgmajfault = value;
else if (!strcmp(key, "total_rss"))
mem_st->rss_in_bytes = value;
else if (!strcmp(key, "total_cache"))
mem_st->cache_in_bytes = value;
else if (!strcmp(key, "total_swap"))
mem_st->swap_in_bytes = value;
}
static int memory_stat_parse(struct memory_stat *mem_st, int pid, uid_t uid) {
FILE *fp;
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
fp = fopen(buf, "r");
if (fp == NULL) {
ALOGE("%s open failed: %s", buf, strerror(errno));
return -1;
}
while (fgets(buf, PAGE_SIZE, fp) != NULL ) {
memory_stat_parse_line(buf, mem_st);
}
fclose(fp);
return 0;
}
#endif
/* /prop/zoneinfo parsing routines */
static int64_t zoneinfo_parse_protection(char *cp) {
int64_t max = 0;
@ -944,6 +998,11 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
int tasksize;
int r;
#ifdef LMKD_LOG_STATS
struct memory_stat mem_st = {};
int memory_stat_parse_result = -1;
#endif
taskname = proc_get_name(pid);
if (!taskname) {
pid_remove(pid);
@ -956,6 +1015,12 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
return -1;
}
#ifdef LMKD_LOG_STATS
if (enable_stats_log) {
memory_stat_parse_result = memory_stat_parse(&mem_st, pid, uid);
}
#endif
TRACE_KILL_START(pid);
/* CAP_KILL required */
@ -972,6 +1037,15 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
return -1;
} else {
#ifdef LMKD_LOG_STATS
if (memory_stat_parse_result == 0) {
stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
mem_st.cache_in_bytes, mem_st.swap_in_bytes);
}
#endif
return tasksize;
}
return tasksize;
@ -988,6 +1062,10 @@ static int find_and_kill_processes(enum vmpressure_level level,
int killed_size;
int pages_freed = 0;
#ifdef LMKD_LOG_STATS
bool lmk_state_change_start = false;
#endif
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
@ -1000,14 +1078,35 @@ static int find_and_kill_processes(enum vmpressure_level level,
killed_size = kill_one_process(procp, min_score_adj, level);
if (killed_size >= 0) {
#ifdef LMKD_LOG_STATS
if (enable_stats_log && !lmk_state_change_start) {
lmk_state_change_start = true;
stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
LMK_STATE_CHANGE_START);
}
#endif
pages_freed += killed_size;
if (pages_freed >= pages_to_free) {
#ifdef LMKD_LOG_STATS
if (enable_stats_log && lmk_state_change_start) {
stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
LMK_STATE_CHANGE_STOP);
}
#endif
return pages_freed;
}
}
}
}
#ifdef LMKD_LOG_STATS
if (enable_stats_log && lmk_state_change_start) {
stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
}
#endif
return pages_freed;
}
@ -1122,10 +1221,8 @@ static void mp_event_common(int data, uint32_t events __unused) {
}
if (skip_count > 0) {
if (debug_process_killing) {
ALOGI("%lu memory pressure events were skipped after a kill!",
skip_count);
}
ALOGI("%lu memory pressure events were skipped after a kill!",
skip_count);
skip_count = 0;
}
@ -1250,25 +1347,24 @@ do_kill:
return;
}
min_score_adj = level_oomadj[level];
} else {
if (debug_process_killing) {
ALOGI("Killing because cache %ldkB is below "
"limit %ldkB for oom_adj %d\n"
" Free memory is %ldkB %s reserved",
other_file * page_k, minfree * page_k, min_score_adj,
other_free * page_k, other_free >= 0 ? "above" : "below");
}
}
if (debug_process_killing) {
ALOGI("Trying to free %d pages", pages_to_free);
}
pages_freed = find_and_kill_processes(level, min_score_adj, pages_to_free);
if (use_minfree_levels) {
ALOGI("Killing because cache %ldkB is below "
"limit %ldkB for oom_adj %d\n"
" Free memory is %ldkB %s reserved",
other_file * page_k, minfree * page_k, min_score_adj,
other_free * page_k, other_free >= 0 ? "above" : "below");
}
if (pages_freed < pages_to_free) {
if (debug_process_killing) {
ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed);
}
ALOGI("Unable to free enough memory (pages to free=%d, pages freed=%d)",
pages_to_free, pages_freed);
} else {
ALOGI("Reclaimed enough memory (pages to free=%d, pages freed=%d)",
pages_to_free, pages_freed);
gettimeofday(&last_report_tm, NULL);
}
}
@ -1485,6 +1581,10 @@ int main(int argc __unused, char **argv __unused) {
per_app_memcg =
property_get_bool("ro.config.per_app_memcg", low_ram_device);
#ifdef LMKD_LOG_STATS
statslog_init(&log_ctx, &enable_stats_log);
#endif
if (!init()) {
if (!use_inkernel_interface) {
/*
@ -1512,6 +1612,10 @@ int main(int argc __unused, char **argv __unused) {
mainloop();
}
#ifdef LMKD_LOG_STATS
statslog_destroy(&log_ctx);
#endif
ALOGI("exiting");
return 0;
}

117
lmkd/statslog.c Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright 2018 Google, Inc
*
* 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 <assert.h>
#include <errno.h>
#include <log/log_id.h>
#include <stats_event_list.h>
#include <time.h>
static int64_t getElapsedRealTimeNs() {
struct timespec t;
t.tv_sec = t.tv_nsec = 0;
clock_gettime(CLOCK_BOOTTIME, &t);
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
}
/**
* Logs the change in LMKD state which is used as start/stop boundaries for logging
* LMK_KILL_OCCURRED event.
* Code: LMK_STATE_CHANGED = 54
*/
int
stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
assert(ctx != NULL);
int ret = -EINVAL;
if (!ctx) {
return ret;
}
reset_log_context(ctx);
if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
return ret;
}
if ((ret = android_log_write_int32(ctx, code)) < 0) {
return ret;
}
if ((ret = android_log_write_int32(ctx, state)) < 0) {
return ret;
}
return write_to_logger(ctx, LOG_ID_STATS);
}
/**
* Logs the event when LMKD kills a process to reduce memory pressure.
* Code: LMK_KILL_OCCURRED = 51
*/
int
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
int64_t swap_in_bytes) {
assert(ctx != NULL);
int ret = -EINVAL;
if (!ctx) {
return ret;
}
reset_log_context(ctx);
if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
return ret;
}
if ((ret = android_log_write_int32(ctx, code)) < 0) {
return ret;
}
if ((ret = android_log_write_int32(ctx, uid)) < 0) {
return ret;
}
if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
return ret;
}
if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
return ret;
}
if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
return ret;
}
if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
return ret;
}
if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
return ret;
}
if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
return ret;
}
if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
return ret;
}
return write_to_logger(ctx, LOG_ID_STATS);
}

91
lmkd/statslog.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright 2018 Google, Inc
*
* 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 _STATSLOG_H_
#define _STATSLOG_H_
#include <assert.h>
#include <stats_event_list.h>
#include <stdbool.h>
#include <sys/cdefs.h>
#include <cutils/properties.h>
__BEGIN_DECLS
/*
* These are defined in
* http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
*/
#define LMK_KILL_OCCURRED 51
#define LMK_STATE_CHANGED 54
#define LMK_STATE_CHANGE_START 1
#define LMK_STATE_CHANGE_STOP 2
/*
* The single event tag id for all stats logs.
* Keep this in sync with system/core/logcat/event.logtags
*/
const static int kStatsEventTag = 1937006964;
static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
assert(log_ctx != NULL);
assert(enable_stats_log != NULL);
*enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
if (*enable_stats_log) {
*log_ctx = create_android_logger(kStatsEventTag);
}
}
static inline void statslog_destroy(android_log_context* log_ctx) {
assert(log_ctx != NULL);
if (*log_ctx) {
android_log_destroy(log_ctx);
}
}
struct memory_stat {
int64_t pgfault;
int64_t pgmajfault;
int64_t rss_in_bytes;
int64_t cache_in_bytes;
int64_t swap_in_bytes;
};
#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
/**
* Logs the change in LMKD state which is used as start/stop boundaries for logging
* LMK_KILL_OCCURRED event.
* Code: LMK_STATE_CHANGED = 54
*/
int
stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
/**
* Logs the event when LMKD kills a process to reduce memory pressure.
* Code: LMK_KILL_OCCURRED = 51
*/
int
stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
char const* process_name, int32_t oom_score, int64_t pgfault,
int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
int64_t swap_in_bytes);
__END_DECLS
#endif /* _STATSLOG_H_ */

View File

@ -110,9 +110,14 @@ std::string getTextAround(const std::string& text, size_t pos,
}
bool getExecPath(std::string &path) {
// exec path as utf8z c_str().
// std::string contains _all_ nul terminated argv[] strings.
return android::base::ReadFileToString("/proc/self/cmdline", &path);
char buf[PATH_MAX + 1];
int ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
if (ret < 0) {
return false;
}
buf[ret] = '\0';
path = buf;
return true;
}
/* Child synchronization primitives */
@ -310,8 +315,8 @@ TEST(lmkd, check_for_oom) {
if (getuid() != static_cast<unsigned>(AID_ROOT)) {
// if not root respawn itself as root and capture output
std::string command = StringPrintf(
"%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
LMKDTEST_RESPAWN_FLAG, test_path.c_str());
"%s=true su root %s 2>&1", LMKDTEST_RESPAWN_FLAG,
test_path.c_str());
std::string test_output = readCommand(command);
GTEST_LOG_(INFO) << test_output;
} else {

View File

@ -1115,9 +1115,6 @@ log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
// client wants to start from the beginning
it = mLogElements.begin();
} else {
// 3 second limit to continue search for out-of-order entries.
log_time min = start - pruneMargin;
// Cap to 300 iterations we look back for out-of-order entries.
size_t count = 300;
@ -1133,7 +1130,7 @@ log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
} else if (element->getRealTime() == start) {
last = ++it;
break;
} else if (!--count || (element->getRealTime() < min)) {
} else if (!--count) {
break;
}
}

View File

@ -98,6 +98,9 @@ ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
endif
ifdef BOARD_USES_METADATA_PARTITION
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
endif
# For /odm partition.
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm

View File

@ -84,7 +84,7 @@ on init
restorecon_recursive /mnt
mount configfs none /config nodev noexec nosuid
chmod 0775 /config/sdcardfs
chmod 0770 /config/sdcardfs
chown system package_info /config/sdcardfs
mkdir /mnt/secure 0700 root root
@ -180,6 +180,12 @@ on init
copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
copy /dev/cpuset/mems /dev/cpuset/system-background/mems
# restricted is for system tasks that are being throttled
# due to screen off.
mkdir /dev/cpuset/restricted
copy /dev/cpuset/cpus /dev/cpuset/restricted/cpus
copy /dev/cpuset/mems /dev/cpuset/restricted/mems
mkdir /dev/cpuset/top-app
copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
copy /dev/cpuset/mems /dev/cpuset/top-app/mems
@ -190,11 +196,13 @@ on init
chown system system /dev/cpuset/background
chown system system /dev/cpuset/system-background
chown system system /dev/cpuset/top-app
chown system system /dev/cpuset/restricted
chown system system /dev/cpuset/tasks
chown system system /dev/cpuset/foreground/tasks
chown system system /dev/cpuset/background/tasks
chown system system /dev/cpuset/system-background/tasks
chown system system /dev/cpuset/top-app/tasks
chown system system /dev/cpuset/restricted/tasks
# set system-background to 0775 so SurfaceFlinger can touch it
chmod 0775 /dev/cpuset/system-background
@ -203,6 +211,7 @@ on init
chmod 0664 /dev/cpuset/background/tasks
chmod 0664 /dev/cpuset/system-background/tasks
chmod 0664 /dev/cpuset/top-app/tasks
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
@ -366,6 +375,10 @@ on post-fs
# create the lost+found directories, so as to enforce our permissions
mkdir /cache/lost+found 0770 root root
restorecon_recursive /metadata
mkdir /metadata/vold
chmod 0700 /metadata/vold
on late-fs
# Ensure that tracefs has the correct permissions.
# This does not work correctly if it is called in post-fs.
@ -424,6 +437,7 @@ on post-fs-data
mkdir /data/misc/carrierid 0770 system radio
mkdir /data/misc/apns 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system
mkdir /data/misc/vpn 0770 system vpn
mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
@ -456,6 +470,8 @@ on post-fs-data
mkdir /data/misc/gcov 0770 root root
mkdir /data/vendor 0771 root root
mkdir /data/vendor_ce 0771 root root
mkdir /data/vendor_de 0771 root root
mkdir /data/vendor/hardware 0771 root root
# For security reasons, /data/local/tmp should always be empty.
@ -688,6 +704,7 @@ on property:vold.decrypt=trigger_load_persist_props
on property:vold.decrypt=trigger_post_fs_data
trigger post-fs-data
trigger zygote-start
on property:vold.decrypt=trigger_restart_min_framework
# A/B update verifier that marks a successful boot.
@ -695,6 +712,8 @@ on property:vold.decrypt=trigger_restart_min_framework
class_start main
on property:vold.decrypt=trigger_restart_framework
stop surfaceflinger
start surfaceflinger
# A/B update verifier that marks a successful boot.
exec_start update_verifier
class_start main
@ -738,11 +757,6 @@ service ueventd /system/bin/ueventd
seclabel u:r:ueventd:s0
shutdown critical
service healthd /system/bin/healthd
class core
critical
group root system wakelock
service console /system/bin/sh
class core
console

View File

@ -2,7 +2,6 @@ on property:sys.usb.config=none && property:sys.usb.configfs=1
write /config/usb_gadget/g1/UDC "none"
stop adbd
setprop sys.usb.ffs.ready 0
setprop sys.usb.ffs.mtp.ready 0
write /config/usb_gadget/g1/bDeviceClass 0
write /config/usb_gadget/g1/bDeviceSubClass 0
write /config/usb_gadget/g1/bDeviceProtocol 0
@ -24,7 +23,7 @@ on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.u
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:sys.usb.configfs=1
on property:sys.usb.config=mtp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@ -33,15 +32,14 @@ on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=mtp && property:s
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
start adbd
on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
setprop sys.usb.state ${sys.usb.config}
on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:sys.usb.configfs=1
on property:sys.usb.config=ptp && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
@ -50,8 +48,7 @@ on property:sys.usb.ffs.mtp.ready=1 && property:sys.usb.config=ptp && property:s
on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
start adbd
on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2

View File

@ -43,6 +43,44 @@
#include <private/android_filesystem_config.h>
#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
static bool supports_esdfs(void) {
std::string filesystems;
if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
PLOG(ERROR) << "Could not read /proc/filesystems";
return false;
}
for (const auto& fs : android::base::Split(filesystems, "\n")) {
if (fs.find("esdfs") != std::string::npos) return true;
}
return false;
}
static bool should_use_sdcardfs(void) {
char property[PROPERTY_VALUE_MAX];
// Allow user to have a strong opinion about state
property_get(PROP_SDCARDFS_USER, property, "");
if (!strcmp(property, "force_on")) {
LOG(WARNING) << "User explicitly enabled sdcardfs";
return true;
} else if (!strcmp(property, "force_off")) {
LOG(WARNING) << "User explicitly disabled sdcardfs";
return !supports_esdfs();
}
// Fall back to device opinion about state
if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {
LOG(WARNING) << "Device explicitly enabled sdcardfs";
return true;
} else {
LOG(WARNING) << "Device explicitly disabled sdcardfs";
return !supports_esdfs();
}
}
// NOTE: This is a vestigial program that simply exists to mount the in-kernel
// sdcardfs filesystem. The older FUSE-based design that used to live here has
// been completely removed to avoid confusion.
@ -61,7 +99,7 @@ static void drop_privs(uid_t uid, gid_t gid) {
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
mode_t mask, bool derive_gid, bool default_normal) {
mode_t mask, bool derive_gid, bool default_normal, bool use_esdfs) {
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
for (int i = 0; i < 4; i++) {
@ -72,7 +110,7 @@ static bool sdcardfs_setup(const std::string& source_path, const std::string& de
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? "esdfs" : "sdcardfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
} else {
@ -104,9 +142,21 @@ static bool sdcardfs_setup_bind_remount(const std::string& source_path, const st
return true;
}
static bool sdcardfs_setup_secondary(const std::string& default_path, const std::string& source_path,
const std::string& dest_path, uid_t fsuid, gid_t fsgid,
bool multi_user, userid_t userid, gid_t gid, mode_t mask,
bool derive_gid, bool default_normal, bool use_esdfs) {
if (use_esdfs) {
return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,
derive_gid, default_normal, use_esdfs);
} else {
return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);
}
}
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
bool derive_gid, bool default_normal) {
bool derive_gid, bool default_normal, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
@ -116,10 +166,13 @@ static void run_sdcardfs(const std::string& source_path, const std::string& labe
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
full_write ? 0007 : 0027)) {
AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
default_normal, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
derive_gid, default_normal, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
@ -127,11 +180,13 @@ static void run_sdcardfs(const std::string& source_path, const std::string& labe
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
full_write ? 0027 : 0022) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
full_write ? 0007 : 0022)) {
AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
derive_gid, default_normal, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,
derive_gid, default_normal, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
}
@ -242,6 +297,6 @@ int main(int argc, char **argv) {
}
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
default_normal);
default_normal, !should_use_sdcardfs());
return 1;
}

119
storaged/Android.bp Normal file
View File

@ -0,0 +1,119 @@
/*
* 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",
":storaged_aidl",
"binder/android/os/storaged/IStoragedPrivate.aidl",
],
static_libs: ["libhealthhalutils"],
header_libs: ["libbatteryservice_headers"],
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: [
"libhealthhalutils",
"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: [
"libhealthhalutils",
"libstoraged",
],
}
// AIDL interface between storaged and framework.jar
filegroup {
name: "storaged_aidl",
srcs: [
"binder/android/os/IStoraged.aidl",
],
}

View File

@ -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))

View File

@ -0,0 +1,24 @@
/*
* 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);
int getRecentPerf();
}

View File

@ -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();
}

View File

@ -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";

View File

@ -26,28 +26,18 @@
#include <unordered_map>
#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 IS_ALIGNED(x, align) (!((x) & ((align) - 1)))
#define ROUND_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
#define SECTOR_SIZE ( 512 )
#define SEC_TO_MSEC ( 1000 )
#define MSEC_TO_USEC ( 1000 )
@ -55,184 +45,24 @@ friend class test_case_name##_##test_name##_Test
#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 +71,35 @@ struct storaged_config {
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;
unique_ptr<disk_stats_monitor> mDsm;
uid_monitor mUidm;
time_t mStarttime;
sp<IBatteryPropertiesRegistrar> battery_properties;
std::unique_ptr<storage_info_t> storage_info;
public:
sp<android::hardware::health::V2_0::IHealth> health;
unique_ptr<storage_info_t> storage_info;
static const uint32_t current_version;
unordered_map<userid_t, bool> proto_loaded;
void load_proto(userid_t user_id);
char* prepare_proto(userid_t user_id, StoragedProto* proto);
void flush_proto(userid_t user_id, StoragedProto* proto);
void flush_proto_data(userid_t user_id, const char* data, ssize_t size);
string proto_path(userid_t user_id) {
return string("/data/misc_ce/") + to_string(user_id) +
"/storaged/storaged.proto";
}
void init_health_service();
public:
storaged_t(void);
~storaged_t() {}
void init(void);
void event(void);
void event_checked(void);
void pause(void) {
@ -270,24 +110,37 @@ public:
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();
}
uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }
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);
virtual ::android::hardware::Return<void> healthInfoChanged(
const ::android::hardware::health::V2_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

View File

@ -0,0 +1,199 @@
/*
* 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>
#include <android/hardware/health/2.0/IHealth.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;
android::sp<android::hardware::health::V2_0::IHealth> mHealth;
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(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
uint32_t window_size = 5, double sigma = 1.0)
: DISK_STATS_PATH(
healthService != nullptr
? nullptr
: (access(MMC_DISK_STATS_PATH, R_OK) == 0
? MMC_DISK_STATS_PATH
: (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH : nullptr))),
mPrevious(),
mAccumulate(),
mAccumulate_pub(),
mStall(false),
mValid(false),
mWindow(window_size),
mSigma(sigma),
mMean(),
mStd(),
mHealth(healthService) {}
bool enabled() { return mHealth != nullptr || DISK_STATS_PATH != nullptr; }
void update(void);
void publish(void);
};
#endif /* _STORAGED_DISKSTATS_H_ */

View File

@ -19,14 +19,26 @@
#include <string.h>
#include <chrono>
#include <android/hardware/health/2.0/IHealth.h>
#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:
protected:
FRIEND_TEST(storaged_test, storage_info_t);
FRIEND_TEST(storaged_test, storage_info_t_proto);
// emmc lifetime
uint16_t eol; // pre-eol (end of life) information
uint16_t lifetime_a; // device life time estimation (type A)
@ -36,16 +48,38 @@ protected:
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() {}
public:
static storage_info_t* get_storage_info(
const sp<android::hardware::health::V2_0::IHealth>& healthService);
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();
uint32_t get_recent_perf();
};
class emmc_info_t : public storage_info_t {
@ -69,4 +103,18 @@ public:
virtual void report();
};
class health_storage_info_t : public storage_info_t {
private:
using IHealth = hardware::health::V2_0::IHealth;
using StorageInfo = hardware::health::V2_0::StorageInfo;
sp<IHealth> mHealth;
void set_values_from_hal_storage_info(const StorageInfo& halInfo);
public:
health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
virtual ~health_storage_info_t() {}
virtual void report();
};
#endif /* _STORAGED_INFO_H_ */

View File

@ -19,42 +19,38 @@
#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);
binder::Status getRecentPerf(int32_t* _aidl_return);
};
// 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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -0,0 +1,77 @@
/*
* 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>
#include <binder/Parcelable.h>
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_ */

View File

@ -39,72 +39,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();
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;
@ -119,7 +128,12 @@ int main(int argc, char** argv) {
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);
@ -127,23 +141,33 @@ int main(int argc, char** argv) {
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;

View File

@ -16,184 +16,118 @@
#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-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <batteryservice/BatteryServiceConstants.h>
#include <batteryservice/IBatteryPropertiesRegistrar.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <healthhalutils/HealthHalUtils.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 ssize_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);
}
constexpr ssize_t min_benchmark_size = 128 * 1024; // 128KB
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);
}
} // namespace
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);
}
const uint32_t storaged_t::current_version = 4;
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);
using android::hardware::interfacesEqual;
using android::hardware::Return;
using android::hardware::health::V1_0::BatteryStatus;
using android::hardware::health::V1_0::toString;
using android::hardware::health::V2_0::get_health_service;
using android::hardware::health::V2_0::HealthInfo;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::Result;
using android::hidl::manager::V1_0::IServiceManager;
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));
}
}
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();
}
}
mPrevious = *stats;
}
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) {
mUidm.set_charger_state(is_charger_on(props.batteryStatus));
Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
return android::hardware::Void();
}
void storaged_t::init_battery_service() {
if (!mConfig.proc_uid_io_available)
void storaged_t::init() {
init_health_service();
mDsm = std::make_unique<disk_stats_monitor>(health);
storage_info.reset(storage_info_t::get_storage_info(health));
}
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 +140,195 @@ void storaged_t::report_storage_info() {
/* 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);
storage_info.reset(storage_info_t::get_storage_info());
mConfig.periodic_chores_interval_flush_proto =
property_get_int32("ro.storaged.flush_proto.interval",
DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
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());
const UidIOUsage& uid_io_usage = proto.uid_io_usage();
uint32_t computed_crc = crc32(current_version,
reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
uid_io_usage.ByteSize());
if (proto.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());
}
}
char* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {
proto->set_version(current_version);
const UidIOUsage& uid_io_usage = proto->uid_io_usage();
proto->set_crc(crc32(current_version,
reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
uid_io_usage.ByteSize()));
uint32_t pagesize = sysconf(_SC_PAGESIZE);
if (user_id == USER_SYSTEM) {
proto->set_padding("", 1);
vector<char> padding;
ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
pagesize);
padding = vector<char>(size - proto->ByteSize(), 0xFD);
proto->set_padding(padding.data(), padding.size());
while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
padding.push_back(0xFD);
proto->set_padding(padding.data(), padding.size());
}
}
if (mConfig.proc_uid_io_available && mTimer &&
(mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
mUidm.report();
char* data = nullptr;
if (posix_memalign(reinterpret_cast<void**>(&data),
pagesize, proto->ByteSize())) {
PLOG_TO(SYSTEM, ERROR) << "Faied to alloc aligned buffer (size: "
<< proto->ByteSize() << ")";
return data;
}
proto->SerializeToArray(data, proto->ByteSize());
return data;
}
void storaged_t::flush_proto_data(userid_t user_id,
const char* data, ssize_t size) {
string proto_file = proto_path(user_id);
string tmp_file = proto_file + "_tmp";
unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),
O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |
(user_id == USER_SYSTEM ? O_DIRECT : 0),
S_IRUSR | S_IWUSR)));
if (fd == -1) {
PLOG_TO(SYSTEM, ERROR) << "Faied to open tmp file: " << tmp_file;
return;
}
if (user_id == USER_SYSTEM) {
time_point<steady_clock> start, end;
uint32_t benchmark_size = 0;
uint64_t benchmark_time_ns = 0;
ssize_t ret;
bool first_write = true;
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 (!first_write && ret == benchmark_unit_size) {
benchmark_size += benchmark_unit_size;
benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();
}
size -= ret;
data += ret;
first_write = false;
}
if (benchmark_size) {
int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
storage_info->update_perf_history(perf, system_clock::now());
}
} else {
if (!WriteFully(fd, data, size)) {
PLOG_TO(SYSTEM, ERROR) << "Faied to write tmp file: " << tmp_file;
return;
}
}
fd.reset(-1);
rename(tmp_file.c_str(), proto_file.c_str());
}
void storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {
unique_ptr<char> proto_data(prepare_proto(user_id, proto));
if (proto_data == nullptr) return;
flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
}
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;

60
storaged/storaged.proto Normal file
View File

@ -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;
optional bytes padding = 5;
}

View File

@ -5,4 +5,4 @@ service storaged /system/bin/storaged
file /d/mmc0/mmc0:0001/ext_csd r
writepid /dev/cpuset/system-background/tasks
user root
group package_info
group package_info

View File

@ -0,0 +1,326 @@
/*
* 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 {
using android::sp;
using android::hardware::health::V2_0::DiskStats;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::Result;
using android::hardware::health::V2_0::toString;
#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 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;
}
return true;
}
void init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {
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;
}
bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
// Get time
struct timespec ts;
if (!get_time(&ts)) {
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
init_disk_stats_other(ts, stats);
return true;
}
void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
dst->read_ios = src.reads;
dst->read_merges = src.readMerges;
dst->read_sectors = src.readSectors;
dst->read_ticks = src.readTicks;
dst->write_ios = src.writes;
dst->write_merges = src.writeMerges;
dst->write_sectors = src.writeSectors;
dst->write_ticks = src.writeTicks;
dst->io_in_flight = src.ioInFlight;
dst->io_ticks = src.ioTicks;
dst->io_in_queue = src.ioInQueue;
}
bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
struct timespec ts;
if (!get_time(&ts)) {
return false;
}
bool success = false;
auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
if (result != Result::SUCCESS || halStats.size() == 0) {
LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
<< " and size " << halStats.size();
return;
}
convert_hal_disk_stats(stats, halStats[0]);
success = true;
});
if (!ret.isOk()) {
LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
return false;
}
if (!success) {
return false;
}
init_disk_stats_other(ts, stats);
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 (mHealth != nullptr) {
if (!get_disk_stats_from_health_hal(mHealth, &curr)) {
return;
}
} else {
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));
}

View File

@ -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,16 @@
#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;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::Result;
using android::hardware::health::V2_0::StorageInfo;
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,14 +48,20 @@ const char* emmc_info_t::emmc_ver_str[9] = {
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;
}
storage_info_t* storage_info_t::get_storage_info()
{
} // namespace
storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
if (healthService != nullptr) {
return new health_storage_info_t(healthService);
}
if (FileExists(emmc_info_t::emmc_sysfs) ||
FileExists(emmc_info_t::emmc_debugfs)) {
return new emmc_info_t;
@ -57,7 +72,39 @@ storage_info_t* storage_info_t::get_storage_info()
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 +114,24 @@ void storage_info_t::refresh()
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 +141,95 @@ void storage_info_t::publish()
<< 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;
}
if (nr_samples < recent_perf.size()) {
recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());
}
uint32_t daily_avg_bw = 0;
if (!recent_perf.empty()) {
daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();
}
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;
}
DCHECK(nr_days > 0);
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;
}
uint32_t storage_info_t::get_recent_perf() {
Mutex::Autolock _l(si_mutex);
if (recent_perf.size() == 0) return 0;
return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
recent_perf.size();
}
void emmc_info_t::report()
{
if (!report_sysfs() && !report_debugfs())
@ -121,6 +275,8 @@ bool emmc_info_t::report_sysfs()
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 +284,8 @@ const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
} // namespace
bool emmc_info_t::report_debugfs()
{
string buffer;
@ -210,3 +368,25 @@ void ufs_info_t::report()
publish();
}
void health_storage_info_t::report() {
auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
if (result != Result::SUCCESS || halInfos.size() == 0) {
LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
<< " and size " << halInfos.size();
return;
}
set_values_from_hal_storage_info(halInfos[0]);
publish();
});
if (!ret.isOk()) {
LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
}
}
void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
eol = halInfo.eol;
lifetime_a = halInfo.lifetimeA;
lifetime_b = halInfo.lifetimeB;
version = halInfo.version;
}

View File

@ -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 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) {
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,87 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) {
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();
}
binder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {
uint32_t recent_perf = storaged_sp->get_recent_perf();
if (recent_perf > INT32_MAX) {
*_aidl_return = INT32_MAX;
} else {
*_aidl_return = static_cast<int32_t>(recent_perf);
}
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);
}

View File

@ -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 @@ static void get_uid_names(const vector<int>& uids, const vector<std::string*>& u
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 @@ std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_lock
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 @@ std::unordered_map<uint32_t, struct uid_info> uid_monitor::get_uid_io_stats_lock
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 @@ void uid_monitor::add_records_locked(uint64_t curr_ts)
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 @@ std::map<uint64_t, struct uid_records> uid_monitor::dump(
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 @@ std::map<uint64_t, struct uid_records> uid_monitor::dump(
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 @@ void uid_monitor::update_curr_io_stats_locked()
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::set_charger_state(charger_stat_t stat)
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)) {
}

View File

@ -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 @@ static bool cmp_uid_info(struct uid_info l, struct uid_info r) {
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;
}

View File

@ -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)

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <chrono>
#include <deque>
#include <fcntl.h>
#include <random>
@ -24,13 +25,20 @@
#include <gtest/gtest.h>
#include <healthhalutils/HealthHalUtils.h>
#include <storaged.h> // data structures
#include <storaged_utils.h> // functions to test
#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 +61,8 @@ static void pause(uint32_t sec) {
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 +87,11 @@ TEST(storaged_test, retvals) {
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 +101,7 @@ TEST(storaged_test, disk_stats) {
}
// 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 +110,18 @@ TEST(storaged_test, disk_stats) {
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 +129,7 @@ static double mean(std::deque<uint32_t> nums) {
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 +181,7 @@ TEST(storaged_test, stream_stats) {
}
}
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 +192,7 @@ static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
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,11 +210,42 @@ static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_st
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) {
using android::hardware::health::V2_0::get_health_service;
auto healthService = get_health_service();
// 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);
ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
access(SDA_DISK_STATS_PATH, R_OK) >= 0);
// testing if detect() will return the right value
disk_stats_monitor dsm_detect;
disk_stats_monitor dsm_detect{healthService};
ASSERT_TRUE(dsm_detect.enabled());
// feed monitor with constant perf data for io perf baseline
// using constant perf is reasonable since the functionality of stream_stats
// has already been tested
@ -257,7 +288,7 @@ TEST(storaged_test, disk_stats_monitor) {
}
// testing if stalled disk_stats can be correctly accumulated in the monitor
disk_stats_monitor dsm_acc;
disk_stats_monitor dsm_acc{healthService};
struct disk_stats norm_inc = {
.read_ios = 200,
.read_merges = 0,
@ -294,14 +325,12 @@ TEST(storaged_test, disk_stats_monitor) {
.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 +345,284 @@ TEST(storaged_test, disk_stats_monitor) {
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, storage_info_t_proto) {
storage_info_t si;
si.day_start_tp = {};
IOPerfHistory proto;
proto.set_nr_samples(10);
proto.set_day_start_sec(0);
si.load_perf_history_proto(proto);
// Skip ahead > 1 day, with no data points in the previous day.
time_point<system_clock> stp;
stp += hours(36);
si.update_perf_history(100, stp);
vector<int> history = si.get_perf_history();
EXPECT_EQ(history.size(), 63UL);
EXPECT_EQ(history[0], 1);
EXPECT_EQ(history[1], 7);
EXPECT_EQ(history[2], 52);
EXPECT_EQ(history[3], 100);
for (size_t i = 4; 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);
}

181
storaged/tools/ranker.py Normal file
View File

@ -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()

52
storaged/uid_info.cpp Normal file
View File

@ -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;
}

View File

@ -39,7 +39,6 @@ cc_binary {
"libcrypto",
"libcutils",
"libkeymaster_portable",
"libkeymaster_staging",
"libtrusty",
"libkeymaster_messages",
"libsoftkeymasterdevice",