healthd mainly reads the information of the battery node and passes it to the BatteryService. Or use it when shutting down the power and charging, etc. Note that the kernel log is used in healthd.
The following is the main function analysis
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")) {//If the input parameters are used to analyze charger_ops, I will not introduce them here healthd_mode_ops = &charger_ops; } else { while ((ch = getopt(argc, argv, "cr")) != -1) {//Analyze the input commands, each command corresponds to different 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 is initialized if (ret) { KLOG_ERROR("Initialization failed, exiting\n"); exit(2); } healthd_mainloop();//Main function KLOG_ERROR("Main loop terminated, exiting\n"); return 3; }
If the computer is turned on normally, without shutting down and charging, healthd_mode_ops = &android_ops; and the specific functions here will be introduced in detail later.
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, };
The following analysis of the healthd_init function is performed. Heathd uses epoll for IO multiplexing.
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; }
The healthd_mode_ops->init function here is the healthd_mode_android_init function of android_ops. Here, the main thing is to add the fd of the binder communication to epoll, unlike the ordinary binder process that finally uses IPCTThreadState::self()->joinThreadPool. In this way, all fds are managed in epoll, using only one thread
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 Add "batteryproperties" Service to ServiceManager
void BatteryPropertiesRegistrar::publish() { defaultServiceManager()->addService(String16("batteryproperties"), this); }
Next, let's take a look at 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 sets the alarm wake-up interval, and then look at the time processing function
static void wakealarm_event(uint32_t /*epevents*/) { unsigned long long wakeups; if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {//End of error 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() ?//Call the main update function, according to the return value, if it is currently charging, return true healthd_config.periodic_chores_interval_fast ://Time set for 1 minute 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 ?//The current time is one minute, epoll is forever blocking, otherwise it is 1 minute -1 : healthd_config.periodic_chores_interval_fast * 1000; }
Let's take a look at uEvent's next.
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"); }
Check out the processing function of uevent_event. After obtaining uevent, it mainly determines whether it is from the power system. If it is called the healthd_battery_update function
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)) {//It is the call to the healthd_battery_update function of this subsystem healthd_battery_update(); break; } /* advance to after the next \0 */ while (*cp++) ; } }
The main function of healthd_mainloop is analyzed below. The main function is mainly the epoll function to listen for 3 fds and handle events whenever there are events.
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 waits for each fd event, timeout is 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].)//Transfer the events of each fd, and each processing function handles (*(void (*)(int))events[n].)(events[n].events); } if (!nevents)//When there is no event, it is because the epoll timeout setting is set, and you must update it at this time. periodic_chores(); healthd_mode_ops->heartbeat(); } return; }
The init function mainly passes in the healthd_config object and initializes and saves some address information of the members inside. It mainly saves some address information and charging method.
void BatteryMonitor::init(struct healthd_config *hc) { String8 path; char pval[PROPERTY_VALUE_MAX]; mHealthdConfig = hc;// Assign the pointer of heatdconfig passed in from outside to the member variable DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH);//Open address /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 (); ("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name); switch(readPowerSupplyType(path)) {//Read the value of the type in each directory. For example, the value of the type under /sys/class/power_supply/battery is Battery. It is read in readPowerSupplyType and converted to ANDROID_POWER_SUPPLY_TYPE_BATTERY case ANDROID_POWER_SUPPLY_TYPE_AC: if (mHealthdConfig->()) { (); ("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->acChargeHeathPath = path;//Configuration path } (); ("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); if (access((), R_OK) == 0) (String8(name));//chargername is the current directory name: ac break; case ANDROID_POWER_SUPPLY_TYPE_USB://usb similar to ac if (mHealthdConfig->()) { (); ("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->usbChargeHeathPath = path; } (); ("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); if (access((), R_OK) == 0) (String8(name)); break; case ANDROID_POWER_SUPPLY_TYPE_WIRELESS://similar (); ("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); if (access((), R_OK) == 0) (String8(name)); break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY://battery mBatteryDevicePresent = true; if (mHealthdConfig->()) { (); ("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryStatusPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryPresentPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) { mHealthdConfig->batteryVoltagePath = path; } else { (); ("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryVoltagePath = path; } } if (mHealthdConfig->()) { (); ("%s/%s/current_now", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryCurrentNowPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/current_avg", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryCurrentAvgPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/charge_counter", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryChargeCounterPath = path; } if (mHealthdConfig->()) { (); ("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) { mHealthdConfig->batteryTemperaturePath = path; } else { (); ("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) mHealthdConfig->batteryTemperaturePath = path; } } if (mHealthdConfig->()) { (); ("%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 (!()) KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); if (!mBatteryDevicePresent) {//The main variable of battery is 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->()) KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); 。。。。。。。。。。。。。// Here are some warnings } if (property_get(".fake_battery", pval, NULL) > 0 && strtol(pval, NULL, 10) != 0) { mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY; mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE; } }
The following is the update function, encapsulating the data in BatteryProperties, and sending BatteryProperties to the upper layer through healthd_mode_ops->battery_update.
bool BatteryMonitor::update(void) { bool logthis; = false; = false; = false; = BATTERY_STATUS_UNKNOWN; = BATTERY_HEALTH_UNKNOWN; //They all take addresses from the previously configured mHealthd, read node information, and save them to props member variable if (!mHealthdConfig->()) = getBooleanField(mHealthdConfig->batteryPresentPath); else = mBatteryDevicePresent; = mBatteryFixedCapacity ? mBatteryFixedCapacity : getIntField(mHealthdConfig->batteryCapacityPath); = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; = mBatteryFixedTemperature ? mBatteryFixedTemperature : getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; char buf[SIZE]; String8 btech; if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0) = getBatteryStatus(buf); if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0) = getBatteryHealth(buf); if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0) = String8(buf); if (readFromFile(mHealthdConfig->acChargeHeathPath, buf, SIZE) > 0) = String8(buf); if (readFromFile(mHealthdConfig->usbChargeHeathPath, buf, SIZE) > 0) = String8(buf); unsigned int i; for (i = 0; i < (); i++) {//Travel over the various charging methods saved before String8 path; ("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].string());//The path is the online field in each directory, such as online under /sys/class/power_supply/usb if (readFromFile(path, buf, SIZE) > 0) { if (buf[0] != '0') {//Read the content in online. If you are currently charging on USB, then the content in online under USB is 1 (); ("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].string());// Let's see which type it is from switch(readPowerSupplyType(path)) { case ANDROID_POWER_SUPPLY_TYPE_AC: = true; break; case ANDROID_POWER_SUPPLY_TYPE_USB:// Assign its value to true = true; break; case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: = 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 () { snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d", , , < 0 ? "-" : "", abs( / 10), abs( % 10), , ); if (!mHealthdConfig->()) { 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, ? "a" : "", ? "u" : "", ? "w" : ""); } healthd_mode_ops->battery_update(&props);//Pass the data to the upper layer BatteryService return | |//Return to whether it is currently charged ; }
Next, let's see how healthd_mode_ops->battery_update passes data to the upper layer
void healthd_mode_android_battery_update( struct android::BatteryProperties *props) { if (gBatteryPropertiesRegistrar != NULL) gBatteryPropertiesRegistrar->notifyListeners(*props); return; }
The upper layer will communicate through binder and register a callback to 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 < (); i++) { if (mListeners[i]->asBinder() == listener->asBinder()) { return; } } (listener); listener->asBinder()->linkToDeath(this); } healthd_battery_update(); }
The update function calls notifyListeners to traverse each listener and pass it to the upper BatteryService
void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { Mutex::Autolock _l(mRegistrationLock); for (size_t i = 0; i < (); i++) { mListeners[i]->batteryPropertiesChanged(props); } }
Let’s take a look at BatteryService. In onStart, communicate with the batteryProperties Service through the ServiceManager, and register the BatteryListener listener into the batteryproperties
@Override public void onStart() { IBinder b = ("batteryproperties"); final IBatteryPropertiesRegistrar batteryPropertiesRegistrar = (b); try { (new BatteryListener()); } catch (RemoteException e) { // Should never happen. } publishBinderService("battery", new BinderService()); publishLocalService(, new LocalService()); }
Let’s take a look at the batteryPropertiesChanged interface of BatteryListener. When this interface is called below, the update function of BatteryService will be called, and then some of the main processes of BatteryService will not be analyzed.
private final class BatteryListener extends { @Override public void batteryPropertiesChanged(BatteryProperties props) { final long identity = (); try { (props); } finally { (identity); } } }
BatteryService accepts healthd data and is passive, and healthd comes through. Have you taken the initiative to check healthd?
In BatteryManager, there are active searches for healthd, the code is as follows
private long queryProperty(int id) { long ret; if (mBatteryPropertiesRegistrar == null) { IBinder b = ("batteryproperties");//Get batteryproperties Service mBatteryPropertiesRegistrar = (b);//Input conversion if (mBatteryPropertiesRegistrar == null) return Long.MIN_VALUE; } try { BatteryProperty prop = new BatteryProperty(); if ((id, prop) == 0)//prop is output ret = (); else ret = Long.MIN_VALUE; } catch (RemoteException e) { ret = Long.MIN_VALUE; } return ret; }
Go to healthd and see the corresponding interface
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); }
The BatteryProperty object in java corresponds to this pointer
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://Return different values according to different IDs if (!mHealthdConfig->()) { val->valueInt64 = getIntField(mHealthdConfig->batteryChargeCounterPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CURRENT_NOW: if (!mHealthdConfig->()) { val->valueInt64 = getIntField(mHealthdConfig->batteryCurrentNowPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CURRENT_AVG: if (!mHealthdConfig->()) { val->valueInt64 = getIntField(mHealthdConfig->batteryCurrentAvgPath); ret = NO_ERROR; } else { ret = NAME_NOT_FOUND; } break; case BATTERY_PROP_CAPACITY: if (!mHealthdConfig->()) { 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; }
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.