aidl: battery: Switch to using JSON config for sysfs paths

This commit is contained in:
roynatech2544
2024-08-03 14:05:27 +09:00
committed by Royna2544
parent c82e343e06
commit d650fc2f0b
11 changed files with 294 additions and 153 deletions

View File

@@ -10,6 +10,7 @@ cc_binary {
init_rc: ["vendor.samsung_ext.framework.battery-service.rc"],
vintf_fragments: ["vendor.samsung_ext.framework.battery-service.xml"],
srcs: [
"JSONParser.cpp",
"SmartCharge.cpp",
"service.cpp",
],
@@ -21,11 +22,19 @@ cc_binary {
"liblog",
"libutils",
"libsafestoi",
"libjsoncpp",
"android.hardware.health@2.0",
"android.hardware.health-V1-ndk",
"vendor.samsung_ext.framework.battery-V1-ndk",
],
whole_static_libs: ["libhealthhalutils"],
system_ext_specific: true,
required: ["battery.default"],
required: ["smartcharge_nodes_json"],
}
prebuilt_etc {
name: "smartcharge_nodes_json",
src: "smartcharge_nodes.json",
filename_from_src: true,
system_ext_specific: true,
}

View File

@@ -0,0 +1,136 @@
#include "JSONParser.hpp"
#include <android-base/logging.h>
#include <initializer_list>
#include <fstream>
#include <functional>
#include <map>
#include <string>
#include <vector>
void ConfigParser::handler_OpenFile(const std::string &node,
const std::string &data) {
LOG(DEBUG) << "Opening file: " << data;
std::ifstream file(node);
std::string line;
if (file) {
file >> line;
} else {
PLOG(ERROR) << "Failed to read file";
}
}
void ConfigParser::handler_WriteFile(const std::string &node,
const std::string &data) {
LOG(DEBUG) << "Writing to file: " << data;
std::ofstream file(node);
if (file) {
file << data;
} else {
PLOG(ERROR) << "Failed to write to file";
}
}
// Returns a pair with the matching device and its match quality.
std::pair<Json::Value, ConfigParser::MatchQuality>
ConfigParser::lookupEntry(const SearchEntry &search) {
std::pair<Json::Value, MatchQuality> current = {root, MatchQuality::NO_MATCH};
for (const auto &devices : root) {
if (devices["codename"].asString() == search.codename) {
current = {devices, MatchQuality::EXACT};
break;
}
if (devices["vendor"].asString() == search.vendor) {
current = {devices, MatchQuality::MATCHES_VENDOR};
}
}
if (current.second == MatchQuality::NO_MATCH) {
LOG(ERROR) << "No matching device found";
return current;
}
LOG(DEBUG) << "Found a match with quality: ";
switch (current.second) {
case MatchQuality::EXACT:
LOG(DEBUG) << "EXACT";
break;
case MatchQuality::MATCHES_VENDOR:
LOG(DEBUG) << "MATCHES_VENDOR";
break;
case MatchQuality::NO_MATCH:
break;
};
return current;
}
ConfigParser::ConfigParser(const std::string &path) {
std::ifstream file(path);
if (!file.is_open()) {
PLOG(ERROR) << "Failed to open file";
return;
}
file >> root;
}
std::function<void(bool)> ConfigParser::findEntry(const SearchEntry &search) {
constexpr int ENABLE_FN_INDEX = 0;
constexpr int DISABLE_FN_INDEX = 1;
std::array<std::function<void(void)>, 2> handlers;
const auto current = lookupEntry(search);
if (current.second == MatchQuality::NO_MATCH) {
return [](bool) {};
}
for (const auto &action : current.first["actions"]) {
if (!action["action"].isString()) {
LOG(ERROR) << "Invalid action type";
return [](bool) {};
}
const std::string actionType = action["action"].asString();
const std::string node = action["node"].asString();
const std::string handlerName = action["handler"].asString();
const std::string handlerData = action["handler_data"].asString();
if (actionType == "enable") {
for (const auto &handler : m_handlers) {
if (handler.first == handlerName) {
handlers[ENABLE_FN_INDEX] = [callback = handler.second, node,
handlerData]() {
callback(node, handlerData);
};
break;
}
}
if (!handlers[ENABLE_FN_INDEX]) {
LOG(ERROR) << "No handlers found for enable action";
return [](bool) {};
}
} else if (actionType == "disable") {
for (const auto &handler : m_handlers) {
if (handler.first == handlerName) {
handlers[DISABLE_FN_INDEX] = [callback = handler.second, node,
handlerData]() {
callback(node, handlerData);
};
break;
}
}
if (!handlers[DISABLE_FN_INDEX]) {
LOG(ERROR) << "No handlers found for disable action";
return [](bool) {};
}
} else {
LOG(ERROR) << "Invalid action type";
return [](bool) {};
}
}
return [handlers](bool enable) {
if (enable) {
handlers[ENABLE_FN_INDEX]();
} else {
handlers[DISABLE_FN_INDEX]();
}
};
}

View File

@@ -0,0 +1,46 @@
#include <json/json.h>
#include <functional>
class ConfigParser {
public:
struct SearchEntry;
private:
Json::Value root;
struct Handler {
std::function<void(const std::string &, const std::string &)> handler;
std::string name;
};
static void handler_OpenFile(const std::string &node,
const std::string &data);
static void handler_WriteFile(const std::string &node,
const std::string &data);
// Takes a node path and data
using HandlerFunction =
std::function<void(const std::string &, const std::string &)>;
// Takes name and handler function
using HandlerType = std::pair<std::string, HandlerFunction>;
std::vector<HandlerType> m_handlers = {
{"OpenFile", handler_OpenFile},
{"WriteFile", handler_WriteFile},
};
enum class MatchQuality { EXACT, MATCHES_VENDOR, NO_MATCH };
// Returns a pair with the matching device and its match quality.
std::pair<Json::Value, MatchQuality> lookupEntry(const SearchEntry &search);
public:
ConfigParser(const std::string &path);
struct SearchEntry {
std::string codename;
std::string vendor;
};
std::function<void(bool)> findEntry(const SearchEntry &search);
};

View File

@@ -5,17 +5,17 @@
*/
#include "SmartCharge.h"
#include "modules/include/battery.h"
#include "JSONParser.hpp"
#include <GetServiceSupport.h>
#include <SafeStoi.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <hidl/HidlTransportSupport.h>
#include <log/log.h>
#include <chrono>
#include <dlfcn.h>
#include <functional>
#include <sstream>
#include <type_traits>
@@ -37,22 +37,18 @@ static constexpr int kInvalidCfg = -1;
static const char kSmartChargeConfigProp[] = "persist.ext.smartcharge.config";
static const char kSmartChargeEnabledProp[] = "persist.ext.smartcharge.enabled";
static const char kSmartChargeOverrideProp[] = "ro.hardware.battery";
static const char kComma = ',';
template <typename T>
using is_integral_or_bool =
std::enable_if_t<std::is_integral_v<T> || std::is_same_v<T, bool>, bool>;
static inline bool isValidBool(const int val) {
return val == !!val;
}
static inline bool isValidBool(const int val) { return val == !!val; }
static inline bool verifyConfig(const int lower, const int upper) {
return !(upper <= lower || upper > 95 || (0 <= lower && lower < 50));
}
template <typename T, is_integral_or_bool<T> = true>
struct ConfigPair {
template <typename T, is_integral_or_bool<T> = true> struct ConfigPair {
T first, second;
std::string toString(void) {
return std::to_string(first) + kComma + std::to_string(second);
@@ -74,8 +70,7 @@ bool fromString(const std::string &v, ConfigPair<U> *pair) {
return false;
}
template <>
bool fromString(const std::string &v, ConfigPair<bool> *pair) {
template <> bool fromString(const std::string &v, ConfigPair<bool> *pair) {
ConfigPair<int> tmp{};
if (fromString<int>(v, &tmp) && isValidBool(tmp.first) &&
isValidBool(tmp.second)) {
@@ -86,11 +81,10 @@ bool fromString(const std::string &v, ConfigPair<bool> *pair) {
return false;
}
template <typename U>
bool getAndParse(const char *prop, ConfigPair<U> *pair) {
template <typename U> bool getAndParse(const char *prop, ConfigPair<U> *pair) {
std::string propval = GetProperty(prop, "");
if (!propval.empty()) {
return fromString(propval, pair);
return fromString(propval, pair);
}
return false;
}
@@ -115,7 +109,8 @@ void SmartCharge::loadHealthImpl(void) {
healthState = USE_HEALTH_HIDL;
ALOGD("%s: Connected to health HIDL V2.0 HAL", __func__);
hidl_death_recp = new hidl_health_death_recipient(health_hidl);
auto ret = health_hidl->linkToDeath(hidl_death_recp, reinterpret_cast<uint64_t>(this));
auto ret = health_hidl->linkToDeath(hidl_death_recp,
reinterpret_cast<uint64_t>(this));
linkToDeathSuccess = ret.isOk();
reason = ret.description();
} else {
@@ -125,9 +120,9 @@ void SmartCharge::loadHealthImpl(void) {
healthState = USE_HEALTH_AIDL;
ALOGD("%s: Connected to health AIDL HAL", __func__);
aidl_death_recp = ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(onServiceDied)
);
auto ret = AIBinder_linkToDeath(health_aidl->asBinder().get(), aidl_death_recp.get(), this);
AIBinder_DeathRecipient_new(onServiceDied));
auto ret = AIBinder_linkToDeath(health_aidl->asBinder().get(),
aidl_death_recp.get(), this);
linkToDeathSuccess = ret == STATUS_OK;
reason = ndk::ScopedAStatus(AStatus_fromStatus(ret)).getDescription();
}
@@ -151,35 +146,14 @@ bool SmartCharge::loadAndParseConfigProp(void) {
return true;
}
#ifdef __LP64__
#define LIB_PATH "/system_ext/lib64/hw/"
#else
#define LIB_PATH "/system_ext/lib/hw/"
#endif
void SmartCharge::loadConfiguration(void) {
ConfigParser parser("/system_ext/etc/smartcharge_nodes.json");
void SmartCharge::loadImplLibrary(void) {
const static std::string filename = "battery." +
GetProperty(kSmartChargeOverrideProp, "default") + ".so";
const static std::string path = LIB_PATH + filename;
ALOGI("%s: Try dlopen '%s'", __func__, path.c_str());
handle = dlopen(path.c_str(), RTLD_NOW);
if (handle) {
setChargableFunc = reinterpret_cast<void(*)(const bool)>(
dlsym(handle, MODULE_SYM_NAME));
if (setChargableFunc) {
ALOGD("%s: " MODULE_SYM_NAME " function loaded", __func__);
} else {
ALOGE("%s: Failed to find " MODULE_SYM_NAME " symbol", __func__);
// Unused handle, close it
dlclose(handle);
}
} else {
ALOGE("%s: %s", __func__, dlerror() ?: "unknown");
}
setChargableFunc = parser.findEntry({GetProperty("ro.product.device", ""),
GetProperty("ro.product.manufacturer", "")});
if (!setChargableFunc) {
setChargableFunc = [] (const bool) {};
ALOGD("%s: Using stub for setChargableFunc", __func__);
setChargableFunc = [](const bool) {};
}
}
@@ -202,7 +176,7 @@ SmartCharge::SmartCharge(void) {
bool ret;
loadHealthImpl();
loadImplLibrary();
loadConfiguration();
ret = loadAndParseConfigProp();
if (ret) {
@@ -227,12 +201,12 @@ void SmartCharge::startLoop(bool withrestart) {
BatteryStatus status_aidl = BatteryStatus::UNKNOWN;
auto ret = health_aidl->getCapacity(&per);
if (!ret.isOk()) {
per = ret.getStatus();
per = ret.getStatus();
break;
}
ret = health_aidl->getChargeStatus(&status_aidl);
if (!ret.isOk()) {
per = ret.getStatus();
per = ret.getStatus();
break;
}
switch (status_aidl) {
@@ -243,15 +217,15 @@ void SmartCharge::startLoop(bool withrestart) {
case BatteryStatus::DISCHARGING:
case BatteryStatus::NOT_CHARGING:
current = ChargeStatus::OFF;
break;
break;
default:
break;
};
break;
}
case USE_HEALTH_HIDL: {
using ::android::hardware::health::V2_0::Result;
using ::android::hardware::health::V1_0::BatteryStatus;
using ::android::hardware::health::V2_0::Result;
ScopedLock _(hal_health_lock);
Result res = Result::UNKNOWN;
@@ -264,10 +238,11 @@ void SmartCharge::startLoop(bool withrestart) {
per = -(static_cast<int>(res));
break;
}
health_hidl->getChargeStatus([&res, &status_hidl](Result hal_res, BatteryStatus hal_value) {
res = hal_res;
status_hidl = hal_value;
});
health_hidl->getChargeStatus(
[&res, &status_hidl](Result hal_res, BatteryStatus hal_value) {
res = hal_res;
status_hidl = hal_value;
});
if (res != Result::SUCCESS) {
per = -(static_cast<int>(res));
break;
@@ -304,7 +279,8 @@ void SmartCharge::startLoop(bool withrestart) {
skip = true;
if (current != policy && !skip) {
ALOGD("%s: Updating current, current %d, policy %d", __func__, current, policy);
ALOGD("%s: Updating current, current %d, policy %d", __func__, current,
policy);
switch (policy) {
case ChargeStatus::OFF:
setChargableFunc(false);
@@ -320,7 +296,8 @@ void SmartCharge::startLoop(bool withrestart) {
skip = false;
if (cv.wait_for(lock, 5s) == std::cv_status::no_timeout) {
// cv signaled, exit now if kRunning is false
if (!kRunning) break;
if (!kRunning)
break;
}
}
ALOGD("%s: --", __func__);
@@ -329,12 +306,14 @@ void SmartCharge::startLoop(bool withrestart) {
void SmartCharge::createLoopThread(bool restart) {
ScopedLock _(thread_lock);
ALOGD("%s: create thread", __func__);
kLoopThread = std::make_shared<std::thread>(&SmartCharge::startLoop, this, restart);
kLoopThread =
std::make_shared<std::thread>(&SmartCharge::startLoop, this, restart);
kRunning = true;
}
ndk::ScopedAStatus SmartCharge::setChargeLimit(int32_t upper_, int32_t lower_) {
ALOGD("%s: upper: %d, lower: %d, kRun: %d", __func__, upper_, lower_, kRunning.load());
ALOGD("%s: upper: %d, lower: %d, kRun: %d", __func__, upper_, lower_,
kRunning.load());
if (!verifyConfig(lower_, upper_))
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
if (kRunning)
@@ -390,56 +369,50 @@ ndk::ScopedAStatus SmartCharge::activate(bool enable, bool restart) {
return ndk::ScopedAStatus::ok();
}
binder_status_t SmartCharge::dump(int fd, const char** /* args */, uint32_t /* numArgs */) {
Dl_info info;
void *addr;
auto tryLockFn = [](std::mutex& m) {
const std::unique_lock<std::mutex> lk{m, std::try_to_lock};
return !lk.owns_lock();
binder_status_t SmartCharge::dump(int fd, const char ** /* args */,
uint32_t /* numArgs */) {
auto tryLockFn = [](std::mutex &m) {
const std::unique_lock<std::mutex> lk{m, std::try_to_lock};
return !lk.owns_lock();
};
dprintf(fd, "Loop thread running: %d\n", kRunning.load());
if (kRunning) {
dprintf(fd, "Loop thread charge control state: ");
switch (status) {
case ChargeStatus::ON:
dprintf(fd, "ON");
break;
case ChargeStatus::OFF:
dprintf(fd, "OFF");
break;
}
dprintf(fd, "\n");
dprintf(fd, "Loop thread charge control state: ");
switch (status) {
case ChargeStatus::ON:
dprintf(fd, "ON");
break;
case ChargeStatus::OFF:
dprintf(fd, "OFF");
break;
}
dprintf(fd, "\n");
}
dprintf(fd, "Configuration (upper/lower): %d %d\n", upper, lower);
dprintf(fd, "Mutex locked (config/thread/cv) %d %d %d\n", tryLockFn(config_lock),
tryLockFn(thread_lock), tryLockFn(kCVLock));
dprintf(fd, "Mutex locked (config/thread/cv) %d %d %d\n",
tryLockFn(config_lock), tryLockFn(thread_lock), tryLockFn(kCVLock));
dprintf(fd, "Connected Health HAL: ");
switch (healthState) {
case USE_HEALTH_AIDL:
dprintf(fd, "AIDL Health HAL V1");
break;
case USE_HEALTH_HIDL:
dprintf(fd, "HIDL Health HAL V2.0");
break;
default:
break;
case USE_HEALTH_AIDL:
dprintf(fd, "AIDL Health HAL V1");
break;
case USE_HEALTH_HIDL:
dprintf(fd, "HIDL Health HAL V2.0");
break;
default:
break;
};
dprintf(fd, "\n");
addr = dlsym(handle, MODULE_SYM_NAME);
if (dladdr(addr, &info) != 0) {
dprintf(fd, "Impl library path: %s\n", info.dli_fname);
}
return STATUS_OK;
}
using ::android::hardware::interfacesEqual;
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())) {
onServiceDied(reinterpret_cast<void*>(cookie));
}
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())) {
onServiceDied(reinterpret_cast<void *>(cookie));
}
}
} // namespace battery

View File

@@ -10,8 +10,6 @@
#include <aidl/android/hardware/health/BnHealth.h>
#include <healthhalutils/HealthHalUtils.h>
#include <dlfcn.h>
#include <atomic>
#include <condition_variable>
#include <functional>
@@ -62,7 +60,6 @@ class SmartCharge : public BnSmartCharge {
// Used by above condition_variable
std::mutex kCVLock;
void* handle;
std::function<void(const bool)> setChargableFunc;
sp<IHealth> health_hidl;
@@ -84,7 +81,7 @@ class SmartCharge : public BnSmartCharge {
} status;
bool loadAndParseConfigProp();
void loadImplLibrary();
void loadConfiguration();
void loadEnabledAndStart();
public:

View File

@@ -1,18 +0,0 @@
cc_library_headers {
name: "aidl_battery_module_headers",
export_include_dirs: ["include"],
}
cc_defaults {
name: "aidl_battery_module_defaults",
header_libs: ["aidl_battery_module_headers"],
relative_install_path: "hw",
compile_multilib: "first",
system_ext_specific: true,
}
cc_library_shared {
name: "battery.default",
defaults: ["aidl_battery_module_defaults"],
srcs: ["DefaultImpl.cpp"],
}

View File

@@ -1,11 +0,0 @@
#include <battery.h>
#include <fstream>
static const char kChargeCtlSysfs[] =
"/sys/class/power_supply/battery/batt_slate_mode";
extern "C" void setChargable(bool enable) {
std::ofstream p(kChargeCtlSysfs);
if (p)
p << !enable;
}

View File

@@ -1,6 +0,0 @@
#include <battery.h>
extern "C"
void setChargable(bool enable) {
(void) enable;
}

View File

@@ -1,18 +0,0 @@
#pragma once
#include <stdbool.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
/**
* setChargable - Target specific method of enabling charge.
*
* @param enable Enable charging or not
* @return void
*/
extern void setChargable(const bool enable);
#define MODULE_SYM_NAME "setChargable"
__END_DECLS

View File

@@ -0,0 +1,35 @@
[
{
"codename": "tb128fu",
"vendor": "lenovo",
"actions": [
{
"action": "enable",
"node": "/sys/class/power_supply/battery/StartCharging_Test",
"handler": "OpenFile"
},
{
"action": "disable",
"node": "/sys/class/power_supply/battery/StopCharging_Test",
"handler": "OpenFile"
}
]
},
{
"vendor": "samsung",
"actions": [
{
"action": "enable",
"node": "/sys/class/power_supply/battery/batt_slate_mode",
"handler": "WriteFile",
"handler_data": "0"
},
{
"action": "disable",
"node": "/sys/class/power_supply/battery/batt_slate_mode",
"handler": "WriteFile",
"handler_data": "1"
}
]
}
]

View File

@@ -2,5 +2,3 @@ persist.ext. u:object_r:ext_prop:s0
persist.ext.smartcharge. u:object_r:ext_smartcharge_prop:s0
persist.ext.flashlight. u:object_r:ext_flashlight_prop:s0
persist.ext.logdump. u:object_r:ext_logger_prop:s0
ro.hardware.battery u:object_r:exported_default_prop:s0 exact string