深入解析android5.1 healthd
healthd主要是讀取電池節(jié)點的信息,傳給BatteryService?;蛘咴陉P機充電等使用。注意healthd中使用的是kernel的log。
下面先從main函數(shù)分析
int main(int argc, char **argv) {
int ch;
int ret;
klog_set_level(KLOG_LEVEL);
healthd_mode_ops = &android_ops;
if (!strcmp(basename(argv[0]), "charger")) {//解析輸入?yún)?shù)如果是charger的使用charger_ops,這里就不做介紹
healthd_mode_ops = &charger_ops;
} else {
while ((ch = getopt(argc, argv, "cr")) != -1) {//分析輸入命令,各個命令對應不同的charger_ops
switch (ch) {
case 'c':
healthd_mode_ops = &charger_ops;
break;
case 'r':
healthd_mode_ops = &recovery_ops;
break;
case '?':
default:
KLOG_ERROR(LOG_TAG, "Unrecognized healthd option: %c\n",
optopt);
exit(1);
}
}
}
ret = healthd_init();//healthd做初始化
if (ret) {
KLOG_ERROR("Initialization failed, exiting\n");
exit(2);
}
healthd_mainloop();//主函數(shù)
KLOG_ERROR("Main loop terminated, exiting\n");
return 3;
}
如果是正常開機,不走關機充電等,healthd_mode_ops = &android_ops;而這里面具體的函數(shù)在后面進行詳細的介紹。
static struct healthd_mode_ops android_ops = {
.init = healthd_mode_android_init,
.preparetowait = healthd_mode_android_preparetowait,
.heartbeat = healthd_mode_nop_heartbeat,
.battery_update = healthd_mode_android_battery_update,
};
下面分析下healthd_init函數(shù),heathd使用了epoll進行IO復用。
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;
}
這里的healthd_mode_ops->init的函數(shù)是android_ops 的healthd_mode_android_init函數(shù),這里主要是將binder通信的fd也加入epoll,而不像普通binder進程最后使用IPCThreadState::self()->joinThreadPool。這樣所有的fd全在epoll管理,只用了一個線程
int healthd_mode_android_preparetowait(void) {
IPCThreadState::self()->flushCommands();
return -1;
}
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->publish將"batteryproperties"這個Service加入到ServiceManager中
void BatteryPropertiesRegistrar::publish() {
defaultServiceManager()->addService(String16("batteryproperties"), this);
}
接下來再來看下wakealarm_init
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))
KLOG_ERROR(LOG_TAG,
"Registration of wakealarm event failed\n");
wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
}
wakealarm_init設置alarm喚醒的interval,再來看下時間處理函數(shù)
static void wakealarm_event(uint32_t /*epevents*/) {
unsigned long long wakeups;
if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {//出錯結(jié)束
KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");
return;
}
KLOG_ERROR(LOG_TAG, "wakealarm_event\n");
periodic_chores();
}
static void periodic_chores() {
healthd_battery_update();
}
void healthd_battery_update(void) {
// Fast wake interval when on charger (watch for overheat);
// slow wake interval when on battery (watch for drained battery).
KLOG_ERROR(LOG_TAG, "healthd_battery_update enter\n");
int new_wake_interval = gBatteryMonitor->update() ?//調(diào)用主要的update函數(shù),根據(jù)返回值,如果當前在充電返回true
healthd_config.periodic_chores_interval_fast ://時間設置1分鐘
healthd_config.periodic_chores_interval_slow;
KLOG_ERROR(LOG_TAG, "healthd_battery_update after\n");
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 ?//當前時間是一分鐘,epoll為永遠阻塞,否則為1分鐘
-1 : healthd_config.periodic_chores_interval_fast * 1000;
}
接下來再來看看uEvent的,
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))
KLOG_ERROR(LOG_TAG,
"register for uevent events failed\n");
}
看看uevent_event的處理函數(shù),獲取uevent后主要判斷是否是電源系統(tǒng)的,如果是調(diào)用healthd_battery_update函數(shù)
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;
KLOG_ERROR(LOG_TAG, "uevent_event\n");
while (*cp) {
if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {//是這個子系統(tǒng)的調(diào)用healthd_battery_update函數(shù)
healthd_battery_update();
break;
}
/* advance to after the next \0 */
while (*cp++)
;
}
}
下面分析下healthd_mainloop這個主函數(shù),主函數(shù)主要是epoll函數(shù)監(jiān)聽3個fd,有事件就處理。
static void healthd_mainloop(void) {
while (1) {
struct epoll_event events[eventct];
int nevents;
int timeout = awake_poll_interval;
int mode_timeout;
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);//epoll_wait等待各個fd的事件,timeout為超時時間
KLOG_ERROR(LOG_TAG, "kangchen healthd_mainloop epoll_wait\n");
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)//遍歷各個fd的事件上來,每個處理函數(shù)處理
(*(void (*)(int))events[n].data.ptr)(events[n].events);
}
if (!nevents)//當什么事件沒有的時候,是因為epoll超時設置走下來的,這時候也要update下
periodic_chores();
healthd_mode_ops->heartbeat();
}
return;
}
init函數(shù)主要將healthd_config 對象傳入,并且將里面的成員的一些地址信息去初始化保存起來。主要是保存一些地址信息,以及充電方式。
void BatteryMonitor::init(struct healthd_config *hc) {
String8 path;
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;//將外面?zhèn)鬟M來的heathdconfig的指針賦給成員變量
DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);//打開地址 /sys/class/power_supply
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
while ((entry = readdir(dir))) {
const char* name = entry->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
char buf[20];
// Look for "type" file in each subdirectory
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
switch(readPowerSupplyType(path)) {//讀取各個目錄下type的值,比如/sys/class/power_supply/battery 下type的值為Battery,在readPowerSupplyType讀取并且轉(zhuǎn)化為ANDROID_POWER_SUPPLY_TYPE_BATTERY
case ANDROID_POWER_SUPPLY_TYPE_AC:
if (mHealthdConfig->acChargeHeathPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->acChargeHeathPath = path;//配置路徑
}
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));//chargername 就是當前目錄名字:ac
break;
case ANDROID_POWER_SUPPLY_TYPE_USB://usb 類似ac
if (mHealthdConfig->usbChargeHeathPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->usbChargeHeathPath = path;
}
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS://類似
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY://battery
mBatteryDevicePresent = true;
if (mHealthdConfig->batteryStatusPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryStatusPath = path;
}
if (mHealthdConfig->batteryHealthPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryHealthPath = path;
}
if (mHealthdConfig->batteryPresentPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryPresentPath = path;
}
if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCapacityPath = path;
}
if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_vol",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryVoltagePath = path;
}
}
if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
} else {
path.clear();
path.appendFormat("%s/%s/batt_temp",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTemperaturePath = path;
}
}
if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
break;
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
break;
}
}
closedir(dir);
}
if (!mChargerNames.size())
KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
if (!mBatteryDevicePresent) {//主要由battery該成員變量就為true
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
} else {
if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
。。。。。。。。。。。。。//這里都是一些警告
}
if (property_get("ro.boot.fake_battery", pval, NULL) > 0
&& strtol(pval, NULL, 10) != 0) {
mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
}
}
下面就是update函數(shù),將數(shù)據(jù)封裝在BatteryProperties 中,并且通過healthd_mode_ops->battery_update把BatteryProperties 發(fā)給上層。
bool BatteryMonitor::update(void) {
bool logthis;
props.chargerAcOnline = false;
props.chargerUsbOnline = false;
props.chargerWirelessOnline = false;
props.batteryStatus = BATTERY_STATUS_UNKNOWN;
props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
//都是從之前配置的mHealthd中取地址,讀取節(jié)點信息,保存到props成員變量中
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
props.batteryPresent = mBatteryDevicePresent;
props.batteryLevel = mBatteryFixedCapacity ?
mBatteryFixedCapacity :
getIntField(mHealthdConfig->batteryCapacityPath);
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
const int SIZE = 128;
char buf[SIZE];
String8 btech;
if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0)
props.batteryStatus = getBatteryStatus(buf);
if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0)
props.batteryHealth = getBatteryHealth(buf);
if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0)
props.batteryTechnology = String8(buf);
if (readFromFile(mHealthdConfig->acChargeHeathPath, buf, SIZE) > 0)
props.acChargeHeath= String8(buf);
if (readFromFile(mHealthdConfig->usbChargeHeathPath, buf, SIZE) > 0)
props.usbChargeHeath= String8(buf);
unsigned int i;
for (i = 0; i < mChargerNames.size(); i++) {//遍歷之前保存的各個充電方式
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());//路徑就是每個目錄下的online字段,比如/sys/class/power_supply/usb 下的online
if (readFromFile(path, buf, SIZE) > 0) {
if (buf[0] != '0') {//讀取online里面的內(nèi)容,如果當前在usb線上充電,那么usb下online里面的內(nèi)容為1
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());//這里看看是哪個type的
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
props.chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB://將其值賦成true
props.chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
props.chargerWirelessOnline = true;
break;
default:
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
}
}
}
}
logthis = !healthd_board_battery_update(&props);
if (logthis) {
char dmesgline[256];
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline),
"battery l=%d v=%d t=%s%d.%d h=%d st=%d",
props.batteryLevel, props.batteryVoltage,
props.batteryTemperature < 0 ? "-" : "",
abs(props.batteryTemperature / 10),
abs(props.batteryTemperature % 10), props.batteryHealth,
props.batteryStatus);
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
char b[20];
snprintf(b, sizeof(b), " c=%d", c / 1000);
strlcat(dmesgline, b, sizeof(dmesgline));
}
} else {
snprintf(dmesgline, sizeof(dmesgline),
"battery none");
}
KLOG_WARNING(LOG_TAG, "%s chg=%s%s%s\n", dmesgline,
props.chargerAcOnline ? "a" : "",
props.chargerUsbOnline ? "u" : "",
props.chargerWirelessOnline ? "w" : "");
}
healthd_mode_ops->battery_update(&props);//將數(shù)據(jù)傳到上層的BatteryService
return props.chargerAcOnline | props.chargerUsbOnline |//返回當前是否屬于充電
props.chargerWirelessOnline;
}
接下來看看healthd_mode_ops->battery_update是怎么把數(shù)據(jù)傳到上層的
void healthd_mode_android_battery_update(
struct android::BatteryProperties *props) {
if (gBatteryPropertiesRegistrar != NULL)
gBatteryPropertiesRegistrar->notifyListeners(*props);
return;
}
上層會通過binder通信,注冊一個回調(diào)到BatteryPropertiesRegistrar
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 (mListeners[i]->asBinder() == listener->asBinder()) {
return;
}
}
mListeners.add(listener);
listener->asBinder()->linkToDeath(this);
}
healthd_battery_update();
}
而update函數(shù)就是調(diào)用了notifyListeners遍歷各個listener傳到上層BatteryService
void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) {
Mutex::Autolock _l(mRegistrationLock);
for (size_t i = 0; i < mListeners.size(); i++) {
mListeners[i]->batteryPropertiesChanged(props);
}
}
再來看看BatteryService中,在onStart中通過ServiceManager,和batteryproperties這個Service通信,將BatteryListener這個listenter注冊到batteryproperties中去
@Override
public void onStart() {
IBinder b = ServiceManager.getService("batteryproperties");
final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
try {
batteryPropertiesRegistrar.registerListener(new BatteryListener());
} catch (RemoteException e) {
// Should never happen.
}
publishBinderService("battery", new BinderService());
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
再來看看BatteryListener 的batteryPropertiesChanged接口,當下面調(diào)這個接口,就會調(diào)用BatteryService的update函數(shù),然后就是BatteryService的一些主要流程就不分析了。
private final class BatteryListener extends IBatteryPropertiesListener.Stub {
@Override
public void batteryPropertiesChanged(BatteryProperties props) {
final long identity = Binder.clearCallingIdentity();
try {
BatteryService.this.update(props);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
BatteryService接受healthd的數(shù)據(jù)都是被動的,healthd穿過來的。有沒有主動去healthd查詢的。
在BatteryManager中就有主動去healthd查詢的,代碼如下
private long queryProperty(int id) {
long ret;
if (mBatteryPropertiesRegistrar == null) {
IBinder b = ServiceManager.getService("batteryproperties");//獲取batteryproperties Service
mBatteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);//接口轉(zhuǎn)化下
if (mBatteryPropertiesRegistrar == null)
return Long.MIN_VALUE;
}
try {
BatteryProperty prop = new BatteryProperty();
if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0)//prop是輸出
ret = prop.getLong();
else
ret = Long.MIN_VALUE;
} catch (RemoteException e) {
ret = Long.MIN_VALUE;
}
return ret;
}
再到healthd看看對應的接口
status_t BatteryPropertiesRegistrar::getProperty(int id, struct BatteryProperty *val) {
return healthd_get_property(id, val);
}
status_t healthd_get_property(int id, struct BatteryProperty *val) {
return gBatteryMonitor->getProperty(id, val);
}
java的BatteryProperty對象對應到這邊是指針
status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
status_t ret = BAD_VALUE;
val->valueInt64 = LONG_MIN;
switch(id) {
case BATTERY_PROP_CHARGE_COUNTER://根據(jù)不同ID,返回不同值
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryChargeCounterPath);
ret = NO_ERROR;
} else {
ret = NAME_NOT_FOUND;
}
break;
case BATTERY_PROP_CURRENT_NOW:
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentNowPath);
ret = NO_ERROR;
} else {
ret = NAME_NOT_FOUND;
}
break;
case BATTERY_PROP_CURRENT_AVG:
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
ret = NO_ERROR;
} else {
ret = NAME_NOT_FOUND;
}
break;
case BATTERY_PROP_CAPACITY:
if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
val->valueInt64 =
getIntField(mHealthdConfig->batteryCapacityPath);
ret = NO_ERROR;
} else {
ret = NAME_NOT_FOUND;
}
break;
case BATTERY_PROP_ENERGY_COUNTER:
if (mHealthdConfig->energyCounter) {
ret = mHealthdConfig->energyCounter(&val->valueInt64);
} else {
ret = NAME_NOT_FOUND;
}
break;
default:
break;
}
return ret;
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Android實現(xiàn)電池管理系統(tǒng)
- Android電池電量監(jiān)聽的示例代碼
- Android4.4開發(fā)之電池低電量告警提示原理與實現(xiàn)方法分析
- Android電池電量跳變
- Android編程實現(xiàn)對電池狀態(tài)的監(jiān)視功能示例
- Android監(jiān)聽電池狀態(tài)實例代碼
- Android查看電池電量的方法(基于BroadcastReceiver)
- Android編程之電池電量信息更新的方法(基于BatteryService實現(xiàn))
- Android獲取手機電池電量用法實例
- Android實現(xiàn)偵聽電池狀態(tài)顯示、電量及充電動態(tài)顯示的方法
相關文章
Android使用TouchDelegate增加View的觸摸范圍
這篇文章主要為大家詳細介紹了Android使用TouchDelegate增加View的觸摸范圍,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
Android Jetpack組件庫LiveData源碼深入探究
LiveData是Jetpack組件的一部分,更多的時候是搭配ViewModel來使用,相對于Observable,LiveData的最大優(yōu)勢是其具有生命感知的,換句話說,LiveData可以保證只有在組件( Activity、Fragment、Service)處于活動生命周期狀態(tài)的時候才會更新數(shù)據(jù)2022-09-09
Android Flutter實現(xiàn)上拉加載組件的示例代碼
既然列表有下拉刷新外當然還有上拉加載更多操作了,本次就為大家詳細介紹如何利用Flutter實現(xiàn)為列表增加上拉加載更多的交互,感興趣的可以了解一下2022-08-08

