storaged: use health HAL
Test: storaged_test Test: manual Bug: 208543110 Change-Id: I60d42fc4f2e411f375fe98d1c8e145ce79c52f88
This commit is contained in:
parent
e8474c2a10
commit
5291a67ee3
8 changed files with 168 additions and 107 deletions
|
|
@ -24,8 +24,10 @@ cc_defaults {
|
|||
shared_libs: [
|
||||
"android.hardware.health@1.0",
|
||||
"android.hardware.health@2.0",
|
||||
"android.hardware.health-V1-ndk",
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libbinder_ndk",
|
||||
"libcutils",
|
||||
"libhidlbase",
|
||||
"liblog",
|
||||
|
|
@ -35,6 +37,12 @@ cc_defaults {
|
|||
"packagemanager_aidl-cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"android.hardware.health-translate-ndk",
|
||||
"libhealthhalutils",
|
||||
"libhealthshim",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
|
|
@ -67,7 +75,6 @@ cc_library_static {
|
|||
":storaged_aidl_private",
|
||||
],
|
||||
|
||||
static_libs: ["libhealthhalutils"],
|
||||
header_libs: ["libbatteryservice_headers"],
|
||||
|
||||
logtags: ["EventLogTags.logtags"],
|
||||
|
|
@ -90,7 +97,6 @@ cc_binary {
|
|||
srcs: ["main.cpp"],
|
||||
|
||||
static_libs: [
|
||||
"libhealthhalutils",
|
||||
"libstoraged",
|
||||
],
|
||||
}
|
||||
|
|
@ -107,7 +113,6 @@ cc_test {
|
|||
srcs: ["tests/storaged_test.cpp"],
|
||||
|
||||
static_libs: [
|
||||
"libhealthhalutils",
|
||||
"libstoraged",
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <aidl/android/hardware/health/IHealth.h>
|
||||
#include <android/hardware/health/2.0/IHealth.h>
|
||||
|
||||
#define FRIEND_TEST(test_case_name, test_name) \
|
||||
|
|
@ -67,6 +68,8 @@ using namespace android;
|
|||
// UID IO threshold in bytes
|
||||
#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
|
||||
|
||||
class storaged_t;
|
||||
|
||||
struct storaged_config {
|
||||
int periodic_chores_interval_unit;
|
||||
int periodic_chores_interval_disk_stats_publish;
|
||||
|
|
@ -75,15 +78,33 @@ struct storaged_config {
|
|||
int event_time_check_usec; // check how much cputime spent in event loop
|
||||
};
|
||||
|
||||
class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
|
||||
public android::hardware::hidl_death_recipient {
|
||||
struct HealthServicePair {
|
||||
std::shared_ptr<aidl::android::hardware::health::IHealth> aidl_health;
|
||||
android::sp<android::hardware::health::V2_0::IHealth> hidl_health;
|
||||
static HealthServicePair get();
|
||||
};
|
||||
|
||||
class hidl_health_death_recipient : public android::hardware::hidl_death_recipient {
|
||||
public:
|
||||
hidl_health_death_recipient(const android::sp<android::hardware::health::V2_0::IHealth>& health)
|
||||
: mHealth(health) {}
|
||||
void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
|
||||
|
||||
private:
|
||||
android::sp<android::hardware::health::V2_0::IHealth> mHealth;
|
||||
};
|
||||
|
||||
class storaged_t : public RefBase {
|
||||
private:
|
||||
time_t mTimer;
|
||||
storaged_config mConfig;
|
||||
unique_ptr<disk_stats_monitor> mDsm;
|
||||
uid_monitor mUidm;
|
||||
time_t mStarttime;
|
||||
sp<android::hardware::health::V2_0::IHealth> health;
|
||||
std::shared_ptr<aidl::android::hardware::health::IHealth> health;
|
||||
sp<android::hardware::hidl_death_recipient> hidl_death_recp;
|
||||
ndk::ScopedAIBinder_DeathRecipient aidl_death_recp;
|
||||
shared_ptr<aidl::android::hardware::health::IHealthInfoCallback> aidl_health_callback;
|
||||
unique_ptr<storage_info_t> storage_info;
|
||||
static const uint32_t current_version;
|
||||
Mutex proto_lock;
|
||||
|
|
@ -135,10 +156,6 @@ class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
|
|||
void add_user_ce(userid_t user_id);
|
||||
void remove_user_ce(userid_t user_id);
|
||||
|
||||
virtual ::android::hardware::Return<void> healthInfoChanged(
|
||||
const ::android::hardware::health::V2_0::HealthInfo& info);
|
||||
void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
|
||||
|
||||
void report_storage_info();
|
||||
|
||||
void flush_protos(unordered_map<int, StoragedProto>* protos);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <android/hardware/health/2.0/IHealth.h>
|
||||
#include <aidl/android/hardware/health/IHealth.h>
|
||||
|
||||
// number of attributes diskstats has
|
||||
#define DISK_STATS_SIZE ( 11 )
|
||||
|
|
@ -162,7 +162,7 @@ private:
|
|||
const double mSigma;
|
||||
struct disk_perf mMean;
|
||||
struct disk_perf mStd;
|
||||
android::sp<android::hardware::health::V2_0::IHealth> mHealth;
|
||||
std::shared_ptr<aidl::android::hardware::health::IHealth> mHealth;
|
||||
|
||||
void update_mean();
|
||||
void update_std();
|
||||
|
|
@ -173,14 +173,15 @@ private:
|
|||
void update(struct disk_stats* stats);
|
||||
|
||||
public:
|
||||
disk_stats_monitor(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
|
||||
disk_stats_monitor(const std::shared_ptr<aidl::android::hardware::health::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))),
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
#include <android/hardware/health/2.0/IHealth.h>
|
||||
#include <aidl/android/hardware/health/IHealth.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include "storaged.h"
|
||||
|
|
@ -71,8 +71,8 @@ class storage_info_t {
|
|||
|
||||
public:
|
||||
static storage_info_t* get_storage_info(
|
||||
const sp<android::hardware::health::V2_0::IHealth>& healthService);
|
||||
virtual ~storage_info_t() {};
|
||||
const shared_ptr<aidl::android::hardware::health::IHealth>& healthService);
|
||||
virtual ~storage_info_t(){};
|
||||
virtual void report() {};
|
||||
void load_perf_history_proto(const IOPerfHistory& perf_history);
|
||||
void refresh(IOPerfHistory* perf_history);
|
||||
|
|
@ -105,14 +105,14 @@ public:
|
|||
|
||||
class health_storage_info_t : public storage_info_t {
|
||||
private:
|
||||
using IHealth = hardware::health::V2_0::IHealth;
|
||||
using StorageInfo = hardware::health::V2_0::StorageInfo;
|
||||
using IHealth = aidl::android::hardware::health::IHealth;
|
||||
using StorageInfo = aidl::android::hardware::health::StorageInfo;
|
||||
|
||||
sp<IHealth> mHealth;
|
||||
shared_ptr<IHealth> mHealth;
|
||||
void set_values_from_hal_storage_info(const StorageInfo& halInfo);
|
||||
|
||||
public:
|
||||
health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
|
||||
health_storage_info_t(const shared_ptr<IHealth>& service) : mHealth(service){};
|
||||
virtual ~health_storage_info_t() {}
|
||||
virtual void report();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,12 +28,16 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <aidl/android/hardware/health/BnHealthInfoCallback.h>
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <android/binder_ibinder.h>
|
||||
#include <android/binder_manager.h>
|
||||
#include <android/hidl/manager/1.0/IServiceManager.h>
|
||||
#include <batteryservice/BatteryServiceConstants.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <health-shim/shim.h>
|
||||
#include <healthhalutils/HealthHalUtils.h>
|
||||
#include <hidl/HidlTransportSupport.h>
|
||||
#include <hwbinder/IPCThreadState.h>
|
||||
|
|
@ -64,26 +68,59 @@ constexpr ssize_t min_benchmark_size = 128 * 1024; // 128KB
|
|||
|
||||
const uint32_t storaged_t::current_version = 4;
|
||||
|
||||
using aidl::android::hardware::health::BatteryStatus;
|
||||
using aidl::android::hardware::health::BnHealthInfoCallback;
|
||||
using aidl::android::hardware::health::HealthInfo;
|
||||
using aidl::android::hardware::health::IHealth;
|
||||
using aidl::android::hardware::health::IHealthInfoCallback;
|
||||
using android::hardware::interfacesEqual;
|
||||
using android::hardware::Return;
|
||||
using android::hardware::health::V1_0::BatteryStatus;
|
||||
using android::hardware::health::V1_0::toString;
|
||||
using android::hardware::health::V2_0::get_health_service;
|
||||
using android::hardware::health::V2_0::HealthInfo;
|
||||
using android::hardware::health::V2_0::IHealth;
|
||||
using android::hardware::health::V2_0::Result;
|
||||
using android::hidl::manager::V1_0::IServiceManager;
|
||||
using HidlHealth = android::hardware::health::V2_0::IHealth;
|
||||
using aidl::android::hardware::health::HealthShim;
|
||||
using ndk::ScopedAIBinder_DeathRecipient;
|
||||
using ndk::ScopedAStatus;
|
||||
|
||||
HealthServicePair HealthServicePair::get() {
|
||||
HealthServicePair ret;
|
||||
auto service_name = IHealth::descriptor + "/default"s;
|
||||
if (AServiceManager_isDeclared(service_name.c_str())) {
|
||||
ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
|
||||
ret.aidl_health = IHealth::fromBinder(binder);
|
||||
if (ret.aidl_health == nullptr) {
|
||||
LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved.";
|
||||
}
|
||||
}
|
||||
if (ret.aidl_health == nullptr) {
|
||||
LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
|
||||
ret.hidl_health = get_health_service();
|
||||
if (ret.hidl_health != nullptr) {
|
||||
ret.aidl_health = ndk::SharedRefBase::make<HealthShim>(ret.hidl_health);
|
||||
}
|
||||
}
|
||||
if (ret.aidl_health == nullptr) {
|
||||
LOG(WARNING) << "health: failed to find IHealth service";
|
||||
return {};
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline charger_stat_t is_charger_on(BatteryStatus prop) {
|
||||
return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
|
||||
CHARGER_ON : CHARGER_OFF;
|
||||
}
|
||||
|
||||
Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
|
||||
mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
|
||||
return android::hardware::Void();
|
||||
}
|
||||
class HealthInfoCallback : public BnHealthInfoCallback {
|
||||
public:
|
||||
HealthInfoCallback(uid_monitor* uidm) : mUidm(uidm) {}
|
||||
ScopedAStatus healthInfoChanged(const HealthInfo& info) override {
|
||||
mUidm->set_charger_state(is_charger_on(info.batteryStatus));
|
||||
return ScopedAStatus::ok();
|
||||
}
|
||||
|
||||
private:
|
||||
uid_monitor* mUidm;
|
||||
};
|
||||
|
||||
void storaged_t::init() {
|
||||
init_health_service();
|
||||
|
|
@ -91,42 +128,59 @@ void storaged_t::init() {
|
|||
storage_info.reset(storage_info_t::get_storage_info(health));
|
||||
}
|
||||
|
||||
static void onHealthBinderDied(void*) {
|
||||
LOG(ERROR) << "health service died, exiting";
|
||||
android::hardware::IPCThreadState::self()->stopProcess();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void storaged_t::init_health_service() {
|
||||
if (!mUidm.enabled())
|
||||
return;
|
||||
|
||||
health = get_health_service();
|
||||
if (health == NULL) {
|
||||
LOG(WARNING) << "health: failed to find IHealth service";
|
||||
return;
|
||||
}
|
||||
auto [aidlHealth, hidlHealth] = HealthServicePair::get();
|
||||
health = aidlHealth;
|
||||
if (health == nullptr) return;
|
||||
|
||||
BatteryStatus status = BatteryStatus::UNKNOWN;
|
||||
auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
|
||||
if (r != Result::SUCCESS) {
|
||||
LOG(WARNING) << "health: cannot get battery status " << toString(r);
|
||||
return;
|
||||
}
|
||||
if (v == BatteryStatus::UNKNOWN) {
|
||||
LOG(WARNING) << "health: invalid battery status";
|
||||
}
|
||||
status = v;
|
||||
});
|
||||
auto ret = health->getChargeStatus(&status);
|
||||
if (!ret.isOk()) {
|
||||
LOG(WARNING) << "health: get charge status transaction error " << ret.description();
|
||||
LOG(WARNING) << "health: cannot get battery status: " << ret.getDescription();
|
||||
}
|
||||
if (status == BatteryStatus::UNKNOWN) {
|
||||
LOG(WARNING) << "health: invalid battery status";
|
||||
}
|
||||
|
||||
mUidm.init(is_charger_on(status));
|
||||
// register listener after init uid_monitor
|
||||
health->registerCallback(this);
|
||||
health->linkToDeath(this, 0 /* cookie */);
|
||||
aidl_health_callback = std::make_shared<HealthInfoCallback>(&mUidm);
|
||||
ret = health->registerCallback(aidl_health_callback);
|
||||
if (!ret.isOk()) {
|
||||
LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
|
||||
}
|
||||
|
||||
if (hidlHealth != nullptr) {
|
||||
hidl_death_recp = new hidl_health_death_recipient(hidlHealth);
|
||||
auto ret = hidlHealth->linkToDeath(hidl_death_recp, 0 /* cookie */);
|
||||
if (!ret.isOk()) {
|
||||
LOG(WARNING) << "Failed to link to death (HIDL): " << ret.description();
|
||||
}
|
||||
} else {
|
||||
aidl_death_recp =
|
||||
ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(onHealthBinderDied));
|
||||
auto ret = AIBinder_linkToDeath(health->asBinder().get(), aidl_death_recp.get(),
|
||||
nullptr /* cookie */);
|
||||
if (ret != STATUS_OK) {
|
||||
LOG(WARNING) << "Failed to link to death (AIDL): "
|
||||
<< ScopedAStatus(AStatus_fromStatus(ret)).getDescription();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
|
||||
if (health != NULL && interfacesEqual(health, who.promote())) {
|
||||
LOG(ERROR) << "health service died, exiting";
|
||||
android::hardware::IPCThreadState::self()->stopProcess();
|
||||
exit(1);
|
||||
void hidl_health_death_recipient::serviceDied(uint64_t cookie,
|
||||
const wp<::android::hidl::base::V1_0::IBase>& who) {
|
||||
if (mHealth != nullptr && interfacesEqual(mHealth, who.promote())) {
|
||||
onHealthBinderDied(reinterpret_cast<void*>(cookie));
|
||||
} else {
|
||||
LOG(ERROR) << "unknown service died";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,8 @@
|
|||
|
||||
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;
|
||||
using aidl::android::hardware::health::DiskStats;
|
||||
using aidl::android::hardware::health::IHealth;
|
||||
|
||||
#ifdef DEBUG
|
||||
void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
|
||||
|
|
@ -121,39 +118,30 @@ void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
|
|||
dst->io_in_queue = src.ioInQueue;
|
||||
}
|
||||
|
||||
bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
|
||||
bool get_disk_stats_from_health_hal(const std::shared_ptr<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::NOT_SUPPORTED) {
|
||||
LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
|
||||
return;
|
||||
std::vector<DiskStats> halStats;
|
||||
auto ret = service->getDiskStats(&halStats);
|
||||
if (ret.isOk()) {
|
||||
if (halStats.size() > 0) {
|
||||
convert_hal_disk_stats(stats, halStats[0]);
|
||||
init_disk_stats_other(ts, stats);
|
||||
return true;
|
||||
}
|
||||
if (result != Result::SUCCESS || halStats.size() == 0) {
|
||||
LOG(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(ERROR) << "getDiskStats failed with " << ret.description();
|
||||
LOG(ERROR) << "getDiskStats succeeded but size is 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
|
||||
LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
|
||||
return false;
|
||||
}
|
||||
|
||||
init_disk_stats_other(ts, stats);
|
||||
return true;
|
||||
LOG(ERROR) << "getDiskStats failed with " << ret.getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
struct disk_perf get_disk_perf(struct disk_stats* stats)
|
||||
|
|
|
|||
|
|
@ -36,9 +36,8 @@ using namespace chrono;
|
|||
using namespace android::base;
|
||||
using namespace storaged_proto;
|
||||
|
||||
using android::hardware::health::V2_0::IHealth;
|
||||
using android::hardware::health::V2_0::Result;
|
||||
using android::hardware::health::V2_0::StorageInfo;
|
||||
using aidl::android::hardware::health::IHealth;
|
||||
using aidl::android::hardware::health::StorageInfo;
|
||||
|
||||
const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
|
||||
const char* emmc_info_t::emmc_ver_str[9] = {
|
||||
|
|
@ -57,7 +56,7 @@ bool FileExists(const std::string& filename)
|
|||
|
||||
} // namespace
|
||||
|
||||
storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
|
||||
storage_info_t* storage_info_t::get_storage_info(const shared_ptr<IHealth>& healthService) {
|
||||
if (healthService != nullptr) {
|
||||
return new health_storage_info_t(healthService);
|
||||
}
|
||||
|
|
@ -326,23 +325,22 @@ void ufs_info_t::report()
|
|||
}
|
||||
|
||||
void health_storage_info_t::report() {
|
||||
auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
|
||||
if (result == Result::NOT_SUPPORTED) {
|
||||
LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
|
||||
vector<StorageInfo> halInfos;
|
||||
auto ret = mHealth->getStorageInfo(&halInfos);
|
||||
if (ret.isOk()) {
|
||||
if (halInfos.size() == 0) {
|
||||
set_values_from_hal_storage_info(halInfos[0]);
|
||||
publish();
|
||||
return;
|
||||
}
|
||||
if (result != Result::SUCCESS || halInfos.size() == 0) {
|
||||
LOG(ERROR) << "getStorageInfo failed with result " << toString(result) << " and size "
|
||||
<< halInfos.size();
|
||||
return;
|
||||
}
|
||||
set_values_from_hal_storage_info(halInfos[0]);
|
||||
publish();
|
||||
});
|
||||
|
||||
if (!ret.isOk()) {
|
||||
LOG(ERROR) << "getStorageInfo failed with " << ret.description();
|
||||
LOG(ERROR) << "getStorageInfo succeeded but size is 0";
|
||||
return;
|
||||
}
|
||||
if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
|
||||
LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
|
||||
return;
|
||||
}
|
||||
LOG(ERROR) << "getStorageInfo failed with " << ret.getDescription();
|
||||
}
|
||||
|
||||
void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
|
||||
|
|
|
|||
|
|
@ -240,9 +240,7 @@ void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
|
|||
}
|
||||
|
||||
TEST(storaged_test, disk_stats_monitor) {
|
||||
using android::hardware::health::V2_0::get_health_service;
|
||||
|
||||
auto healthService = get_health_service();
|
||||
auto [healthService, hidlHealth] = HealthServicePair::get();
|
||||
|
||||
// asserting that there is one file for diskstats
|
||||
ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue