Merge "Merge Android Pie into master"
This commit is contained in:
commit
5d707816ac
80 changed files with 4002 additions and 1943 deletions
|
|
@ -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)
|
void adf_free_device_data(struct adf_device_data *data)
|
||||||
{
|
{
|
||||||
delete [] data->attachments;
|
delete [] data->attachments;
|
||||||
|
data->attachments = nullptr;
|
||||||
delete [] data->allowed_attachments;
|
delete [] data->allowed_attachments;
|
||||||
|
data->allowed_attachments = nullptr;
|
||||||
delete [] static_cast<char *>(data->custom_data);
|
delete [] static_cast<char *>(data->custom_data);
|
||||||
|
data->custom_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int adf_device_post(struct adf_device *dev,
|
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;
|
return err;
|
||||||
|
|
||||||
std::vector<adf_id_t> ids;
|
std::vector<adf_id_t> ids;
|
||||||
for (size_t i = 0; i < data.n_allowed_attachments; i++)
|
if (data.allowed_attachments != nullptr)
|
||||||
if (data.allowed_attachments[i].overlay_engine == overlay_engine)
|
for (size_t i = 0; i < data.n_allowed_attachments; i++)
|
||||||
ids.push_back(data.allowed_attachments[i].interface);
|
if (data.allowed_attachments[i].overlay_engine == overlay_engine)
|
||||||
|
ids.push_back(data.allowed_attachments[i].interface);
|
||||||
|
|
||||||
adf_free_device_data(&data);
|
adf_free_device_data(&data);
|
||||||
return adf_id_vector_to_array(ids, interfaces);
|
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;
|
return err;
|
||||||
|
|
||||||
std::vector<adf_id_t> ids;
|
std::vector<adf_id_t> ids;
|
||||||
for (size_t i = 0; i < data.n_allowed_attachments; i++)
|
if (data.allowed_attachments != nullptr)
|
||||||
if (data.allowed_attachments[i].interface == interface)
|
for (size_t i = 0; i < data.n_allowed_attachments; i++)
|
||||||
ids.push_back(data.allowed_attachments[i].overlay_engine);
|
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);
|
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)
|
void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data)
|
||||||
{
|
{
|
||||||
delete [] data->supported_formats;
|
delete [] data->supported_formats;
|
||||||
|
data->supported_formats = nullptr;
|
||||||
delete [] static_cast<char *>(data->custom_data);
|
delete [] static_cast<char *>(data->custom_data);
|
||||||
|
data->custom_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool adf_overlay_engine_supports_format(int fd, __u32 format)
|
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)
|
if (err < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (i = 0; i < data.n_supported_formats; i++) {
|
if (data.supported_formats != nullptr) {
|
||||||
if (data.supported_formats[i] == format) {
|
for (i = 0; i < data.n_supported_formats; i++) {
|
||||||
ret = true;
|
if (data.supported_formats[i] == format) {
|
||||||
break;
|
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,
|
const __u32 *formats, size_t n_formats,
|
||||||
adf_id_t interface, adf_id_t *overlay_engine)
|
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);
|
ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs);
|
||||||
|
|
||||||
if (n_engs <= 0)
|
if (engs == nullptr)
|
||||||
return false;
|
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,
|
ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev,
|
||||||
formats, n_formats, engs, n_engs, &filtered_engs);
|
formats, n_formats, engs, n_engs, &filtered_engs);
|
||||||
free(engs);
|
free(engs);
|
||||||
|
|
||||||
if (n_filtered_engs <= 0)
|
if (filtered_engs == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*overlay_engine = filtered_engs[0];
|
*overlay_engine = filtered_engs[0];
|
||||||
|
|
@ -700,17 +709,17 @@ int adf_find_simple_post_configuration(struct adf_device *dev,
|
||||||
|
|
||||||
if (n_intfs < 0)
|
if (n_intfs < 0)
|
||||||
return n_intfs;
|
return n_intfs;
|
||||||
else if (!n_intfs)
|
else if (!intfs)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
adf_id_t *primary_intfs;
|
adf_id_t *primary_intfs = nullptr;
|
||||||
ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
|
ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev,
|
||||||
ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
|
ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs);
|
||||||
free(intfs);
|
free(intfs);
|
||||||
|
|
||||||
if (n_primary_intfs < 0)
|
if (n_primary_intfs < 0)
|
||||||
return n_primary_intfs;
|
return n_primary_intfs;
|
||||||
else if (!n_primary_intfs)
|
else if (!primary_intfs)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!formats) {
|
if (!formats) {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@ cc_binary {
|
||||||
name: "bootstat",
|
name: "bootstat",
|
||||||
defaults: ["bootstat_defaults"],
|
defaults: ["bootstat_defaults"],
|
||||||
static_libs: ["libbootstat"],
|
static_libs: ["libbootstat"],
|
||||||
|
shared_libs: [
|
||||||
|
"libstatslog"
|
||||||
|
],
|
||||||
init_rc: ["bootstat.rc"],
|
init_rc: ["bootstat.rc"],
|
||||||
product_variables: {
|
product_variables: {
|
||||||
pdk: {
|
pdk: {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@
|
||||||
#include <cutils/android_reboot.h>
|
#include <cutils/android_reboot.h>
|
||||||
#include <cutils/properties.h>
|
#include <cutils/properties.h>
|
||||||
#include <metricslogger/metrics_logger.h>
|
#include <metricslogger/metrics_logger.h>
|
||||||
|
#include <statslog.h>
|
||||||
|
|
||||||
#include "boot_event_record_store.h"
|
#include "boot_event_record_store.h"
|
||||||
|
|
||||||
|
|
@ -1026,6 +1027,16 @@ const BootloaderTimingMap GetBootLoaderTimings() {
|
||||||
return timings;
|
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
|
// Parses and records the set of bootloader stages and associated boot times
|
||||||
// from the ro.boot.boottime system property.
|
// from the ro.boot.boottime system property.
|
||||||
void RecordBootloaderTimings(BootEventRecordStore* boot_event_store,
|
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);
|
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.
|
// from power on to boot_complete, including bootloader times.
|
||||||
void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
|
std::chrono::milliseconds GetAbsoluteBootTime(const BootloaderTimingMap& bootloader_timings,
|
||||||
const BootloaderTimingMap& bootloader_timings,
|
std::chrono::milliseconds uptime) {
|
||||||
std::chrono::milliseconds uptime) {
|
|
||||||
int32_t bootloader_time_ms = 0;
|
int32_t bootloader_time_ms = 0;
|
||||||
|
|
||||||
for (const auto& timing : bootloader_timings) {
|
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 bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);
|
||||||
auto absolute_total =
|
return bootloader_duration + uptime;
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(bootloader_duration + uptime);
|
|
||||||
boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total.count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the boot time offset. This is useful when Android is running in a
|
// Records the closest estimation to the absolute device boot time in seconds.
|
||||||
// container, because the boot_clock is not reset when Android reboots.
|
// i.e. from power on to boot_complete, including bootloader times.
|
||||||
std::chrono::nanoseconds GetBootTimeOffset() {
|
void RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,
|
||||||
static const int64_t boottime_offset =
|
std::chrono::milliseconds absolute_total) {
|
||||||
android::base::GetIntProperty<int64_t>("ro.boot.boottime_offset", 0);
|
auto absolute_total_sec = std::chrono::duration_cast<std::chrono::seconds>(absolute_total);
|
||||||
return std::chrono::nanoseconds(boottime_offset);
|
boot_event_store->AddBootEventWithValue("absolute_boot_time", absolute_total_sec.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME
|
// Logs the total boot time and reason to statsd.
|
||||||
// clock.
|
void LogBootInfoToStatsd(std::chrono::milliseconds end_time,
|
||||||
android::base::boot_clock::duration GetUptime() {
|
std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,
|
||||||
return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
|
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() {
|
void SetSystemBootReason() {
|
||||||
|
|
@ -1088,6 +1111,20 @@ void SetSystemBootReason() {
|
||||||
SetProperty(last_reboot_reason_property, "");
|
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,
|
// Records several metrics related to the time it takes to boot the device,
|
||||||
// including disambiguating boot time on encrypted or non-encrypted devices.
|
// including disambiguating boot time on encrypted or non-encrypted devices.
|
||||||
void RecordBootComplete() {
|
void RecordBootComplete() {
|
||||||
|
|
@ -1097,10 +1134,11 @@ void RecordBootComplete() {
|
||||||
auto uptime_ns = GetUptime();
|
auto uptime_ns = GetUptime();
|
||||||
auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);
|
auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);
|
||||||
time_t current_time_utc = time(nullptr);
|
time_t current_time_utc = time(nullptr);
|
||||||
|
time_t time_since_last_boot = 0;
|
||||||
|
|
||||||
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
|
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
|
||||||
time_t last_boot_time_utc = record.second;
|
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);
|
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");
|
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.cold_boot_wait");
|
||||||
|
|
||||||
const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
|
const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();
|
||||||
|
int32_t bootloader_boot_duration = GetBootloaderTime(bootloader_timings);
|
||||||
RecordBootloaderTimings(&boot_event_store, bootloader_timings);
|
RecordBootloaderTimings(&boot_event_store, bootloader_timings);
|
||||||
|
|
||||||
auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(uptime_ns);
|
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
|
// Records the boot_reason metric by querying the ro.boot.bootreason system
|
||||||
|
|
|
||||||
|
|
@ -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("encrypt");
|
||||||
mkf2fs_args.push_back("-O");
|
mkf2fs_args.push_back("-O");
|
||||||
mkf2fs_args.push_back("quota");
|
mkf2fs_args.push_back("quota");
|
||||||
|
mkf2fs_args.push_back("-O");
|
||||||
|
mkf2fs_args.push_back("verity");
|
||||||
mkf2fs_args.push_back(fileName);
|
mkf2fs_args.push_back(fileName);
|
||||||
mkf2fs_args.push_back(nullptr);
|
mkf2fs_args.push_back(nullptr);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ static int format_f2fs(char *fs_blkdev, uint64_t dev_sz, bool crypt_footer)
|
||||||
"-f",
|
"-f",
|
||||||
"-O", "encrypt",
|
"-O", "encrypt",
|
||||||
"-O", "quota",
|
"-O", "quota",
|
||||||
|
"-O", "verity",
|
||||||
"-w", "4096",
|
"-w", "4096",
|
||||||
fs_blkdev,
|
fs_blkdev,
|
||||||
size_str.c_str(),
|
size_str.c_str(),
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
|
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
|
||||||
#define PERROR PLOG(ERROR) << 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:
|
/* fstab has the following format:
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
|
||||||
libbase \
|
libbase \
|
||||||
libutils \
|
libutils \
|
||||||
libcrypto \
|
libcrypto \
|
||||||
|
libkeystore_aidl \
|
||||||
libkeystore_binder \
|
libkeystore_binder \
|
||||||
libhidlbase \
|
libhidlbase \
|
||||||
libhidltransport \
|
libhidltransport \
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,15 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <android/security/IKeystoreService.h>
|
||||||
#include <binder/IPCThreadState.h>
|
#include <binder/IPCThreadState.h>
|
||||||
#include <binder/IServiceManager.h>
|
#include <binder/IServiceManager.h>
|
||||||
#include <binder/PermissionCache.h>
|
#include <binder/PermissionCache.h>
|
||||||
#include <gatekeeper/password_handle.h> // for password_handle_t
|
#include <gatekeeper/password_handle.h> // for password_handle_t
|
||||||
#include <hardware/gatekeeper.h>
|
#include <hardware/gatekeeper.h>
|
||||||
#include <hardware/hw_auth_token.h>
|
#include <hardware/hw_auth_token.h>
|
||||||
#include <keystore/IKeystoreService.h>
|
|
||||||
#include <keystore/keystore.h> // For error code
|
#include <keystore/keystore.h> // For error code
|
||||||
|
#include <keystore/keystore_return_types.h>
|
||||||
#include <log/log.h>
|
#include <log/log.h>
|
||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
#include <utils/String16.h>
|
#include <utils/String16.h>
|
||||||
|
|
@ -317,11 +318,15 @@ public:
|
||||||
// TODO: cache service?
|
// TODO: cache service?
|
||||||
sp<IServiceManager> sm = defaultServiceManager();
|
sp<IServiceManager> sm = defaultServiceManager();
|
||||||
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
|
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) {
|
if (service != NULL) {
|
||||||
auto ret = service->addAuthToken(*auth_token, *auth_token_length);
|
std::vector<uint8_t> auth_token_vector(*auth_token,
|
||||||
if (!ret.isOk()) {
|
(*auth_token) + *auth_token_length);
|
||||||
ALOGE("Failure sending auth token to KeyStore: %" PRId32, int32_t(ret));
|
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 {
|
} else {
|
||||||
ALOGE("Unable to communicate with KeyStore");
|
ALOGE("Unable to communicate with KeyStore");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
cc_library_headers {
|
cc_library_headers {
|
||||||
name: "libhealthd_headers",
|
name: "libhealthd_headers",
|
||||||
vendor_available: true,
|
vendor_available: true,
|
||||||
|
recovery_available: true,
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
header_libs: ["libbatteryservice_headers"],
|
header_libs: ["libbatteryservice_headers"],
|
||||||
export_header_lib_headers: ["libbatteryservice_headers"],
|
export_header_lib_headers: ["libbatteryservice_headers"],
|
||||||
|
|
@ -9,7 +10,9 @@ cc_library_headers {
|
||||||
cc_library_static {
|
cc_library_static {
|
||||||
name: "libbatterymonitor",
|
name: "libbatterymonitor",
|
||||||
srcs: ["BatteryMonitor.cpp"],
|
srcs: ["BatteryMonitor.cpp"],
|
||||||
|
cflags: ["-Wall", "-Werror"],
|
||||||
vendor_available: true,
|
vendor_available: true,
|
||||||
|
recovery_available: true,
|
||||||
export_include_dirs: ["include"],
|
export_include_dirs: ["include"],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libutils",
|
"libutils",
|
||||||
|
|
@ -18,3 +21,66 @@ cc_library_static {
|
||||||
header_libs: ["libhealthd_headers"],
|
header_libs: ["libhealthd_headers"],
|
||||||
export_header_lib_headers: ["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"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,6 @@
|
||||||
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
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)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_MODULE := libhealthd_draw
|
LOCAL_MODULE := libhealthd_draw
|
||||||
|
|
@ -45,6 +24,8 @@ else
|
||||||
LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
|
LOCAL_CFLAGS += -DHEALTHD_DRAW_SPLIT_OFFSET=0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
LOCAL_HEADER_LIBRARIES := libbatteryservice_headers
|
||||||
|
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
@ -68,6 +49,11 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := \
|
||||||
$(LOCAL_PATH)/include
|
$(LOCAL_PATH)/include
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := \
|
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 \
|
libminui \
|
||||||
libpng \
|
libpng \
|
||||||
libz \
|
libz \
|
||||||
|
|
@ -92,7 +78,6 @@ LOCAL_CHARGER_NO_UI := true
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
healthd_common.cpp \
|
|
||||||
charger.cpp \
|
charger.cpp \
|
||||||
|
|
||||||
LOCAL_MODULE := charger
|
LOCAL_MODULE := charger
|
||||||
|
|
@ -106,14 +91,17 @@ LOCAL_CFLAGS := -Werror
|
||||||
ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
|
ifeq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
|
||||||
LOCAL_CFLAGS += -DCHARGER_NO_UI
|
LOCAL_CFLAGS += -DCHARGER_NO_UI
|
||||||
endif
|
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_charger \
|
||||||
libhealthd_draw \
|
libhealthd_draw \
|
||||||
libbatterymonitor \
|
libbatterymonitor \
|
||||||
|
|
@ -124,6 +112,8 @@ LOCAL_STATIC_LIBRARIES := \
|
||||||
libm \
|
libm \
|
||||||
libc \
|
libc \
|
||||||
|
|
||||||
|
LOCAL_STATIC_LIBRARIES := $(CHARGER_STATIC_LIBRARIES)
|
||||||
|
|
||||||
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
|
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
|
||||||
LOCAL_STATIC_LIBRARIES += \
|
LOCAL_STATIC_LIBRARIES += \
|
||||||
libminui \
|
libminui \
|
||||||
|
|
@ -144,6 +134,21 @@ LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT) \
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
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)
|
ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
|
||||||
define _add-charger-image
|
define _add-charger-image
|
||||||
include $$(CLEAR_VARS)
|
include $$(CLEAR_VARS)
|
||||||
|
|
@ -171,41 +176,3 @@ include $(BUILD_PHONY_PACKAGE)
|
||||||
_add-charger-image :=
|
_add-charger-image :=
|
||||||
_img_modules :=
|
_img_modules :=
|
||||||
endif # LOCAL_CHARGER_NO_UI
|
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)
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,10 @@ BatteryMonitor::BatteryMonitor()
|
||||||
initBatteryProperties(&props);
|
initBatteryProperties(&props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
|
||||||
|
return batteryMonitor->props;
|
||||||
|
}
|
||||||
|
|
||||||
int BatteryMonitor::getBatteryStatus(const char* status) {
|
int BatteryMonitor::getBatteryStatus(const char* status) {
|
||||||
int ret;
|
int ret;
|
||||||
struct sysfsStringEnumMap batteryStatusMap[] = {
|
struct sysfsStringEnumMap batteryStatusMap[] = {
|
||||||
|
|
@ -531,12 +535,6 @@ void BatteryMonitor::init(struct healthd_config *hc) {
|
||||||
POWER_SUPPLY_SYSFS_PATH, name);
|
POWER_SUPPLY_SYSFS_PATH, name);
|
||||||
if (access(path, R_OK) == 0) {
|
if (access(path, R_OK) == 0) {
|
||||||
mHealthdConfig->batteryVoltagePath = path;
|
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);
|
name);
|
||||||
if (access(path, R_OK) == 0) {
|
if (access(path, R_OK) == 0) {
|
||||||
mHealthdConfig->batteryTemperaturePath = path;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
39
healthd/HealthServiceDefault.cpp
Normal file
39
healthd/HealthServiceDefault.cpp
Normal 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();
|
||||||
|
}
|
||||||
93
healthd/HealthServiceHealthd.cpp
Normal file
93
healthd/HealthServiceHealthd.cpp
Normal 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");
|
||||||
|
}
|
||||||
5
healthd/android.hardware.health@2.0-service.rc
Normal file
5
healthd/android.hardware.health@2.0-service.rc
Normal 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
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#define LOG_TAG "charger"
|
#define LOG_TAG "charger"
|
||||||
#define KLOG_LEVEL 6
|
#define KLOG_LEVEL 6
|
||||||
|
|
||||||
|
#include <health2/Health.h>
|
||||||
#include <healthd/healthd.h>
|
#include <healthd/healthd.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -62,7 +63,9 @@ static struct healthd_mode_ops charger_ops = {
|
||||||
};
|
};
|
||||||
#endif
|
#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) {
|
static int healthd_mode_nop_preparetowait(void) {
|
||||||
|
|
@ -76,7 +79,7 @@ static void healthd_mode_nop_battery_update(
|
||||||
struct android::BatteryProperties* /*props*/) {
|
struct android::BatteryProperties* /*props*/) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int healthd_charger_main(int argc, char** argv) {
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
healthd_mode_ops = &charger_ops;
|
healthd_mode_ops = &charger_ops;
|
||||||
|
|
@ -100,3 +103,9 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
return healthd_main();
|
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
181
healthd/charger_test.cpp
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
4
healthd/healthd.rc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
service healthd /system/bin/healthd
|
||||||
|
class hal
|
||||||
|
critical
|
||||||
|
group root system wakelock
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
#include "AnimationParser.h"
|
#include "AnimationParser.h"
|
||||||
#include "healthd_draw.h"
|
#include "healthd_draw.h"
|
||||||
|
|
||||||
|
#include <health2/Health.h>
|
||||||
#include <healthd/healthd.h>
|
#include <healthd/healthd.h>
|
||||||
|
|
||||||
using namespace android;
|
using namespace android;
|
||||||
|
|
@ -612,6 +613,8 @@ animation* init_animation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void healthd_mode_charger_init(struct healthd_config* config) {
|
void healthd_mode_charger_init(struct healthd_config* config) {
|
||||||
|
using android::hardware::health::V2_0::implementation::Health;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
charger* charger = &charger_state;
|
charger* charger = &charger_state;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -666,6 +669,10 @@ void healthd_mode_charger_init(struct healthd_config* config) {
|
||||||
charger->next_screen_transition = -1;
|
charger->next_screen_transition = -1;
|
||||||
charger->next_key_check = -1;
|
charger->next_key_check = -1;
|
||||||
charger->next_pwr_check = -1;
|
charger->next_pwr_check = -1;
|
||||||
|
|
||||||
|
// Initialize Health implementation (which initializes the internal BatteryMonitor).
|
||||||
|
Health::initInstance(config);
|
||||||
|
|
||||||
healthd_config = config;
|
healthd_config = config;
|
||||||
charger->boot_min_cap = config->boot_min_cap;
|
charger->boot_min_cap = config->boot_min_cap;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ class BatteryMonitor {
|
||||||
int getChargeStatus();
|
int getChargeStatus();
|
||||||
status_t getProperty(int id, struct BatteryProperty *val);
|
status_t getProperty(int id, struct BatteryProperty *val);
|
||||||
void dumpState(int fd);
|
void dumpState(int fd);
|
||||||
|
friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct healthd_config *mHealthdConfig;
|
struct healthd_config *mHealthdConfig;
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,6 @@ enum EventWakeup {
|
||||||
// Global helper functions
|
// Global helper functions
|
||||||
|
|
||||||
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
|
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 {
|
struct healthd_mode_ops {
|
||||||
void (*init)(struct healthd_config *config);
|
void (*init)(struct healthd_config *config);
|
||||||
|
|
|
||||||
11
healthd/manifest_healthd.xml
Normal file
11
healthd/manifest_healthd.xml
Normal 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>
|
||||||
|
|
@ -36,6 +36,7 @@ static const std::set<std::string> kExportedActionableProperties = {
|
||||||
"init.svc.zygote",
|
"init.svc.zygote",
|
||||||
"persist.bluetooth.btsnoopenable",
|
"persist.bluetooth.btsnoopenable",
|
||||||
"persist.sys.crash_rcu",
|
"persist.sys.crash_rcu",
|
||||||
|
"persist.sys.usb.usbradio.config",
|
||||||
"persist.sys.zram_enabled",
|
"persist.sys.zram_enabled",
|
||||||
"ro.board.platform",
|
"ro.board.platform",
|
||||||
"ro.bootmode",
|
"ro.bootmode",
|
||||||
|
|
|
||||||
|
|
@ -149,8 +149,8 @@ bool SetupMessageSockets(base::unique_fd (*result)[2]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int kMaxMessageSize = sizeof(FuseBuffer);
|
constexpr int kMaxMessageSize = sizeof(FuseBuffer);
|
||||||
if (setsockopt(fds[0], 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_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
|
setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) {
|
||||||
PLOG(ERROR) << "Failed to update buffer size for socket";
|
PLOG(ERROR) << "Failed to update buffer size for socket";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace fuse {
|
||||||
|
|
||||||
// The numbers came from sdcard.c.
|
// The numbers came from sdcard.c.
|
||||||
// Maximum number of bytes to write/read in one request/one reply.
|
// 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 size_t kFuseMaxRead = 128 * 1024;
|
||||||
constexpr int32_t kFuseSuccess = 0;
|
constexpr int32_t kFuseSuccess = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ static inline uint64_t get8LE(const uint8_t* src) {
|
||||||
static const struct fs_path_config android_dirs[] = {
|
static const struct fs_path_config android_dirs[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
{ 00770, AID_SYSTEM, AID_CACHE, 0, "cache" },
|
{ 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" },
|
||||||
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
|
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
|
||||||
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
|
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
|
cwren@google.com
|
||||||
jhawkins@google.com
|
jhawkins@google.com
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ class ComplexEventLogger {
|
||||||
public:
|
public:
|
||||||
// Create a complex event with category|category|.
|
// Create a complex event with category|category|.
|
||||||
explicit ComplexEventLogger(int 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.
|
// Add tagged data to the event, with the given tag and integer value.
|
||||||
void AddTaggedData(int tag, int32_t value);
|
void AddTaggedData(int tag, int32_t value);
|
||||||
// Add tagged data to the event, with the given tag and string value.
|
// Add tagged data to the event, with the given tag and string value.
|
||||||
|
|
@ -70,14 +72,49 @@ enum {
|
||||||
LOGBUILDER_VALUE = 802,
|
LOGBUILDER_VALUE = 802,
|
||||||
LOGBUILDER_COUNTER = 803,
|
LOGBUILDER_COUNTER = 803,
|
||||||
LOGBUILDER_HISTOGRAM = 804,
|
LOGBUILDER_HISTOGRAM = 804,
|
||||||
|
LOGBUILDER_PACKAGENAME = 806,
|
||||||
|
|
||||||
ACTION_BOOT = 1098,
|
ACTION_BOOT = 1098,
|
||||||
FIELD_PLATFORM_REASON = 1099,
|
FIELD_PLATFORM_REASON = 1099,
|
||||||
|
|
||||||
|
FIELD_DURATION_MILLIS = 1304,
|
||||||
|
|
||||||
|
FIELD_END_BATTERY_PERCENT = 1308,
|
||||||
|
|
||||||
ACTION_HIDDEN_API_ACCESSED = 1391,
|
ACTION_HIDDEN_API_ACCESSED = 1391,
|
||||||
FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
|
FIELD_HIDDEN_API_ACCESS_METHOD = 1392,
|
||||||
FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
|
FIELD_HIDDEN_API_ACCESS_DENIED = 1393,
|
||||||
FIELD_HIDDEN_API_SIGNATURE = 1394,
|
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 {
|
enum {
|
||||||
|
|
@ -91,5 +128,30 @@ enum {
|
||||||
ACCESS_METHOD_LINKING = 3,
|
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 metricslogger
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,10 @@ ComplexEventLogger::ComplexEventLogger(int category) : logger(kSysuiMultiActionT
|
||||||
logger << LOGBUILDER_CATEGORY << category;
|
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) {
|
void ComplexEventLogger::AddTaggedData(int tag, int32_t value) {
|
||||||
logger << tag << value;
|
logger << tag << value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,20 @@ int receive_packet(int s, struct dhcp_msg *msg)
|
||||||
* to construct the pseudo header used in the checksum calculation.
|
* to construct the pseudo header used in the checksum calculation.
|
||||||
*/
|
*/
|
||||||
dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
|
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;
|
saddr = packet.ip.saddr;
|
||||||
daddr = packet.ip.daddr;
|
daddr = packet.ip.daddr;
|
||||||
nread = ntohs(packet.ip.tot_len);
|
nread = ntohs(packet.ip.tot_len);
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,15 @@ enum {
|
||||||
* (except disconnect and sending CAMERA_CMD_PING) after getting this.
|
* (except disconnect and sending CAMERA_CMD_PING) after getting this.
|
||||||
*/
|
*/
|
||||||
CAMERA_ERROR_RELEASED = 2,
|
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
|
CAMERA_ERROR_SERVER_DIED = 100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
140
libsystem/include/system/graphics-base-v1.0.h
Normal file
140
libsystem/include/system/graphics-base-v1.0.h
Normal 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_
|
||||||
48
libsystem/include/system/graphics-base-v1.1.h
Normal file
48
libsystem/include/system/graphics-base-v1.1.h
Normal 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_
|
||||||
|
|
@ -1,141 +1,7 @@
|
||||||
// This file is autogenerated by hidl-gen. Do not edit manually.
|
#ifndef SYSTEM_CORE_GRAPHICS_BASE_H_
|
||||||
// Source: android.hardware.graphics.common@1.0
|
#define SYSTEM_CORE_GRAPHICS_BASE_H_
|
||||||
// Root: android.hardware:hardware/interfaces
|
|
||||||
|
|
||||||
#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
|
#include "graphics-base-v1.0.h"
|
||||||
#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
|
#include "graphics-base-v1.1.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#endif // SYSTEM_CORE_GRAPHICS_BASE_H_
|
||||||
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_
|
|
||||||
|
|
|
||||||
16
libsystem/include/system/graphics-sw.h
Normal file
16
libsystem/include/system/graphics-sw.h
Normal 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_
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
* generated.
|
* generated.
|
||||||
*/
|
*/
|
||||||
#include "graphics-base.h"
|
#include "graphics-base.h"
|
||||||
|
#include "graphics-sw.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -32,8 +33,6 @@ extern "C" {
|
||||||
|
|
||||||
/* for compatibility */
|
/* for compatibility */
|
||||||
#define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888
|
#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_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_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP
|
||||||
#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
|
#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I
|
||||||
|
|
@ -257,6 +256,11 @@ struct android_smpte2086_metadata {
|
||||||
float minLuminance;
|
float minLuminance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct android_cta861_3_metadata {
|
||||||
|
float maxContentLightLevel;
|
||||||
|
float maxFrameAverageLightLevel;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
/* Returns the USB product ID from the device descriptor for the USB device */
|
||||||
uint16_t usb_device_get_product_id(struct usb_device *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);
|
const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
|
||||||
|
|
||||||
/* Returns a USB descriptor string for the given string ID.
|
/* 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,
|
int usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,
|
||||||
size_t* response_size);
|
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.
|
/* Returns a USB descriptor string for the given string ID.
|
||||||
* Used to implement usb_device_get_manufacturer_name,
|
* Used to implement usb_device_get_manufacturer_name,
|
||||||
* usb_device_get_product_name and usb_device_get_serial.
|
* usb_device_get_product_name and usb_device_get_serial.
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,11 @@ struct usb_host_context {
|
||||||
int wddbus;
|
int wddbus;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MAX_DESCRIPTORS_LENGTH 4096
|
||||||
|
|
||||||
struct usb_device {
|
struct usb_device {
|
||||||
char dev_name[64];
|
char dev_name[64];
|
||||||
unsigned char desc[4096];
|
unsigned char desc[MAX_DESCRIPTORS_LENGTH];
|
||||||
int desc_length;
|
int desc_length;
|
||||||
int fd;
|
int fd;
|
||||||
int writeable;
|
int writeable;
|
||||||
|
|
@ -387,6 +389,8 @@ struct usb_device *usb_device_new(const char *dev_name, int fd)
|
||||||
return device;
|
return device;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
|
// TODO It would be more appropriate to have callers do this
|
||||||
|
// since this function doesn't "own" this file descriptor.
|
||||||
close(fd);
|
close(fd);
|
||||||
free(device);
|
free(device);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -455,11 +459,18 @@ uint16_t usb_device_get_product_id(struct usb_device *device)
|
||||||
return __le16_to_cpu(desc->idProduct);
|
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;
|
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.
|
/* Returns a USB descriptor string for the given string ID.
|
||||||
* Return value: < 0 on error. 0 on success.
|
* Return value: < 0 on error. 0 on success.
|
||||||
* The string is returned in ucs2_out in USB-native UCS-2 encoding.
|
* The string is returned in ucs2_out in USB-native UCS-2 encoding.
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,23 @@ static char16_t* allocFromUTF8(const char* u8str, size_t u8len)
|
||||||
return getEmptyString();
|
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()
|
String16::String16()
|
||||||
|
|
@ -98,35 +115,9 @@ String16::String16(const String16& o, size_t len, size_t begin)
|
||||||
setTo(o, len, begin);
|
setTo(o, len, begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
String16::String16(const char16_t* o)
|
String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
mString = getEmptyString();
|
String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
|
||||||
}
|
|
||||||
|
|
||||||
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 String8& o)
|
String16::String16(const String8& o)
|
||||||
: mString(allocFromUTF8(o.string(), o.size()))
|
: 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)
|
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)
|
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||||
->editResize((len+1)*sizeof(char16_t));
|
->editResize((len+1)*sizeof(char16_t));
|
||||||
if (buf) {
|
if (buf) {
|
||||||
|
|
@ -211,6 +207,11 @@ status_t String16::append(const String16& other)
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
|
||||||
|
android_errorWriteLog(0x534e4554, "73826242");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||||
if (buf) {
|
if (buf) {
|
||||||
|
|
@ -232,6 +233,11 @@ status_t String16::append(const char16_t* chrs, size_t otherLen)
|
||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (myLen >= SIZE_MAX / sizeof(char16_t) - otherLen) {
|
||||||
|
android_errorWriteLog(0x534e4554, "73826242");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
|
||||||
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
->editResize((myLen+otherLen+1)*sizeof(char16_t));
|
||||||
if (buf) {
|
if (buf) {
|
||||||
|
|
|
||||||
|
|
@ -643,6 +643,55 @@ TEST(ziparchive, ErrorCodeString) {
|
||||||
ASSERT_STREQ("I/O error", ErrorCodeString(kIoError));
|
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 {
|
class VectorReader : public zip_archive::Reader {
|
||||||
public:
|
public:
|
||||||
VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
|
VectorReader(const std::vector<uint8_t>& input) : Reader(), input_(input) {}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,33 @@ cc_binary {
|
||||||
"libcutils",
|
"libcutils",
|
||||||
"liblog",
|
"liblog",
|
||||||
],
|
],
|
||||||
|
static_libs: [
|
||||||
|
"libstatslogc",
|
||||||
|
"libstatssocket",
|
||||||
|
],
|
||||||
local_include_dirs: ["include"],
|
local_include_dirs: ["include"],
|
||||||
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
|
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
|
||||||
init_rc: ["lmkd.rc"],
|
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 {
|
cc_library_static {
|
||||||
|
|
|
||||||
140
lmkd/lmkd.c
140
lmkd/lmkd.c
|
|
@ -38,6 +38,10 @@
|
||||||
#include <lmkd.h>
|
#include <lmkd.h>
|
||||||
#include <log/log.h>
|
#include <log/log.h>
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
#include "statslog.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
|
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
|
||||||
* to profile and correlate with OOM kills
|
* to profile and correlate with OOM kills
|
||||||
|
|
@ -246,6 +250,11 @@ struct reread_data {
|
||||||
int fd;
|
int fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
static bool enable_stats_log;
|
||||||
|
static android_log_context log_ctx;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PIDHASH_SZ 1024
|
#define PIDHASH_SZ 1024
|
||||||
static struct proc *pidhash[PIDHASH_SZ];
|
static struct proc *pidhash[PIDHASH_SZ];
|
||||||
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
#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++;
|
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 */
|
/* /prop/zoneinfo parsing routines */
|
||||||
static int64_t zoneinfo_parse_protection(char *cp) {
|
static int64_t zoneinfo_parse_protection(char *cp) {
|
||||||
int64_t max = 0;
|
int64_t max = 0;
|
||||||
|
|
@ -944,6 +998,11 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
|
||||||
int tasksize;
|
int tasksize;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
struct memory_stat mem_st = {};
|
||||||
|
int memory_stat_parse_result = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
taskname = proc_get_name(pid);
|
taskname = proc_get_name(pid);
|
||||||
if (!taskname) {
|
if (!taskname) {
|
||||||
pid_remove(pid);
|
pid_remove(pid);
|
||||||
|
|
@ -956,6 +1015,12 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
|
||||||
return -1;
|
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);
|
TRACE_KILL_START(pid);
|
||||||
|
|
||||||
/* CAP_KILL required */
|
/* CAP_KILL required */
|
||||||
|
|
@ -972,6 +1037,15 @@ static int kill_one_process(struct proc* procp, int min_score_adj,
|
||||||
if (r) {
|
if (r) {
|
||||||
ALOGE("kill(%d): errno=%d", pid, errno);
|
ALOGE("kill(%d): errno=%d", pid, errno);
|
||||||
return -1;
|
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;
|
return tasksize;
|
||||||
|
|
@ -988,6 +1062,10 @@ static int find_and_kill_processes(enum vmpressure_level level,
|
||||||
int killed_size;
|
int killed_size;
|
||||||
int pages_freed = 0;
|
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--) {
|
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
|
||||||
struct proc *procp;
|
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);
|
killed_size = kill_one_process(procp, min_score_adj, level);
|
||||||
if (killed_size >= 0) {
|
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;
|
pages_freed += killed_size;
|
||||||
if (pages_freed >= pages_to_free) {
|
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;
|
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;
|
return pages_freed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1122,10 +1221,8 @@ static void mp_event_common(int data, uint32_t events __unused) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip_count > 0) {
|
if (skip_count > 0) {
|
||||||
if (debug_process_killing) {
|
ALOGI("%lu memory pressure events were skipped after a kill!",
|
||||||
ALOGI("%lu memory pressure events were skipped after a kill!",
|
skip_count);
|
||||||
skip_count);
|
|
||||||
}
|
|
||||||
skip_count = 0;
|
skip_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1250,25 +1347,24 @@ do_kill:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
min_score_adj = level_oomadj[level];
|
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);
|
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 (pages_freed < pages_to_free) {
|
||||||
if (debug_process_killing) {
|
ALOGI("Unable to free enough memory (pages to free=%d, pages freed=%d)",
|
||||||
ALOGI("Unable to free enough memory (pages freed=%d)", pages_freed);
|
pages_to_free, pages_freed);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
ALOGI("Reclaimed enough memory (pages to free=%d, pages freed=%d)",
|
||||||
|
pages_to_free, pages_freed);
|
||||||
gettimeofday(&last_report_tm, NULL);
|
gettimeofday(&last_report_tm, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1485,6 +1581,10 @@ int main(int argc __unused, char **argv __unused) {
|
||||||
per_app_memcg =
|
per_app_memcg =
|
||||||
property_get_bool("ro.config.per_app_memcg", low_ram_device);
|
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 (!init()) {
|
||||||
if (!use_inkernel_interface) {
|
if (!use_inkernel_interface) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -1512,6 +1612,10 @@ int main(int argc __unused, char **argv __unused) {
|
||||||
mainloop();
|
mainloop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef LMKD_LOG_STATS
|
||||||
|
statslog_destroy(&log_ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
ALOGI("exiting");
|
ALOGI("exiting");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
117
lmkd/statslog.c
Normal file
117
lmkd/statslog.c
Normal 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
91
lmkd/statslog.h
Normal 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_ */
|
||||||
|
|
@ -110,9 +110,14 @@ std::string getTextAround(const std::string& text, size_t pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getExecPath(std::string &path) {
|
bool getExecPath(std::string &path) {
|
||||||
// exec path as utf8z c_str().
|
char buf[PATH_MAX + 1];
|
||||||
// std::string contains _all_ nul terminated argv[] strings.
|
int ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
|
||||||
return android::base::ReadFileToString("/proc/self/cmdline", &path);
|
if (ret < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buf[ret] = '\0';
|
||||||
|
path = buf;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Child synchronization primitives */
|
/* Child synchronization primitives */
|
||||||
|
|
@ -310,8 +315,8 @@ TEST(lmkd, check_for_oom) {
|
||||||
if (getuid() != static_cast<unsigned>(AID_ROOT)) {
|
if (getuid() != static_cast<unsigned>(AID_ROOT)) {
|
||||||
// if not root respawn itself as root and capture output
|
// if not root respawn itself as root and capture output
|
||||||
std::string command = StringPrintf(
|
std::string command = StringPrintf(
|
||||||
"%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
|
"%s=true su root %s 2>&1", LMKDTEST_RESPAWN_FLAG,
|
||||||
LMKDTEST_RESPAWN_FLAG, test_path.c_str());
|
test_path.c_str());
|
||||||
std::string test_output = readCommand(command);
|
std::string test_output = readCommand(command);
|
||||||
GTEST_LOG_(INFO) << test_output;
|
GTEST_LOG_(INFO) << test_output;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1115,9 +1115,6 @@ log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
|
||||||
// client wants to start from the beginning
|
// client wants to start from the beginning
|
||||||
it = mLogElements.begin();
|
it = mLogElements.begin();
|
||||||
} else {
|
} 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.
|
// Cap to 300 iterations we look back for out-of-order entries.
|
||||||
size_t count = 300;
|
size_t count = 300;
|
||||||
|
|
||||||
|
|
@ -1133,7 +1130,7 @@ log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
|
||||||
} else if (element->getRealTime() == start) {
|
} else if (element->getRealTime() == start) {
|
||||||
last = ++it;
|
last = ++it;
|
||||||
break;
|
break;
|
||||||
} else if (!--count || (element->getRealTime() < min)) {
|
} else if (!--count) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ ifdef BOARD_USES_PRODUCT_SERVICESIMAGE
|
||||||
else
|
else
|
||||||
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
|
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product-services $(TARGET_ROOT_OUT)/product-services
|
||||||
endif
|
endif
|
||||||
|
ifdef BOARD_USES_METADATA_PARTITION
|
||||||
|
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
|
||||||
|
endif
|
||||||
|
|
||||||
# For /odm partition.
|
# For /odm partition.
|
||||||
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
|
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ on init
|
||||||
restorecon_recursive /mnt
|
restorecon_recursive /mnt
|
||||||
|
|
||||||
mount configfs none /config nodev noexec nosuid
|
mount configfs none /config nodev noexec nosuid
|
||||||
chmod 0775 /config/sdcardfs
|
chmod 0770 /config/sdcardfs
|
||||||
chown system package_info /config/sdcardfs
|
chown system package_info /config/sdcardfs
|
||||||
|
|
||||||
mkdir /mnt/secure 0700 root root
|
mkdir /mnt/secure 0700 root root
|
||||||
|
|
@ -180,6 +180,12 @@ on init
|
||||||
copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
|
copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus
|
||||||
copy /dev/cpuset/mems /dev/cpuset/system-background/mems
|
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
|
mkdir /dev/cpuset/top-app
|
||||||
copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
|
copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus
|
||||||
copy /dev/cpuset/mems /dev/cpuset/top-app/mems
|
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/background
|
||||||
chown system system /dev/cpuset/system-background
|
chown system system /dev/cpuset/system-background
|
||||||
chown system system /dev/cpuset/top-app
|
chown system system /dev/cpuset/top-app
|
||||||
|
chown system system /dev/cpuset/restricted
|
||||||
chown system system /dev/cpuset/tasks
|
chown system system /dev/cpuset/tasks
|
||||||
chown system system /dev/cpuset/foreground/tasks
|
chown system system /dev/cpuset/foreground/tasks
|
||||||
chown system system /dev/cpuset/background/tasks
|
chown system system /dev/cpuset/background/tasks
|
||||||
chown system system /dev/cpuset/system-background/tasks
|
chown system system /dev/cpuset/system-background/tasks
|
||||||
chown system system /dev/cpuset/top-app/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
|
# set system-background to 0775 so SurfaceFlinger can touch it
|
||||||
chmod 0775 /dev/cpuset/system-background
|
chmod 0775 /dev/cpuset/system-background
|
||||||
|
|
@ -203,6 +211,7 @@ on init
|
||||||
chmod 0664 /dev/cpuset/background/tasks
|
chmod 0664 /dev/cpuset/background/tasks
|
||||||
chmod 0664 /dev/cpuset/system-background/tasks
|
chmod 0664 /dev/cpuset/system-background/tasks
|
||||||
chmod 0664 /dev/cpuset/top-app/tasks
|
chmod 0664 /dev/cpuset/top-app/tasks
|
||||||
|
chmod 0664 /dev/cpuset/restricted/tasks
|
||||||
chmod 0664 /dev/cpuset/tasks
|
chmod 0664 /dev/cpuset/tasks
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -366,6 +375,10 @@ on post-fs
|
||||||
# create the lost+found directories, so as to enforce our permissions
|
# create the lost+found directories, so as to enforce our permissions
|
||||||
mkdir /cache/lost+found 0770 root root
|
mkdir /cache/lost+found 0770 root root
|
||||||
|
|
||||||
|
restorecon_recursive /metadata
|
||||||
|
mkdir /metadata/vold
|
||||||
|
chmod 0700 /metadata/vold
|
||||||
|
|
||||||
on late-fs
|
on late-fs
|
||||||
# Ensure that tracefs has the correct permissions.
|
# Ensure that tracefs has the correct permissions.
|
||||||
# This does not work correctly if it is called in post-fs.
|
# 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/carrierid 0770 system radio
|
||||||
mkdir /data/misc/apns 0770 system radio
|
mkdir /data/misc/apns 0770 system radio
|
||||||
mkdir /data/misc/zoneinfo 0775 system system
|
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/textclassifier 0771 system system
|
||||||
mkdir /data/misc/vpn 0770 system vpn
|
mkdir /data/misc/vpn 0770 system vpn
|
||||||
mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
|
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/misc/gcov 0770 root root
|
||||||
|
|
||||||
mkdir /data/vendor 0771 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
|
mkdir /data/vendor/hardware 0771 root root
|
||||||
|
|
||||||
# For security reasons, /data/local/tmp should always be empty.
|
# 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
|
on property:vold.decrypt=trigger_post_fs_data
|
||||||
trigger post-fs-data
|
trigger post-fs-data
|
||||||
|
trigger zygote-start
|
||||||
|
|
||||||
on property:vold.decrypt=trigger_restart_min_framework
|
on property:vold.decrypt=trigger_restart_min_framework
|
||||||
# A/B update verifier that marks a successful boot.
|
# A/B update verifier that marks a successful boot.
|
||||||
|
|
@ -695,6 +712,8 @@ on property:vold.decrypt=trigger_restart_min_framework
|
||||||
class_start main
|
class_start main
|
||||||
|
|
||||||
on property:vold.decrypt=trigger_restart_framework
|
on property:vold.decrypt=trigger_restart_framework
|
||||||
|
stop surfaceflinger
|
||||||
|
start surfaceflinger
|
||||||
# A/B update verifier that marks a successful boot.
|
# A/B update verifier that marks a successful boot.
|
||||||
exec_start update_verifier
|
exec_start update_verifier
|
||||||
class_start main
|
class_start main
|
||||||
|
|
@ -738,11 +757,6 @@ service ueventd /system/bin/ueventd
|
||||||
seclabel u:r:ueventd:s0
|
seclabel u:r:ueventd:s0
|
||||||
shutdown critical
|
shutdown critical
|
||||||
|
|
||||||
service healthd /system/bin/healthd
|
|
||||||
class core
|
|
||||||
critical
|
|
||||||
group root system wakelock
|
|
||||||
|
|
||||||
service console /system/bin/sh
|
service console /system/bin/sh
|
||||||
class core
|
class core
|
||||||
console
|
console
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ on property:sys.usb.config=none && property:sys.usb.configfs=1
|
||||||
write /config/usb_gadget/g1/UDC "none"
|
write /config/usb_gadget/g1/UDC "none"
|
||||||
stop adbd
|
stop adbd
|
||||||
setprop sys.usb.ffs.ready 0
|
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/bDeviceClass 0
|
||||||
write /config/usb_gadget/g1/bDeviceSubClass 0
|
write /config/usb_gadget/g1/bDeviceSubClass 0
|
||||||
write /config/usb_gadget/g1/bDeviceProtocol 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}
|
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
|
||||||
setprop sys.usb.state ${sys.usb.config}
|
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"
|
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
|
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}
|
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
|
on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
|
||||||
start adbd
|
start adbd
|
||||||
|
|
||||||
on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
|
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=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"
|
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/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
|
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}
|
write /config/usb_gadget/g1/UDC ${sys.usb.controller}
|
||||||
setprop sys.usb.state ${sys.usb.config}
|
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"
|
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
|
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}
|
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
|
on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
|
||||||
start adbd
|
start adbd
|
||||||
|
|
||||||
on property:sys.usb.ffs.ready=1 && property:sys.usb.ffs.mtp.ready=1 && \
|
on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=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"
|
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/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
|
symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,44 @@
|
||||||
|
|
||||||
#include <private/android_filesystem_config.h>
|
#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
|
// 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
|
// sdcardfs filesystem. The older FUSE-based design that used to live here has
|
||||||
// been completely removed to avoid confusion.
|
// 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,
|
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,
|
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
|
// Try several attempts, each time with one less option, to gracefully
|
||||||
// handle older kernels that aren't updated yet.
|
// handle older kernels that aren't updated yet.
|
||||||
for (int i = 0; i < 4; i++) {
|
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",
|
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
|
||||||
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
|
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) {
|
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
|
||||||
PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
|
PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -104,9 +142,21 @@ static bool sdcardfs_setup_bind_remount(const std::string& source_path, const st
|
||||||
return true;
|
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,
|
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,
|
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_default = "/mnt/runtime/default/" + label;
|
||||||
std::string dest_path_read = "/mnt/runtime/read/" + label;
|
std::string dest_path_read = "/mnt/runtime/read/" + label;
|
||||||
std::string dest_path_write = "/mnt/runtime/write/" + 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"
|
// Multi-user storage is fully isolated per user, so "other"
|
||||||
// permissions are completely masked off.
|
// permissions are completely masked off.
|
||||||
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
|
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
|
||||||
AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
|
AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
|
||||||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
|
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
|
||||||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
|
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
|
||||||
full_write ? 0007 : 0027)) {
|
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";
|
LOG(FATAL) << "failed to sdcardfs_setup";
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
// the Android directories are masked off to a single user
|
||||||
// deep inside attr_from_stat().
|
// deep inside attr_from_stat().
|
||||||
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
|
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
|
||||||
AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
|
AID_SDCARD_RW, 0006, derive_gid, default_normal, use_esdfs) ||
|
||||||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
|
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
|
||||||
full_write ? 0027 : 0022) ||
|
multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,
|
||||||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
|
derive_gid, default_normal, use_esdfs) ||
|
||||||
full_write ? 0007 : 0022)) {
|
!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";
|
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,
|
run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
|
||||||
default_normal);
|
default_normal, !should_use_sdcardfs());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
storaged/Android.bp
Normal file
119
storaged/Android.bp
Normal 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",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -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))
|
|
||||||
24
storaged/binder/android/os/IStoraged.aidl
Normal file
24
storaged/binder/android/os/IStoraged.aidl
Normal 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();
|
||||||
|
}
|
||||||
25
storaged/binder/android/os/storaged/IStoragedPrivate.aidl
Normal file
25
storaged/binder/android/os/storaged/IStoragedPrivate.aidl
Normal 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();
|
||||||
|
}
|
||||||
19
storaged/binder/android/os/storaged/UidInfo.aidl
Normal file
19
storaged/binder/android/os/storaged/UidInfo.aidl
Normal 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";
|
||||||
|
|
@ -26,28 +26,18 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <batteryservice/IBatteryPropertiesListener.h>
|
#include <utils/Mutex.h>
|
||||||
#include <batteryservice/IBatteryPropertiesRegistrar.h>
|
|
||||||
|
|
||||||
#include "storaged_info.h"
|
#include <android/hardware/health/2.0/IHealth.h>
|
||||||
#include "storaged_uid_monitor.h"
|
|
||||||
|
|
||||||
using namespace android;
|
|
||||||
|
|
||||||
#define FRIEND_TEST(test_case_name, test_name) \
|
#define FRIEND_TEST(test_case_name, test_name) \
|
||||||
friend class test_case_name##_##test_name##_Test
|
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 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 SECTOR_SIZE ( 512 )
|
||||||
#define SEC_TO_MSEC ( 1000 )
|
#define SEC_TO_MSEC ( 1000 )
|
||||||
#define MSEC_TO_USEC ( 1000 )
|
#define MSEC_TO_USEC ( 1000 )
|
||||||
|
|
@ -55,184 +45,24 @@ friend class test_case_name##_##test_name##_Test
|
||||||
#define SEC_TO_USEC ( 1000000 )
|
#define SEC_TO_USEC ( 1000000 )
|
||||||
#define HOUR_TO_SEC ( 3600 )
|
#define HOUR_TO_SEC ( 3600 )
|
||||||
#define DAY_TO_SEC ( 3600 * 24 )
|
#define DAY_TO_SEC ( 3600 * 24 )
|
||||||
|
#define WEEK_TO_DAYS ( 7 )
|
||||||
|
#define YEAR_TO_WEEKS ( 52 )
|
||||||
|
|
||||||
// number of attributes diskstats has
|
#include "storaged_diskstats.h"
|
||||||
#define DISK_STATS_SIZE ( 11 )
|
#include "storaged_info.h"
|
||||||
// maximum size limit of a stats file
|
#include "storaged_uid_monitor.h"
|
||||||
#define DISK_STATS_FILE_MAX_SIZE ( 256 )
|
#include "storaged.pb.h"
|
||||||
#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
|
#include "uid_info.h"
|
||||||
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
|
using namespace std;
|
||||||
uint64_t end_time; // monotonic time accounting ends
|
using namespace android;
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Periodic chores intervals in seconds
|
// Periodic chores intervals in seconds
|
||||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
|
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )
|
||||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
|
#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )
|
||||||
#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 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
|
// UID IO threshold in bytes
|
||||||
#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
|
#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_unit;
|
||||||
int periodic_chores_interval_disk_stats_publish;
|
int periodic_chores_interval_disk_stats_publish;
|
||||||
int periodic_chores_interval_uid_io;
|
int periodic_chores_interval_uid_io;
|
||||||
bool proc_uid_io_available; // whether uid_io is accessible
|
int periodic_chores_interval_flush_proto;
|
||||||
bool diskstats_available; // whether diskstats is accessible
|
|
||||||
int event_time_check_usec; // check how much cputime spent in event loop
|
int event_time_check_usec; // check how much cputime spent in event loop
|
||||||
};
|
};
|
||||||
|
|
||||||
class storaged_t : public BnBatteryPropertiesListener,
|
class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
|
||||||
public IBinder::DeathRecipient {
|
public android::hardware::hidl_death_recipient {
|
||||||
private:
|
private:
|
||||||
time_t mTimer;
|
time_t mTimer;
|
||||||
storaged_config mConfig;
|
storaged_config mConfig;
|
||||||
disk_stats_publisher mDiskStats;
|
unique_ptr<disk_stats_monitor> mDsm;
|
||||||
disk_stats_monitor mDsm;
|
|
||||||
uid_monitor mUidm;
|
uid_monitor mUidm;
|
||||||
time_t mStarttime;
|
time_t mStarttime;
|
||||||
sp<IBatteryPropertiesRegistrar> battery_properties;
|
sp<android::hardware::health::V2_0::IHealth> health;
|
||||||
std::unique_ptr<storage_info_t> storage_info;
|
unique_ptr<storage_info_t> storage_info;
|
||||||
public:
|
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);
|
||||||
~storaged_t() {}
|
void init(void);
|
||||||
void event(void);
|
void event(void);
|
||||||
void event_checked(void);
|
void event_checked(void);
|
||||||
void pause(void) {
|
void pause(void) {
|
||||||
|
|
@ -270,24 +110,37 @@ public:
|
||||||
return mStarttime;
|
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();
|
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) {
|
double hours, uint64_t threshold, bool force_report) {
|
||||||
return mUidm.dump(hours, threshold, force_report);
|
return mUidm.dump(hours, threshold, force_report);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_uid_io_interval(int interval) {
|
void update_uid_io_interval(int interval) {
|
||||||
if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
|
if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {
|
||||||
mConfig.periodic_chores_interval_uid_io = interval;
|
mConfig.periodic_chores_interval_uid_io = interval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_battery_service();
|
void add_user_ce(userid_t user_id);
|
||||||
virtual void batteryPropertiesChanged(struct BatteryProperties props);
|
void remove_user_ce(userid_t user_id);
|
||||||
void binderDied(const wp<IBinder>& who);
|
|
||||||
|
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 report_storage_info();
|
||||||
|
|
||||||
|
void flush_protos(unordered_map<int, StoragedProto>* protos);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Eventlog tag
|
// Eventlog tag
|
||||||
|
|
|
||||||
199
storaged/include/storaged_diskstats.h
Normal file
199
storaged/include/storaged_diskstats.h
Normal 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_ */
|
||||||
|
|
@ -19,14 +19,26 @@
|
||||||
|
|
||||||
#include <string.h>
|
#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) \
|
#define FRIEND_TEST(test_case_name, test_name) \
|
||||||
friend class test_case_name##_##test_name##_Test
|
friend class test_case_name##_##test_name##_Test
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace android;
|
||||||
|
using namespace chrono;
|
||||||
|
using namespace storaged_proto;
|
||||||
|
|
||||||
class storage_info_t {
|
class storage_info_t {
|
||||||
protected:
|
protected:
|
||||||
FRIEND_TEST(storaged_test, storage_info_t);
|
FRIEND_TEST(storaged_test, storage_info_t);
|
||||||
|
FRIEND_TEST(storaged_test, storage_info_t_proto);
|
||||||
// emmc lifetime
|
// emmc lifetime
|
||||||
uint16_t eol; // pre-eol (end of life) information
|
uint16_t eol; // pre-eol (end of life) information
|
||||||
uint16_t lifetime_a; // device life time estimation (type A)
|
uint16_t lifetime_a; // device life time estimation (type A)
|
||||||
|
|
@ -36,16 +48,38 @@ protected:
|
||||||
const string userdata_path = "/data";
|
const string userdata_path = "/data";
|
||||||
uint64_t userdata_total_kb;
|
uint64_t userdata_total_kb;
|
||||||
uint64_t userdata_free_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),
|
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();
|
void publish();
|
||||||
storage_info_t* s_info;
|
storage_info_t* s_info;
|
||||||
public:
|
|
||||||
static storage_info_t* get_storage_info();
|
public:
|
||||||
virtual ~storage_info_t() {}
|
static storage_info_t* get_storage_info(
|
||||||
|
const sp<android::hardware::health::V2_0::IHealth>& healthService);
|
||||||
|
virtual ~storage_info_t() {};
|
||||||
virtual void report() {};
|
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 {
|
class emmc_info_t : public storage_info_t {
|
||||||
|
|
@ -69,4 +103,18 @@ public:
|
||||||
virtual void report();
|
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_ */
|
#endif /* _STORAGED_INFO_H_ */
|
||||||
|
|
|
||||||
|
|
@ -19,42 +19,38 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <binder/IInterface.h>
|
#include <binder/BinderService.h>
|
||||||
#include <binder/IBinder.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 StoragedService : public BinderService<StoragedService>, public BnStoraged {
|
||||||
class IStoraged : public IInterface {
|
private:
|
||||||
|
void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
|
||||||
|
void dumpUidRecords(int fd, const vector<struct uid_record>& entries);
|
||||||
public:
|
public:
|
||||||
enum {
|
static status_t start();
|
||||||
DUMPUIDS = IBinder::FIRST_CALL_TRANSACTION,
|
static char const* getServiceName() { return "storaged"; }
|
||||||
};
|
virtual status_t dump(int fd, const Vector<String16> &args) override;
|
||||||
// Request the service to run the test function
|
|
||||||
virtual std::vector<struct uid_info> dump_uids(const char* option) = 0;
|
|
||||||
|
|
||||||
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 StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
|
||||||
class BpStoraged : public BpInterface<IStoraged> {
|
|
||||||
public:
|
public:
|
||||||
BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
|
static status_t start();
|
||||||
virtual std::vector<struct uid_info> dump_uids(const char* option);
|
static char const* getServiceName() { return "storaged_pri"; }
|
||||||
|
|
||||||
|
binder::Status dumpUids(vector<UidInfo>* _aidl_return);
|
||||||
|
binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Server
|
sp<IStoragedPrivate> get_storaged_pri_service();
|
||||||
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();
|
|
||||||
|
|
||||||
#endif /* _STORAGED_SERVICE_H_ */
|
#endif /* _STORAGED_SERVICE_H_ */
|
||||||
|
|
@ -23,88 +23,103 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
enum uid_stat_t {
|
#include <cutils/multiuser.h>
|
||||||
FOREGROUND = 0,
|
#include <utils/Mutex.h>
|
||||||
BACKGROUND = 1,
|
|
||||||
UID_STATS = 2
|
#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 {
|
class io_usage {
|
||||||
CHARGER_OFF = 0,
|
public:
|
||||||
CHARGER_ON = 1,
|
io_usage() : bytes{{{0}}} {};
|
||||||
CHARGER_STATS = 2
|
uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];
|
||||||
};
|
bool is_zero() const;
|
||||||
|
io_usage& operator+= (const io_usage& stats) {
|
||||||
enum io_type_t {
|
for (int i = 0; i < IO_TYPES; i++) {
|
||||||
READ = 0,
|
for (int j = 0; j < UID_STATS; j++) {
|
||||||
WRITE = 1,
|
for (int k = 0; k < CHARGER_STATS; k++) {
|
||||||
IO_TYPES = 2
|
bytes[i][j][k] += stats.bytes[i][j][k];
|
||||||
};
|
}
|
||||||
|
}
|
||||||
struct uid_io_stats {
|
}
|
||||||
uint64_t rchar; // characters read
|
return *this;
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uid_io_usage {
|
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 {
|
struct uid_record {
|
||||||
std::string name;
|
string name;
|
||||||
struct uid_io_usage ios;
|
struct uid_io_usage ios;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uid_records {
|
struct uid_records {
|
||||||
uint64_t start_ts;
|
uint64_t start_ts;
|
||||||
std::vector<struct uid_record> entries;
|
vector<struct uid_record> entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
class uid_monitor {
|
class uid_monitor {
|
||||||
private:
|
private:
|
||||||
|
FRIEND_TEST(storaged_test, uid_monitor);
|
||||||
// last dump from /proc/uid_io/stats, uid -> uid_info
|
// 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
|
// 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}
|
// 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 ON/OFF
|
||||||
charger_stat_t charger_stat;
|
charger_stat_t charger_stat;
|
||||||
// protects curr_io_stats, last_uid_io_stats, records and 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
|
// start time for IO records
|
||||||
uint64_t start_ts;
|
uint64_t start_ts;
|
||||||
|
// true if UID_IO_STATS_PATH is accessible
|
||||||
|
const bool enable;
|
||||||
|
|
||||||
// reads from /proc/uid_io/stats
|
// 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
|
// flushes curr_io_stats to records
|
||||||
void add_records_locked(uint64_t curr_ts);
|
void add_records_locked(uint64_t curr_ts);
|
||||||
// updates curr_io_stats and set last_uid_io_stats
|
// updates curr_io_stats and set last_uid_io_stats
|
||||||
void update_curr_io_stats_locked();
|
void update_curr_io_stats_locked();
|
||||||
|
// writes io_history to protobuf
|
||||||
|
void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uid_monitor();
|
uid_monitor();
|
||||||
~uid_monitor();
|
|
||||||
// called by storaged main thread
|
// called by storaged main thread
|
||||||
void init(charger_stat_t stat);
|
void init(charger_stat_t stat);
|
||||||
// called by storaged -u
|
// 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
|
// 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);
|
double hours, uint64_t threshold, bool force_report);
|
||||||
// called by battery properties listener
|
// called by battery properties listener
|
||||||
void set_charger_state(charger_stat_t stat);
|
void set_charger_state(charger_stat_t stat);
|
||||||
// called by storaged periodic_chore or dump with force_report
|
// 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_ */
|
#endif /* _STORAGED_UID_MONITOR_H_ */
|
||||||
|
|
|
||||||
|
|
@ -24,21 +24,20 @@
|
||||||
|
|
||||||
#include "storaged.h"
|
#include "storaged.h"
|
||||||
|
|
||||||
|
using namespace android::os::storaged;
|
||||||
|
|
||||||
// Diskstats
|
// Diskstats
|
||||||
bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
|
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_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);
|
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
|
// 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
|
// 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_ */
|
#endif /* _STORAGED_UTILS_H_ */
|
||||||
|
|
|
||||||
77
storaged/include/uid_info.h
Normal file
77
storaged/include/uid_info.h
Normal 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_ */
|
||||||
|
|
@ -39,72 +39,81 @@
|
||||||
#include <storaged_service.h>
|
#include <storaged_service.h>
|
||||||
#include <storaged_utils.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
|
// Function of storaged's main thread
|
||||||
void* storaged_main(void* /* unused */) {
|
void* storaged_main(void* /* unused */) {
|
||||||
storaged = new storaged_t();
|
storaged_sp = new storaged_t();
|
||||||
|
|
||||||
storaged->init_battery_service();
|
storaged_sp->init();
|
||||||
storaged->report_storage_info();
|
storaged_sp->report_storage_info();
|
||||||
|
|
||||||
LOG_TO(SYSTEM, INFO) << "storaged: Start";
|
LOG_TO(SYSTEM, INFO) << "storaged: Start";
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
storaged->event_checked();
|
storaged_sp->event_checked();
|
||||||
storaged->pause();
|
storaged_sp->pause();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help_message(void) {
|
void help_message(void) {
|
||||||
printf("usage: storaged [OPTION]\n");
|
printf("usage: storaged [OPTION]\n");
|
||||||
printf(" -u --uid Dump uid I/O usage to stdout\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");
|
printf(" -s --start Start storaged (default)\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
int flag_main_service = 0;
|
bool flag_main_service = false;
|
||||||
int flag_dump_uid = 0;
|
bool flag_dump_uid = false;
|
||||||
|
bool flag_dump_task = false;
|
||||||
|
bool flag_dump_perf = false;
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int opt_idx = 0;
|
int opt_idx = 0;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"start", no_argument, 0, 's'},
|
{"perf", no_argument, nullptr, 'p'},
|
||||||
{"kill", no_argument, 0, 'k'},
|
{"start", no_argument, nullptr, 's'},
|
||||||
{"uid", no_argument, 0, 'u'},
|
{"task", no_argument, nullptr, 't'},
|
||||||
{"help", no_argument, 0, 'h'}
|
{"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) {
|
if (opt == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
flag_dump_perf = true;
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
flag_main_service = 1;
|
flag_main_service = true;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
flag_dump_task = true;
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
flag_dump_uid = 1;
|
flag_dump_uid = true;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
default:
|
||||||
help_message();
|
help_message();
|
||||||
return 0;
|
return 0;
|
||||||
case '?':
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "no supported option\n");
|
|
||||||
help_message();
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 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");
|
fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
|
||||||
help_message();
|
help_message();
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -119,7 +128,12 @@ int main(int argc, char** argv) {
|
||||||
return -1;
|
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();
|
android::ProcessState::self()->startThreadPool();
|
||||||
IPCThreadState::self()->joinThreadPool();
|
IPCThreadState::self()->joinThreadPool();
|
||||||
pthread_join(storaged_main_thread, NULL);
|
pthread_join(storaged_main_thread, NULL);
|
||||||
|
|
@ -127,23 +141,33 @@ int main(int argc, char** argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flag_dump_uid) {
|
sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();
|
||||||
sp<IStoraged> storaged_service = get_storaged_service();
|
if (storaged_service == NULL) {
|
||||||
if (storaged_service == NULL) {
|
fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
|
||||||
fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
|
||||||
std::vector<struct uid_info> res = storaged_service->dump_uids(NULL);
|
|
||||||
|
|
||||||
if (res.size() == 0) {
|
if (flag_dump_uid || flag_dump_task) {
|
||||||
fprintf(stderr, "UID I/O is not readable in this version of kernel.\n");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_running_uids_info(res);
|
sort_running_uids_info(uid_io);
|
||||||
log_console_running_uids_info(res);
|
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;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -16,184 +16,118 @@
|
||||||
|
|
||||||
#define LOG_TAG "storaged"
|
#define LOG_TAG "storaged"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.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/logging.h>
|
||||||
|
#include <android-base/unique_fd.h>
|
||||||
|
#include <android/hidl/manager/1.0/IServiceManager.h>
|
||||||
#include <batteryservice/BatteryServiceConstants.h>
|
#include <batteryservice/BatteryServiceConstants.h>
|
||||||
#include <batteryservice/IBatteryPropertiesRegistrar.h>
|
|
||||||
#include <binder/IPCThreadState.h>
|
|
||||||
#include <binder/IServiceManager.h>
|
|
||||||
#include <cutils/properties.h>
|
#include <cutils/properties.h>
|
||||||
|
#include <healthhalutils/HealthHalUtils.h>
|
||||||
|
#include <hidl/HidlTransportSupport.h>
|
||||||
|
#include <hwbinder/IPCThreadState.h>
|
||||||
#include <log/log.h>
|
#include <log/log.h>
|
||||||
|
|
||||||
#include <storaged.h>
|
#include <storaged.h>
|
||||||
#include <storaged_utils.h>
|
#include <storaged_utils.h>
|
||||||
|
|
||||||
/* disk_stats_publisher */
|
using namespace android::base;
|
||||||
void disk_stats_publisher::publish(void) {
|
using namespace chrono;
|
||||||
// Logging
|
using namespace google::protobuf::io;
|
||||||
struct disk_perf perf = get_disk_perf(&mAccumulate);
|
using namespace storaged_proto;
|
||||||
log_debug_disk_perf(&perf, "regular");
|
|
||||||
log_event_disk_stats(&mAccumulate, "regular");
|
|
||||||
// Reset global structures
|
|
||||||
memset(&mAccumulate, 0, sizeof(struct disk_stats));
|
|
||||||
}
|
|
||||||
|
|
||||||
void disk_stats_publisher::update(void) {
|
namespace {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* disk_stats_monitor */
|
/*
|
||||||
void disk_stats_monitor::update_mean() {
|
* The system user is the initial user that is implicitly created on first boot
|
||||||
CHECK(mValid);
|
* and hosts most of the system services. Keep this in sync with
|
||||||
mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
|
* frameworks/base/core/java/android/os/UserManager.java
|
||||||
mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
|
*/
|
||||||
mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
|
constexpr int USER_SYSTEM = 0;
|
||||||
mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
|
|
||||||
mMean.queue = (uint32_t)mStats.queue.get_mean();
|
|
||||||
}
|
|
||||||
|
|
||||||
void disk_stats_monitor::update_std() {
|
constexpr ssize_t benchmark_unit_size = 16 * 1024; // 16KB
|
||||||
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) {
|
constexpr ssize_t min_benchmark_size = 128 * 1024; // 128KB
|
||||||
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) {
|
} // namespace
|
||||||
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) {
|
const uint32_t storaged_t::current_version = 4;
|
||||||
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* stats) {
|
using android::hardware::interfacesEqual;
|
||||||
struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
|
using android::hardware::Return;
|
||||||
struct disk_perf perf = get_disk_perf(&inc);
|
using android::hardware::health::V1_0::BatteryStatus;
|
||||||
// Update internal data structures
|
using android::hardware::health::V1_0::toString;
|
||||||
if (LIKELY(mValid)) {
|
using android::hardware::health::V2_0::get_health_service;
|
||||||
CHECK_EQ(mBuffer.size(), mWindow);
|
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());
|
inline charger_stat_t is_charger_on(BatteryStatus prop) {
|
||||||
mBuffer.pop();
|
return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
|
||||||
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) ?
|
|
||||||
CHARGER_ON : CHARGER_OFF;
|
CHARGER_ON : CHARGER_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void storaged_t::batteryPropertiesChanged(struct BatteryProperties props) {
|
Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
|
||||||
mUidm.set_charger_state(is_charger_on(props.batteryStatus));
|
mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
|
||||||
|
return android::hardware::Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
void storaged_t::init_battery_service() {
|
void storaged_t::init() {
|
||||||
if (!mConfig.proc_uid_io_available)
|
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;
|
return;
|
||||||
|
|
||||||
battery_properties = get_battery_properties_service();
|
health = get_health_service();
|
||||||
if (battery_properties == NULL) {
|
if (health == NULL) {
|
||||||
LOG_TO(SYSTEM, WARNING) << "failed to find batteryproperties service";
|
LOG_TO(SYSTEM, WARNING) << "health: failed to find IHealth service";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BatteryProperty val;
|
BatteryStatus status = BatteryStatus::UNKNOWN;
|
||||||
battery_properties->getProperty(BATTERY_PROP_BATTERY_STATUS, &val);
|
auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
|
||||||
mUidm.init(is_charger_on(val.valueInt64));
|
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
|
// register listener after init uid_monitor
|
||||||
battery_properties->registerListener(this);
|
health->registerCallback(this);
|
||||||
IInterface::asBinder(battery_properties)->linkToDeath(this);
|
health->linkToDeath(this, 0 /* cookie */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void storaged_t::binderDied(const wp<IBinder>& who) {
|
void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
|
||||||
if (battery_properties != NULL &&
|
if (health != NULL && interfacesEqual(health, who.promote())) {
|
||||||
IInterface::asBinder(battery_properties) == who) {
|
LOG_TO(SYSTEM, ERROR) << "health service died, exiting";
|
||||||
LOG_TO(SYSTEM, ERROR) << "batteryproperties service died, exiting";
|
android::hardware::IPCThreadState::self()->stopProcess();
|
||||||
IPCThreadState::self()->stopProcess();
|
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
LOG_TO(SYSTEM, ERROR) << "unknown service died";
|
LOG_TO(SYSTEM, ERROR) << "unknown service died";
|
||||||
|
|
@ -206,44 +140,195 @@ void storaged_t::report_storage_info() {
|
||||||
|
|
||||||
/* storaged_t */
|
/* storaged_t */
|
||||||
storaged_t::storaged_t(void) {
|
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 =
|
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 =
|
mConfig.event_time_check_usec =
|
||||||
property_get_int32("ro.storaged.event.perf_check", 0);
|
property_get_int32("ro.storaged.event.perf_check", 0);
|
||||||
|
|
||||||
mConfig.periodic_chores_interval_disk_stats_publish =
|
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 =
|
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);
|
mStarttime = time(NULL);
|
||||||
|
mTimer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void storaged_t::event(void) {
|
void storaged_t::add_user_ce(userid_t user_id) {
|
||||||
if (mConfig.diskstats_available) {
|
load_proto(user_id);
|
||||||
mDiskStats.update();
|
proto_loaded[user_id] = true;
|
||||||
mDsm.update();
|
}
|
||||||
storage_info->refresh();
|
|
||||||
if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
|
void storaged_t::remove_user_ce(userid_t user_id) {
|
||||||
mDiskStats.publish();
|
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 &&
|
char* data = nullptr;
|
||||||
(mTimer % mConfig.periodic_chores_interval_uid_io) == 0) {
|
if (posix_memalign(reinterpret_cast<void**>(&data),
|
||||||
mUidm.report();
|
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;
|
mTimer += mConfig.periodic_chores_interval_unit;
|
||||||
|
|
|
||||||
60
storaged/storaged.proto
Normal file
60
storaged/storaged.proto
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -5,4 +5,4 @@ service storaged /system/bin/storaged
|
||||||
file /d/mmc0/mmc0:0001/ext_csd r
|
file /d/mmc0/mmc0:0001/ext_csd r
|
||||||
writepid /dev/cpuset/system-background/tasks
|
writepid /dev/cpuset/system-background/tasks
|
||||||
user root
|
user root
|
||||||
group package_info
|
group package_info
|
||||||
|
|
|
||||||
326
storaged/storaged_diskstats.cpp
Normal file
326
storaged/storaged_diskstats.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/parseint.h>
|
#include <android-base/parseint.h>
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
|
|
@ -27,9 +29,16 @@
|
||||||
#include <log/log_event_list.h>
|
#include <log/log_event_list.h>
|
||||||
|
|
||||||
#include "storaged.h"
|
#include "storaged.h"
|
||||||
|
#include "storaged_info.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace chrono;
|
||||||
using namespace android::base;
|
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_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
|
||||||
const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
|
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";
|
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;
|
struct stat buffer;
|
||||||
return stat(filename.c_str(), &buffer) == 0;
|
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) ||
|
if (FileExists(emmc_info_t::emmc_sysfs) ||
|
||||||
FileExists(emmc_info_t::emmc_debugfs)) {
|
FileExists(emmc_info_t::emmc_debugfs)) {
|
||||||
return new emmc_info_t;
|
return new emmc_info_t;
|
||||||
|
|
@ -57,7 +72,39 @@ storage_info_t* storage_info_t::get_storage_info()
|
||||||
return new storage_info_t;
|
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;
|
struct statvfs buf;
|
||||||
if (statvfs(userdata_path.c_str(), &buf) != 0) {
|
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_total_kb = buf.f_bsize * buf.f_blocks >> 10;
|
||||||
userdata_free_kb = buf.f_bfree * 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()
|
void storage_info_t::publish()
|
||||||
|
|
@ -76,6 +141,95 @@ void storage_info_t::publish()
|
||||||
<< LOG_ID_EVENTS;
|
<< 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()
|
void emmc_info_t::report()
|
||||||
{
|
{
|
||||||
if (!report_sysfs() && !report_debugfs())
|
if (!report_sysfs() && !report_debugfs())
|
||||||
|
|
@ -121,6 +275,8 @@ bool emmc_info_t::report_sysfs()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
|
const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
|
||||||
/* 2 characters in string for each byte */
|
/* 2 characters in string for each byte */
|
||||||
const size_t EXT_CSD_REV_IDX = 192 * 2;
|
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_A_IDX = 268 * 2;
|
||||||
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
|
const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
bool emmc_info_t::report_debugfs()
|
bool emmc_info_t::report_debugfs()
|
||||||
{
|
{
|
||||||
string buffer;
|
string buffer;
|
||||||
|
|
@ -210,3 +368,25 @@ void ufs_info_t::report()
|
||||||
publish();
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -29,60 +30,69 @@
|
||||||
#include <private/android_filesystem_config.h>
|
#include <private/android_filesystem_config.h>
|
||||||
|
|
||||||
#include <storaged.h>
|
#include <storaged.h>
|
||||||
|
#include <storaged_utils.h>
|
||||||
#include <storaged_service.h>
|
#include <storaged_service.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
using namespace android::base;
|
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*/) {
|
status_t StoragedService::start() {
|
||||||
Parcel data, reply;
|
return BinderService<StoragedService>::publish();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
|
|
||||||
|
|
||||||
status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
|
void StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {
|
||||||
switch(code) {
|
map<string, io_usage> merged_entries = merge_io_usage(entries);
|
||||||
case DUMPUIDS: {
|
for (const auto& rec : merged_entries) {
|
||||||
if (!data.checkInterface(this))
|
dprintf(fd, "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
|
||||||
return BAD_TYPE;
|
" %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
|
||||||
std::vector<struct uid_info> res = dump_uids(NULL);
|
rec.first.c_str(),
|
||||||
reply->writeInt32(res.size());
|
rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],
|
||||||
for (auto uid : res) {
|
rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],
|
||||||
reply->writeInt32(uid.uid);
|
rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],
|
||||||
reply->writeCString(uid.name.c_str());
|
rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],
|
||||||
reply->write(&uid.io, sizeof(uid.io));
|
rec.second.bytes[READ][FOREGROUND][CHARGER_ON],
|
||||||
}
|
rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],
|
||||||
return NO_ERROR;
|
rec.second.bytes[READ][BACKGROUND][CHARGER_ON],
|
||||||
}
|
rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return BBinder::onTransact(code, data, reply, flags);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<struct uid_info> Storaged::dump_uids(const char* /* option */) {
|
void StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {
|
||||||
std::vector<struct uid_info> uids_v;
|
for (const auto& record : entries) {
|
||||||
std::unordered_map<uint32_t, struct uid_info> uids_m = storaged->get_uids();
|
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) {
|
for (const auto& task_it : record.ios.task_ios) {
|
||||||
uids_v.push_back(it.second);
|
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();
|
IPCThreadState* self = IPCThreadState::self();
|
||||||
const int pid = self->getCallingPid();
|
const int pid = self->getCallingPid();
|
||||||
const int uid = self->getCallingUid();
|
const int uid = self->getCallingUid();
|
||||||
|
|
@ -96,6 +106,7 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) {
|
||||||
int time_window = 0;
|
int time_window = 0;
|
||||||
uint64_t threshold = 0;
|
uint64_t threshold = 0;
|
||||||
bool force_report = false;
|
bool force_report = false;
|
||||||
|
bool debug = false;
|
||||||
for (size_t i = 0; i < args.size(); i++) {
|
for (size_t i = 0; i < args.size(); i++) {
|
||||||
const auto& arg = args[i];
|
const auto& arg = args[i];
|
||||||
if (arg == String16("--hours")) {
|
if (arg == String16("--hours")) {
|
||||||
|
|
@ -123,47 +134,87 @@ status_t Storaged::dump(int fd, const Vector<String16>& args) {
|
||||||
force_report = true;
|
force_report = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == String16("--debug")) {
|
||||||
|
debug = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t last_ts = 0;
|
uint64_t last_ts = 0;
|
||||||
const std::map<uint64_t, struct uid_records>& records =
|
map<uint64_t, struct uid_records> records =
|
||||||
storaged->get_uid_records(hours, threshold, force_report);
|
storaged_sp->get_uid_records(hours, threshold, force_report);
|
||||||
for (const auto& it : records) {
|
for (const auto& it : records) {
|
||||||
if (last_ts != it.second.start_ts) {
|
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;
|
last_ts = it.first;
|
||||||
|
|
||||||
for (const auto& record : it.second.entries) {
|
if (!debug) {
|
||||||
dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
|
dumpUidRecords(fd, it.second.entries);
|
||||||
record.name.c_str(),
|
} else {
|
||||||
record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
|
dumpUidRecordsDebug(fd, it.second.entries);
|
||||||
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 (time_window) {
|
if (time_window) {
|
||||||
storaged->update_uid_io_interval(time_window);
|
storaged_sp->update_uid_io_interval(time_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO_ERROR;
|
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();
|
sp<IServiceManager> sm = defaultServiceManager();
|
||||||
if (sm == NULL) return NULL;
|
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;
|
if (binder == NULL) return NULL;
|
||||||
|
|
||||||
sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
|
return interface_cast<IStoragedPrivate>(binder);
|
||||||
|
|
||||||
return storaged;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,87 @@
|
||||||
using namespace android;
|
using namespace android;
|
||||||
using namespace android::base;
|
using namespace android::base;
|
||||||
using namespace android::content::pm;
|
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();
|
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();
|
sp<IServiceManager> sm = defaultServiceManager();
|
||||||
if (sm == NULL) {
|
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;
|
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;
|
std::string buffer;
|
||||||
if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
|
if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {
|
||||||
PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
|
PLOG_TO(SYSTEM, ERROR) << UID_IO_STATS_PATH << ": ReadFileToString failed";
|
||||||
return uid_io_stats;
|
return uid_io_stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> io_stats = Split(buffer, "\n");
|
std::vector<std::string> io_stats = Split(std::move(buffer), "\n");
|
||||||
struct uid_info u;
|
uid_info u;
|
||||||
vector<int> uids;
|
vector<int> uids;
|
||||||
vector<std::string*> uid_names;
|
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()) {
|
if (io_stats[i].empty()) {
|
||||||
continue;
|
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;
|
if (io_stats[i].compare(0, 4, "task")) {
|
||||||
uid_io_stats[u.uid].name = std::to_string(u.uid);
|
if (!u.parse_uid_io_stats(std::move(io_stats[i])))
|
||||||
uids.push_back(u.uid);
|
continue;
|
||||||
uid_names.push_back(&uid_io_stats[u.uid].name);
|
uid_io_stats[u.uid] = u;
|
||||||
if (last_uid_io_stats.find(u.uid) == last_uid_io_stats.end()) {
|
uid_io_stats[u.uid].name = std::to_string(u.uid);
|
||||||
refresh_uid_names = true;
|
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 {
|
} 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;
|
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 int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
|
||||||
const std::map<uint64_t, struct uid_records>& curr_records)
|
|
||||||
|
inline size_t history_size(
|
||||||
|
const std::map<uint64_t, struct uid_records>& history)
|
||||||
{
|
{
|
||||||
int count = 0;
|
size_t count = 0;
|
||||||
for (auto const& it : curr_records) {
|
for (auto const& it : history) {
|
||||||
count += it.second.entries.size();
|
count += it.second.entries.size();
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct uid_io_usage zero_io_usage;
|
} // namespace
|
||||||
|
|
||||||
void uid_monitor::add_records_locked(uint64_t curr_ts)
|
void uid_monitor::add_records_locked(uint64_t curr_ts)
|
||||||
{
|
{
|
||||||
// remove records more than 5 days old
|
// remove records more than 5 days old
|
||||||
if (curr_ts > 5 * DAY_TO_SEC) {
|
if (curr_ts > 5 * DAY_TO_SEC) {
|
||||||
auto it = records.lower_bound(curr_ts - 5 * DAY_TO_SEC);
|
auto it = io_history.lower_bound(curr_ts - 5 * DAY_TO_SEC);
|
||||||
records.erase(records.begin(), it);
|
io_history.erase(io_history.begin(), it);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct uid_records new_records;
|
struct uid_records new_records;
|
||||||
for (const auto& p : curr_io_stats) {
|
for (const auto& p : curr_io_stats) {
|
||||||
struct uid_record record = {};
|
struct uid_record record = {};
|
||||||
record.name = p.first;
|
record.name = p.first;
|
||||||
record.ios = p.second;
|
if (!p.second.uid_ios.is_zero()) {
|
||||||
if (memcmp(&record.ios, &zero_io_usage, sizeof(struct uid_io_usage))) {
|
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);
|
new_records.entries.push_back(record);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,25 +245,25 @@ void uid_monitor::add_records_locked(uint64_t curr_ts)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// make some room for new records
|
// 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;
|
new_records.entries.size() - MAX_UID_RECORDS_SIZE;
|
||||||
while (overflow > 0 && records.size() > 0) {
|
while (overflow > 0 && io_history.size() > 0) {
|
||||||
auto del_it = records.begin();
|
auto del_it = io_history.begin();
|
||||||
overflow -= del_it->second.entries.size();
|
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(
|
std::map<uint64_t, struct uid_records> uid_monitor::dump(
|
||||||
double hours, uint64_t threshold, bool force_report)
|
double hours, uint64_t threshold, bool force_report)
|
||||||
{
|
{
|
||||||
if (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;
|
std::map<uint64_t, struct uid_records> dump_records;
|
||||||
uint64_t first_ts = 0;
|
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;
|
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;
|
const std::vector<struct uid_record>& recs = it->second.entries;
|
||||||
struct uid_records filtered;
|
struct uid_records filtered;
|
||||||
|
|
||||||
for (const auto& rec : recs) {
|
for (const auto& rec : recs) {
|
||||||
if (rec.ios.bytes[READ][FOREGROUND][CHARGER_ON] +
|
const io_usage& uid_usage = rec.ios.uid_ios;
|
||||||
rec.ios.bytes[READ][FOREGROUND][CHARGER_OFF] +
|
if (uid_usage.bytes[READ][FOREGROUND][CHARGER_ON] +
|
||||||
rec.ios.bytes[READ][BACKGROUND][CHARGER_ON] +
|
uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF] +
|
||||||
rec.ios.bytes[READ][BACKGROUND][CHARGER_OFF] +
|
uid_usage.bytes[READ][BACKGROUND][CHARGER_ON] +
|
||||||
rec.ios.bytes[WRITE][FOREGROUND][CHARGER_ON] +
|
uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF] +
|
||||||
rec.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
|
uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON] +
|
||||||
rec.ios.bytes[WRITE][BACKGROUND][CHARGER_ON] +
|
uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF] +
|
||||||
rec.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
|
uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON] +
|
||||||
|
uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {
|
||||||
filtered.entries.push_back(rec);
|
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()
|
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();
|
get_uid_io_stats_locked();
|
||||||
if (uid_io_stats.empty()) {
|
if (uid_io_stats.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& it : uid_io_stats) {
|
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()) {
|
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];
|
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 -
|
int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -
|
||||||
last_uid_io_stats[uid.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 -
|
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 -
|
int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -
|
||||||
last_uid_io_stats[uid.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;
|
(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;
|
(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;
|
(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;
|
(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;
|
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();
|
update_curr_io_stats_locked();
|
||||||
add_records_locked(time(NULL));
|
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)
|
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) {
|
if (charger_stat == stat) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -289,16 +510,11 @@ void uid_monitor::set_charger_state(charger_stat_t stat)
|
||||||
void uid_monitor::init(charger_stat_t stat)
|
void uid_monitor::init(charger_stat_t stat)
|
||||||
{
|
{
|
||||||
charger_stat = stat;
|
charger_stat = stat;
|
||||||
|
|
||||||
start_ts = time(NULL);
|
start_ts = time(NULL);
|
||||||
last_uid_io_stats = get_uid_io_stats();
|
last_uid_io_stats = get_uid_io_stats();
|
||||||
}
|
}
|
||||||
|
|
||||||
uid_monitor::uid_monitor()
|
uid_monitor::uid_monitor()
|
||||||
{
|
: enable(!access(UID_IO_STATS_PATH, R_OK)) {
|
||||||
sem_init(&um_lock, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uid_monitor::~uid_monitor()
|
|
||||||
{
|
|
||||||
sem_destroy(&um_lock);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -41,124 +42,7 @@
|
||||||
#include <storaged.h>
|
#include <storaged.h>
|
||||||
#include <storaged_utils.h>
|
#include <storaged_utils.h>
|
||||||
|
|
||||||
bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
|
bool cmp_uid_info(const UidInfo& l, const UidInfo& r) {
|
||||||
// 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) {
|
|
||||||
// Compare background I/O first.
|
// Compare background I/O first.
|
||||||
for (int i = UID_STATS - 1; i >= 0; i--) {
|
for (int i = UID_STATS - 1; i >= 0; i--) {
|
||||||
uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;
|
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;
|
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);
|
std::sort(uids.begin(), uids.end(), cmp_uid_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging functions
|
// 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 "
|
printf("name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes "
|
||||||
"bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
|
"bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\n");
|
||||||
|
|
||||||
for (const auto& uid : uids) {
|
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[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[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,
|
||||||
uid.io[0].fsync, uid.io[1].fsync);
|
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);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
void log_console_perf_history(const vector<int>& perf_history) {
|
||||||
void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
|
if (perf_history.size() < 3 ||
|
||||||
// skip if the input structure are all zeros
|
perf_history.size() != perf_history[0] +
|
||||||
if (perf == NULL) return;
|
perf_history[1] +
|
||||||
struct disk_perf zero_cmp;
|
perf_history[2] + (size_t)3) {
|
||||||
memset(&zero_cmp, 0, sizeof(zero_cmp));
|
return;
|
||||||
if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
|
}
|
||||||
|
|
||||||
LOG_TO(SYSTEM, INFO) << "perf(ios) " << type
|
printf("\nI/O perf history (KB/s) : most_recent <--------- least_recent \n");
|
||||||
<< " 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
|
|
||||||
|
|
||||||
void log_event_disk_stats(struct disk_stats* stats, const char* type) {
|
std::stringstream line;
|
||||||
// skip if the input structure are all zeros
|
int start = 3;
|
||||||
if (stats == NULL) return;
|
int end = 3 + perf_history[0];
|
||||||
struct disk_stats zero_cmp;
|
std::copy(perf_history.begin() + start, perf_history.begin() + end,
|
||||||
memset(&zero_cmp, 0, sizeof(zero_cmp));
|
std::ostream_iterator<int>(line, " "));
|
||||||
// skip event logging diskstats when it is zero increment (all first 11 entries are zero)
|
printf("last 24 hours : %s\n", line.str().c_str());
|
||||||
if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
|
|
||||||
|
|
||||||
android_log_event_list(EVENTLOGTAG_DISKSTATS)
|
line.str("");
|
||||||
<< type << stats->start_time << stats->end_time
|
start = end;
|
||||||
<< stats->read_ios << stats->read_merges
|
end += perf_history[1];
|
||||||
<< stats->read_sectors << stats->read_ticks
|
std::copy(perf_history.begin() + start, perf_history.begin() + end,
|
||||||
<< stats->write_ios << stats->write_merges
|
std::ostream_iterator<int>(line, " "));
|
||||||
<< stats->write_sectors << stats->write_ticks
|
printf("last 7 days : %s\n", line.str().c_str());
|
||||||
<< (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue
|
|
||||||
<< LOG_ID_EVENTS;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
@ -24,13 +25,20 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <healthhalutils/HealthHalUtils.h>
|
||||||
#include <storaged.h> // data structures
|
#include <storaged.h> // data structures
|
||||||
#include <storaged_utils.h> // functions to test
|
#include <storaged_utils.h> // functions to test
|
||||||
|
|
||||||
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
|
#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
|
||||||
#define SDA_DISK_STATS_PATH "/sys/block/sda/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";
|
const char* path = "/cache/test";
|
||||||
int fd = open(path, O_WRONLY | O_CREAT, 0600);
|
int fd = open(path, O_WRONLY | O_CREAT, 0600);
|
||||||
ASSERT_LT(-1, fd);
|
ASSERT_LT(-1, fd);
|
||||||
|
|
@ -53,6 +61,8 @@ static void pause(uint32_t sec) {
|
||||||
sleep(sec);
|
sleep(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// the return values of the tested functions should be the expected ones
|
// the return values of the tested functions should be the expected ones
|
||||||
const char* DISK_STATS_PATH;
|
const char* DISK_STATS_PATH;
|
||||||
TEST(storaged_test, retvals) {
|
TEST(storaged_test, retvals) {
|
||||||
|
|
@ -77,13 +87,11 @@ TEST(storaged_test, retvals) {
|
||||||
EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
|
EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
|
||||||
|
|
||||||
// reading a wrong path should not damage the output structure
|
// 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) {
|
TEST(storaged_test, disk_stats) {
|
||||||
struct disk_stats stats;
|
struct disk_stats stats = {};
|
||||||
memset(&stats, 0, sizeof(struct disk_stats));
|
|
||||||
|
|
||||||
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
|
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
|
||||||
|
|
||||||
// every entry of stats (except io_in_flight) should all be greater than 0
|
// 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
|
// accumulation of the increments should be the same with the overall increment
|
||||||
struct disk_stats base, tmp, curr, acc, inc[5];
|
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));
|
|
||||||
|
|
||||||
for (uint i = 0; i < 5; ++i) {
|
for (uint i = 0; i < 5; ++i) {
|
||||||
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
|
ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
|
@ -106,22 +110,18 @@ TEST(storaged_test, disk_stats) {
|
||||||
sleep(5);
|
sleep(5);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
inc[i] = get_inc_disk_stats(&tmp, &curr);
|
get_inc_disk_stats(&tmp, &curr, &inc[i]);
|
||||||
add_disk_stats(&inc[i], &acc);
|
add_disk_stats(&inc[i], &acc);
|
||||||
tmp = curr;
|
tmp = curr;
|
||||||
pause(5);
|
write_and_pause(5);
|
||||||
}
|
}
|
||||||
struct disk_stats overall_inc;
|
struct disk_stats overall_inc = {};
|
||||||
memset(&overall_inc, 0, sizeof(disk_stats));
|
get_inc_disk_stats(&base, &curr, &overall_inc);
|
||||||
overall_inc= get_inc_disk_stats(&base, &curr);
|
|
||||||
|
|
||||||
for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
|
EXPECT_EQ(overall_inc, acc);
|
||||||
if (i == 8) continue; // skip io_in_flight which can be 0
|
|
||||||
EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static double mean(std::deque<uint32_t> nums) {
|
double mean(std::deque<uint32_t> nums) {
|
||||||
double sum = 0.0;
|
double sum = 0.0;
|
||||||
for (uint32_t i : nums) {
|
for (uint32_t i : nums) {
|
||||||
sum += i;
|
sum += i;
|
||||||
|
|
@ -129,7 +129,7 @@ static double mean(std::deque<uint32_t> nums) {
|
||||||
return sum / nums.size();
|
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 sum = 0.0;
|
||||||
double avg = mean(nums);
|
double avg = mean(nums);
|
||||||
for (uint32_t i : 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;
|
struct disk_perf retval;
|
||||||
retval.read_perf = (double)perf.read_perf * mul;
|
retval.read_perf = (double)perf.read_perf * mul;
|
||||||
retval.read_ios = (double)perf.read_ios * 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;
|
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;
|
struct disk_stats retval;
|
||||||
retval.read_ios = stats1.read_ios + stats2.read_ios;
|
retval.read_ios = stats1.read_ios + stats2.read_ios;
|
||||||
retval.read_merges = stats1.read_merges + stats2.read_merges;
|
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;
|
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) {
|
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
|
// 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
|
// 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
|
// feed monitor with constant perf data for io perf baseline
|
||||||
// using constant perf is reasonable since the functionality of stream_stats
|
// using constant perf is reasonable since the functionality of stream_stats
|
||||||
// has already been tested
|
// 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
|
// 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 = {
|
struct disk_stats norm_inc = {
|
||||||
.read_ios = 200,
|
.read_ios = 200,
|
||||||
.read_merges = 0,
|
.read_merges = 0,
|
||||||
|
|
@ -294,14 +325,12 @@ TEST(storaged_test, disk_stats_monitor) {
|
||||||
.io_avg = 0
|
.io_avg = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
struct disk_stats stats_base;
|
struct disk_stats stats_base = {};
|
||||||
memset(&stats_base, 0, sizeof(stats_base));
|
|
||||||
|
|
||||||
int loop_size = 100;
|
int loop_size = 100;
|
||||||
for (int i = 0; i < loop_size; ++i) {
|
for (int i = 0; i < loop_size; ++i) {
|
||||||
stats_base = disk_stats_add(stats_base, norm_inc);
|
stats_base = disk_stats_add(stats_base, norm_inc);
|
||||||
dsm_acc.update(&stats_base);
|
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);
|
EXPECT_FALSE(dsm_acc.mStall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,36 +345,284 @@ TEST(storaged_test, disk_stats_monitor) {
|
||||||
EXPECT_TRUE(dsm_acc.mValid);
|
EXPECT_TRUE(dsm_acc.mValid);
|
||||||
EXPECT_FALSE(dsm_acc.mStall);
|
EXPECT_FALSE(dsm_acc.mStall);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
|
struct disk_stats stats_prev = {};
|
||||||
EXPECT_LE(stats1.read_ios, stats2.read_ios);
|
loop_size = 10;
|
||||||
EXPECT_LE(stats1.read_merges, stats2.read_merges);
|
write_and_pause(5);
|
||||||
EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
|
for (int i = 0; i < loop_size; ++i) {
|
||||||
EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
|
dsm_detect.update();
|
||||||
|
expect_increasing(stats_prev, dsm_detect.mPrevious);
|
||||||
EXPECT_LE(stats1.write_ios, stats2.write_ios);
|
stats_prev = dsm_detect.mPrevious;
|
||||||
EXPECT_LE(stats1.write_merges, stats2.write_merges);
|
write_and_pause(5);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
181
storaged/tools/ranker.py
Normal 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
52
storaged/uid_info.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,6 @@ cc_binary {
|
||||||
"libcrypto",
|
"libcrypto",
|
||||||
"libcutils",
|
"libcutils",
|
||||||
"libkeymaster_portable",
|
"libkeymaster_portable",
|
||||||
"libkeymaster_staging",
|
|
||||||
"libtrusty",
|
"libtrusty",
|
||||||
"libkeymaster_messages",
|
"libkeymaster_messages",
|
||||||
"libsoftkeymasterdevice",
|
"libsoftkeymasterdevice",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue