Android电源管理 --- 电池状态的更新其实很简单

一 系统架构图

下面来看看相关的重要的服务和类。

二 PowerManagerService服务

电源管理服务PowerManagerService,负责协调设备上的电源管理功能。如,控制系统的待机状态,控制显示屏的开关和亮度调节等。 PowerManagerService在SystemServer服务中创建和启动,SystemServer是system_server进程的入口类。system_server进程会启动android中使用的一系列服务,包括PowerManagerService。 主要是创建指定服务,并调用其onStart()方法。system_server进程以后再做介绍。下面我们看看PowerManagerService创建和onStart() 方法做了哪些事。

java 复制代码
//PowerManagerService构造方法
    @VisibleForTesting
    PowerManagerService(Context context, Injector injector) {
        super(context);

        mContext = context;
        mBinderService = new BinderService(mContext);
        mLocalService = new LocalService();
        mNativeWrapper = injector.createNativeWrapper();
        mSystemProperties = injector.createSystemPropertiesWrapper();
        mClock = injector.createClock();
        mInjector = injector;
        //创建启动ServiceThread线程
        mHandlerThread = new ServiceThread(TAG,
                Process.THREAD_PRIORITY_DISPLAY, /* allowIo= */ false);
        mHandlerThread.start();
        //创建hanlder
        mHandler = injector.createHandler(mHandlerThread.getLooper(),
                new PowerManagerHandlerCallback());
        mConstants = new Constants(mHandler);
        mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context);
        mAmbientDisplaySuppressionController =
                mInjector.createAmbientDisplaySuppressionController(
                        mAmbientSuppressionChangedCallback);
        mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
        mFaceDownDetector = new FaceDownDetector(this::onFlip);
        mScreenUndimDetector = new ScreenUndimDetector();
        
        //跟踪电池消耗率
        mBatterySavingStats = new BatterySavingStats(mLock);
        mBatterySaverPolicy =
                mInjector.createBatterySaverPolicy(mLock, mContext, mBatterySavingStats);
        //负责省电模式转换
        mBatterySaverController = mInjector.createBatterySaverController(mLock, mContext,
                mBatterySaverPolicy, mBatterySavingStats);
        mBatterySaverStateMachine = mInjector.createBatterySaverStateMachine(mLock, mContext,
                mBatterySaverController);
        //控制低功耗待机状态
        mLowPowerStandbyController = mInjector.createLowPowerStandbyController(mContext,
                Looper.getMainLooper());
        mInattentiveSleepWarningOverlayController =
                mInjector.createInattentiveSleepWarningController();

        mAppOpsManager = injector.createAppOpsManager(mContext);

        ...
        
        mScreenBrightnessMinimum = min;
        mScreenBrightnessMaximum = max;
        mScreenBrightnessDefault = def;
   
        ....
    
        mWakefulnessRaw = WAKEFULNESS_AWAKE;
        ...
        
        mNativeWrapper.nativeInit(this);
        mNativeWrapper.nativeSetAutoSuspend(false);
        mNativeWrapper.nativeSetPowerMode(Mode.INTERACTIVE, true);
        mNativeWrapper.nativeSetPowerMode(Mode.DOUBLE_TAP_TO_WAKE, false);
        mInjector.invalidateIsInteractiveCaches();
        }
    }

PowerManagerService构造方法中,主要了一下事:

1.创建Handler,异步执行一些电源管理操作。比如用户活动超时以更新电源状态,等等

2.创建各种控制管理类,比如跟踪省电消耗,控制低功耗待机状态,息屏控制,app耗电控制等。这些类创建好后被PowerManagerService中的变量引用,用于后面的具体电源管理操作。

3.屏幕亮度初始值,最小,最大,默认值。

4.调用nativeInit方法,这个是个native方法,实现逻辑在PowerManagerService.cpp中的nativeInit函数,用来装载Power模块并将模块初始化。

创建完成PowerManagerService后。执行onStart()方法

java 复制代码
    @Override
    public void onStart() {
        publishBinderService(Context.POWER_SERVICE, mBinderService, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_DEFAULT | DUMP_FLAG_PRIORITY_CRITICAL);
        publishLocalService(PowerManagerInternal.class, mLocalService);

        Watchdog.getInstance().addMonitor(this);
        Watchdog.getInstance().addThread(mHandler);
    }

onStart()方法只是注册了mBinderService和mLocalService两个服务,用于其他的服务,app以及只有系统进程可以访问的服务。

当系统服务已经准备好时,需要执行一些系统服务的初始化动作,这时PowerManagerService的systemReady方法会被调用。

systemReady的主要工作:

1.通过getLocalService方法,获取一些在系统进程中注册的本地服务引用。如屏幕保护管理服务,屏幕显示设备管理服务,电池管理服务,等等。

2.创建Notifier,Notifier类的作用是,对重要的电源状态的改变发送广播。

3.注册设置的监听,当设置有变化时处理。如屏保,息屏显示,睡眠时间,屏幕亮度调整模式等等。

4.注册系统其他组件的广播。如电池电量变化,用户切换等

在架构图中,我们还可以看到PowerManager这类。它为android的应用提供接口。应用程序不直接使用PowerManagerService服务来管理电源。而是通过PowerManager提供的接口来管理。

三 BatteryService电池管理

BatteryService服务,用于监控电池充电状态,电池电量,当电量发生变化时,它会发出ACTION_BATTERY_CHANGED广播。它也是系统核心服务,在SystemServer中启动。而在PowerManagerService服务中的systemReady方法中就注册了这个广播,当接收到ACTION_BATTERY_CHANGED广播时,会调用BatteryReceiver这个类进行处理。我们来看一下BatteryService的构造方法代码:

java 复制代码
    public BatteryService(Context context) {
        super(context);

        mContext = context;
        mHandler = new Handler(true /*async*/);
        mLed = new Led(context, getLocalService(LightsManager.class));
        mBatteryStats = BatteryStatsService.getService();
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);

        //表示电量不足时的值,低于这个值系统将关闭
        mCriticalBatteryLevel = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
        //表示电量不足时的值,低于这个值系统将发出警告
        mLowBatteryWarningLevel = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lowBatteryWarningLevel);
        //表示停止电量不足警告的值。电量高于这个值后系统将停止电量不足的警告
        mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
                com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
        //表示电池温度太高的值,高于这个温度系统将关机。
        mShutdownBatteryTemperature = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_shutdownBatteryTemperature);

        mBatteryLevelsEventQueue = new ArrayDeque<>();
        mMetricsLogger = new MetricsLogger();

        // watch for invalid charger messages if the invalid_charger switch exists
        //监听设备插入了无效充电器的事件。事件发生时调用onUEvent方法。
        if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
            UEventObserver invalidChargerObserver = new UEventObserver() {
                @Override
                public void onUEvent(UEvent event) {
                    final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
                    synchronized (mLock) {
                        if (mInvalidCharger != invalidCharger) {
                            mInvalidCharger = invalidCharger;
                        }
                    }
                }
            };
            invalidChargerObserver.startObserving(
                    "DEVPATH=/devices/virtual/switch/invalid_charger");
        }

        mBatteryInputSuspended = PowerProperties.battery_input_suspended().orElse(false);
    }

构造函数主要做了以下工作:

1.获取BatteryStatsService服务。

2.获取设置的低电量的阀值,当电量达到这些值时,系统会根据这些值做相应的动作。

3.获取高的温度阀值。

4.监听设备插入了无效充电器的事件。

同样SystemServer进程创建BatteryService服务后,会调用其onStart方法:

java 复制代码
    @Override
    public void onStart() {
        registerHealthCallback();

        mBinderService = new BinderService();
        publishBinderService("battery", mBinderService);
        mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
        publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
        publishLocalService(BatteryManagerInternal.class, new LocalService());
    }
    
//registerHealthCallback
private void registerHealthCallback() {
    traceBegin("HealthInitWrapper");
    // IHealth is lazily retrieved.
    try {
        mHealthServiceWrapper = HealthServiceWrapper.create(this::update);
    } catch (RemoteException ex) {
        Slog.e(TAG, "health: cannot register callback. (RemoteException)");
        throw ex.rethrowFromSystemServer();
    } catch (NoSuchElementException ex) {
        Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
        throw ex;
    } finally {
        traceEnd();
    }
}

在onStart方法中,注册一些服务,让其他模块可以访问这些服务。其中registerHealthCallback方法是向HealthService服务中注册回调。有回调时,调用BatteryService的update方法。

电量的改变是如何通知到BatteryService服务的?首先我们要了解一下Health AIDL HAL服务

四 Health AIDL HAL

Health AIDL HAL:Android 系统架构图:

Health AIDL HAL实际上是使用AIDL 实现 HAL功能。AIDL语言大家都比较熟悉,是在android中实现进程通信的一套框架,android系统一直在使用这套框架实现进程通信,是一套比较成熟的框架。也就是Health AIDL HAL服务使用了AIDL实现,使得其他进程访问电池等硬件信息时就像使用AIDL调用其他进程的接口一样方便。

下面我们看看Health AIDL HAL的具体实现。

在hardware/interfaces/health/aidl/default这个文件夹下的相关文件是health的默认实现。 首先看看main.cpp中的main方法。

c 复制代码
int main(int argc, char** argv) {
    ...   
    // make a default health service
    auto config = std::make_unique<healthd_config>(); 
    ::android::hardware::health::InitHealthdConfig(config.get()); //初始化healthd_config配置
    
    // gInstanceName 的值是"default"
    auto binder = ndk::SharedRefBase::make<Health>(gInstanceName, std::move(config)); // 创建Health binder服务

    if (argc >= 2 && argv[1] == gChargerArg) {
    #if !CHARGER_FORCE_NO_UI
        // If charger shouldn't have UI for your device, simply drop the line below
        // for your service implementation. This corresponds to
        // ro.charger.no_ui=true
        return ChargerModeMain(binder, std::make_shared<ChargerCallback>(binder));
#endif

        LOG(INFO) << "Starting charger mode without UI.";
    } else {
        LOG(INFO) << "Starting health HAL.";
    }

    auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder); //创建HalHealthLoop 实例
    return hal_health_loop->StartLoop(); // 调用StartLoop() 函数
}

main函数首先初始化healthd_config,创建Health binder服务,创建HalHealthLoop实例,并调用StartLoop()函数进入无限循环,如果没有错误的话该方法不会退出。下面我们逐步来看看里面的代码实现。

1. 创建Health binder服务

auto binder = ndk::SharedRefBase::make(gInstanceName, std::move(config));该语句实际是调Health构造函数创建对象。

c 复制代码
//Health构造函数
Health::Health(std::string_view instance_name, std::unique_ptr<struct healthd_config>&& config)
    : instance_name_(instance_name),
      healthd_config_(std::move(config)),
      death_recipient_(AIBinder_DeathRecipient_new(&OnCallbackDiedWrapped)) {
    battery_monitor_.init(healthd_config_.get());
}

其中battery_monitor_.init(healthd_config_.get())用来初始化battery_monitor_对象中的相关数据。battery_monitor_是BatteryMonitor类的用于管理监控电池信息的类,它的init方法中初始化了healthd_config配置数据

c 复制代码
void BatteryMonitor::init(struct healthd_config *hc) {
    String8 path;
    char pval[PROPERTY_VALUE_MAX];

    mHealthdConfig = hc;
    //打开POWER_SUPPLY_SYSFS_PATH 文件夹
    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
    if (dir == NULL) {
        KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
    } else {
        struct dirent* entry;

        while ((entry = readdir(dir.get()))) {
            const char* name = entry->d_name;
            //跳过当前文件夹和父文件夹
            if (!strcmp(name, ".") || !strcmp(name, ".."))
                continue;

            std::vector<String8>::iterator itIgnoreName =
                    find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
                         String8(name));
            //跳过指定的文件
            if (itIgnoreName != hc->ignorePowerSupplyNames.end())
                continue;

            // Look for "type" file in each subdirectory
            path.clear();
            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
            switch(readPowerSupplyType(path)) {
            case ANDROID_POWER_SUPPLY_TYPE_AC:
            case ANDROID_POWER_SUPPLY_TYPE_USB:
            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
                path.clear();
                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
                break;

            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
                // Some devices expose the battery status of sub-component like
                // stylus. Such a device-scoped battery info needs to be skipped
                // in BatteryMonitor, which is intended to report the status of
                // the battery supplying the power to the whole system.
                if (isScopedPowerSupply(name)) continue;
                mBatteryDevicePresent = true;

                if (mHealthdConfig->batteryStatusPath.empty()) {
                    path.clear();
                    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
                }

                if (mHealthdConfig->batteryHealthPath.empty()) {
                    path.clear();
                    path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
                }

                if (mHealthdConfig->batteryPresentPath.empty()) {
                    path.clear();
                    path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
                }

                if (mHealthdConfig->batteryCapacityPath.empty()) {
                    path.clear();
                    path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
                                      name);
                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
                }
                ....

主要代码说明:

  1. std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);

    POWER_SUPPLY_SYSFS_PATH是宏定义,实际的值是"/sys/class/power_supply"。这句意思是打开POWER_SUPPLY_SYSFS_PATH指向的文件夹,dir的类型是DIR指向目录的指针,最终dir变量即指向了POWER_SUPPLY_SYSFS_PATH指向的文件夹。

  2. while ((entry = readdir(dir.get()))) { const char* name = entry->d_name; 逐个读取dir文件夹中的子文件夹,赋值给entry。直到读取完退出while 循环。将子文件夹entry的名称赋值给name。

  3. path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);

    switch(readPowerSupplyType(path)) {

    case ANDROID_POWER_SUPPLY_TYPE_AC:

    case ANDROID_POWER_SUPPLY_TYPE_USB:

    case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:

    case ANDROID_POWER_SUPPLY_TYPE_DOCK:

    ...

    case ANDROID_POWER_SUPPLY_TYPE_BATTERY:

    根据当前文件夹的名称,生成一个type文件路径。再通过readPowerSupplyType(path)获取系统电源支持类型。不同的type走不同的switch分支。如果的电池则type是ANDROID_POWER_SUPPLY_TYPE_BATTERY。

  4. if (mHealthdConfig->batteryStatusPath.empty()) {

    path.clear();

    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name);

    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path; }

    如果mHealthdConfig->batteryStatusPath变量还没有配置,则进入配置逻辑。根据当前文件夹的名称,获取当前文件夹下的status文件路径。 access(path.c_str(), R_OK) 测试status文件是否可以访问。如果可以则返回0,然后将status的路径path保存到mHealthdConfig->batteryStatusPath中。status事实上是代表的是电池的状态。也就是说电池的状态存放在status中。要知道电池的状态只要读取status的值即可。其他的电池参数比如"voltage_now","capacity","present"也是一样的处理逻辑。

所以这段代码主要将代表电池的相关参数的文件路径保存到healthd_config结构体中,方便后续读取其中的值。

2. 创建HalHealthLoop实例

c 复制代码
    // Caller must ensure that the lifetime of service_ is not shorter than this object.
    HalHealthLoop(std::shared_ptr<IHealth> service, std::shared_ptr<HalHealthLoopCallback> callback)
        : service_(std::move(service)), callback_(std::move(callback)) {}

这个比较简单,初始化service_和callback_ ,根据main中的代码可以看到传入的都是binder,即Health对象。所以service_和callback_指向的都是Health对象

3. StartLoop()函数

该方法主要是做一些初始化工作,然后进入循环等待事件处理。

c 复制代码
int HealthLoop::StartLoop() {
    int ret;

    klog_set_level(KLOG_LEVEL);
    //初始化工作
    ret = InitInternal();
    if (ret) {
        KLOG_ERROR(LOG_TAG, "Initialization failed, exiting\n");
        return 2;
    }
    //进入无限循环
    MainLoop();
    KLOG_ERROR(LOG_TAG, "Main loop terminated, exiting\n");
    return 3;
}

//InitInternal函数
int HealthLoop::InitInternal() {
    //创建epoll监听epollfd_
    epollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
    if (epollfd_ == -1) {
        KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);
        return -1;
    }

    // Call subclass's init for any additional init steps.
    // Note that healthd_config_ is initialized before wakealarm_fd_; see
    // AdjustUeventWakealarmPeriods().
    //在子类中实际初始化
    Init(&healthd_config_);
    //定时器初始化
    WakeAlarmInit();
    //Uevent初始化
    UeventInit();

    return 0;
}

StartLoop函数调用InitInternal() 做一些初始化工作,再调用MainLoop()函数进入等待事件的循环。再看看InitInternal()函数。调用epoll_create1系统函数,创建epoll监听对象 epollfd_,在MainLoop()函数中监听的就是这个对象。再调用3个初始化方法:Init(&healthd_config_),这个函数是在子类中实现,这里实际是HalHealthLoop类。然后是定时器初始化WakeAlarmInit(),再Uevent事件初始化UeventInit()。下面逐步看3个初始化函数的源码实现。首先是Init代码:

c 复制代码
//Init 函数
void HalHealthLoop::Init(struct healthd_config* config) {
    callback_->OnInit(this, config);
}

//callback_->OnInit 函数
void Health::OnInit(HalHealthLoop* hal_health_loop, struct healthd_config* config) {
    LOG(INFO) << instance_name_ << " instance initializing with healthd_config...";

    // Similar to HIDL's android::hardware::health::V2_1::implementation::HalHealthLoop::Init,
    // copy configuration parameters to |config| for HealthLoop (e.g. uevent / wake alarm periods)
    *config = *healthd_config_.get();

    binder_status_t status = ABinderProcess_setupPolling(&binder_fd_);

    if (status == ::STATUS_OK && binder_fd_ >= 0) {
        std::shared_ptr<Health> thiz = ref<Health>();
        auto binder_event = [thiz](auto*, uint32_t epevents) { thiz->BinderEvent(epevents); };
        if (hal_health_loop->RegisterEvent(binder_fd_, binder_event, EVENT_NO_WAKEUP_FD) != 0) {
            PLOG(ERROR) << instance_name_ << " instance: Register for binder events failed";
        }
    }

    std::string health_name = IHealth::descriptor + "/"s + instance_name_;
    //向ServiceManager注册binder服务
    CHECK_EQ(STATUS_OK, AServiceManager_addService(this->asBinder().get(), health_name.c_str()))
            << instance_name_ << ": Failed to register HAL";

    LOG(INFO) << instance_name_ << ": Hal init done";
}

Init的实现是在子类中,这里我们子类是HalHealthLoop,它的Init函数只是调用了callback_->OnInit()。在上面HalHealthLoop构造方法中已经知道callback_其实是传入的Health对象。再看看Health::OnInit函数,它做了2个重要的事,第一个调用hal_health_loop->RegisterEvent函数,注册监听变量binder_fd_,binder_fd_是当前进程binder轮询机制的文件描述符。 第2个是调用AServiceManager_addService来将Health注册为binder服务。也就是说,在其他进程中可以通过获取服务代理和Health进行进程通信。BatteryService中正是利用这种方法来获取电池数据的更新。

WakeAlarmInit代码:

c 复制代码
void HealthLoop::WakeAlarmInit(void) {
    wakealarm_fd_.reset(timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK));
    if (wakealarm_fd_ == -1) {
        KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");
        return;
    }

    if (RegisterEvent(wakealarm_fd_, &HealthLoop::WakeAlarmEvent, EVENT_WAKEUP_FD))
        KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");

    WakeAlarmSetInterval(healthd_config_.periodic_chores_interval_fast);
}

首先使用timerfd_create创建一个定时器文件描述符wakealarm_fd_, 并将其注册到epoll中监听变化。然后调用WakeAlarmSetInterval设置定时器间隔。healthd_config_.periodic_chores_interval_fast的值是在宏DEFAULT_PERIODIC_CHORES_INTERVAL_FAST 中定义,它的值是60s。

UeventInit代码:

c 复制代码
void HealthLoop::UeventInit(void) {
    uevent_fd_.reset(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 (RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD))
        KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
}

uevent_open_socket(64 * 1024, true): 打开uevent套接字,设置缓冲区大小为64*1024字节,以及设置为非阻塞模式。

fcntl(uevent_fd_, F_SETFL, O_NONBLOCK):设置文件描述符uevent_fd_为非阻塞式。

RegisterEvent(uevent_fd_, &HealthLoop::UeventEvent, EVENT_WAKEUP_FD)注册epoll事件监听,当uevent_fd_有变化时,回调HealthLoop::UeventEvent。

uevent 套接字通常用于监听设备的事件通知。当硬件的状态发生变化,如设备的插入拔出,状态变化等,内核会向uevent套接字发送通知。应用监听到uevent事件就可以对变化做相应的处理。

如果在进入UeventEvent回调函数里查看会发现当事件的信息为"SUBSYSTEM=power_supply"时,会调用ScheduleBatteryUpdate()函数,来更新电池信息。所以这里应该监听处理的是电源的状态变化。

总体来说3个初始化方法 Init,WakeAlarmInit,UeventInit,为binder_fd_,wakealarm_fd_以及uevent_fd_这3个变量设置了监听,当它们发生变化时,处理对应的回调方法。而具体监听处理逻辑正在是MainLoop函数中,下面看看其代码实现:

c 复制代码
void HealthLoop::MainLoop(void) {
    int nevents = 0;
    while (1) {
        reject_event_register_ = true;
        size_t eventct = event_handlers_.size();
        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) PeriodicChores();

        Heartbeat();

        mode_timeout = 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) {
                auto* event_handler = reinterpret_cast<EventHandler*>(events[n].data.ptr);
                event_handler->func(event_handler->object, events[n].events);
            }
        }
    }

    return;
}

while (1) 代表代码进入死循环,如果没有异常,循环不会推出。

nevents = epoll_wait(epollfd_, events, eventct, timeout): 调用epoll_wait等待epollfd_监听是事件到来,如果监听的文件描述符有变化,则调用之前注册的events回调。epollfd_监听的文件描述符就是前面3个初始化方法中注册的binder_fd_,wakealarm_fd_以及uevent_fd_。

for (int n = 0; n < nevents; ++n):监听的数据有变化,则处理变化的回调。通过events[n].data.ptr获取之前注册的回调函数。event_handler->func(event_handler->object, events[n].events),这段是执行回调函数。wakealarm_fd_对应的是HealthLoop::WakeAlarmEvent。binder_fd_对应的是binder_event, uevent_fd_对应的是ealthLoop::UeventEvent。这些在前面的初始化代码中可以找到。 PS:Heartbeat()和PrepareToWait() 没有找到对应在Health类中的具体实现,可能是在这里没有实现。有了解的可以评论区讨论。

这里我们基本可以判断出,电池信息的获取和分发是在注册的回调函数中处理。当定时事件到达或者电池状态改变时,会调用注册的回调函数。而在回调中都会调用到ScheduleBatteryUpdate函数。我们再来看看在这个函数中如何获取电池信息的。

4.ScheduleBatteryUpdate函数

c 复制代码
void HalHealthLoop::ScheduleBatteryUpdate() {
    // ignore errors. impl may not be able to handle any callbacks, so
    // update() may return errors.
    if (auto res = service_->update(); !res.isOk()) {
        LOG(WARNING) << "update() on the health HAL implementation failed with "
                     << res.getDescription();
    }

    HealthInfo health_info;
    auto res = service_->getHealthInfo(&health_info);
    CHECK(res.isOk()) << "getHealthInfo() on the health HAL implementation failed with "
                      << res.getDescription();
    OnHealthInfoChanged(health_info);
}

//Health::update函数
ndk::ScopedAStatus Health::update() {
    HealthInfo health_info;
    if (auto res = getHealthInfo(&health_info); !res.isOk()) {
        LOG(DEBUG) << "Cannot call getHealthInfo for update(): " << res.getDescription();
        // Propagate service specific errors. If there's none, report unknown error.
        if (res.getServiceSpecificError() != 0 ||
            res.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
            return res;
        }
        return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
                IHealth::STATUS_UNKNOWN, res.getDescription().c_str());
    }
    battery_monitor_.logValues();
    OnHealthInfoChanged(health_info);
    return ndk::ScopedAStatus::ok();
}

if (auto res = service_->update(); !res.isOk()) : service_ 是Health类的实例对象。update()函数里更新了电池信息,如果没有异常返回ok,有则返回指定的错误。在这段代码里如果有异常只是打印了log,不管有没有异常都会走到下面service_->getHealthInfo(&health_info)来获取电池信息。看一下getHealthInfo的代码。

c 复制代码
ndk::ScopedAStatus Health::getHealthInfo(HealthInfo* out) {

    battery_monitor_.updateValues();

    *out = battery_monitor_.getHealthInfo();

    ...

    return ndk::ScopedAStatus::ok();
}

//battery_monitor_.updateValues 函数
void BatteryMonitor::updateValues(void) {
    //初始化HealthInfo对象,设置一些基本的值
    initHealthInfo(mHealthInfo.get());

    //根据文件路径获取对应的值
    if (!mHealthdConfig->batteryPresentPath.empty())
        mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
    else
        mHealthInfo->batteryPresent = mBatteryDevicePresent;

    mHealthInfo->batteryLevel = mBatteryFixedCapacity
                                        ? mBatteryFixedCapacity
                                        : getIntField(mHealthdConfig->batteryCapacityPath);
    mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;

    if (!mHealthdConfig->batteryCurrentNowPath.empty())
        mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);

    if (!mHealthdConfig->batteryFullChargePath.empty())
        mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);

    if (!mHealthdConfig->batteryCycleCountPath.empty())
        mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);

    if (!mHealthdConfig->batteryChargeCounterPath.empty())
        mHealthInfo->batteryChargeCounterUah =
                getIntField(mHealthdConfig->batteryChargeCounterPath);

    if (!mHealthdConfig->batteryCurrentAvgPath.empty())
        mHealthInfo->batteryCurrentAverageMicroamps =
                getIntField(mHealthdConfig->batteryCurrentAvgPath);
    ...
    
}

battery_monitor_.updateValues(): battery_monitor_ 监控电池状态的类的实例。 在上面的代码中,我们介绍过battery_monitor_.init这个方法,主要是将代表电池各种状态的各种文件路径保存在mHealthdConfig对象中。而这里的battery_monitor_.updateValues()方法,则是按照保存的文件路径,读取文件的值,即电池的状态,然后保存在mHealthInfo对象中。BatteryMonitor::updateValues 里的代码相对简单,基本都是读取文件值的操作,读取文件的值后,保存到mHealthInfo对象对应的变量中。

*out = battery_monitor_.getHealthInfo(): 该方法只是简单的返回mHealthInfo对象,将其赋值给out指针变量。这样在调用完getHealthInfo(&health_info)函数后,外部传入的HealthInfo类型的变量health_info,就指向了保存有最新的电池状态信息的对象。获取到电池信息后接下来就是调用OnHealthInfoChanged(health_info)函数,将信息通知到需要更新的地方。

具体信息通知到哪里了?看看OnHealthInfoChanged代码中能不能找到答案。

c 复制代码
void HalHealthLoop::OnHealthInfoChanged(const HealthInfo& health_info) {
    //callback_为Health类的对象
    callback_->OnHealthInfoChanged(health_info);
    set_charger_online(health_info);
    AdjustWakealarmPeriods(charger_online());
}

//callback_->OnHealthInfoChanged 函数实现
void Health::OnHealthInfoChanged(const HealthInfo& health_info) {
    // Notify all callbacks
    //获得锁
    std::unique_lock<decltype(callbacks_lock_)> lock(callbacks_lock_);
    // is_dead notifies a callback and return true if it is dead.
    auto is_dead = [&](const auto& linked) {
        //分发消息
        auto res = linked->callback()->healthInfoChanged(health_info);
        return IsDeadObjectLogged(res);
    };
    auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead);
    
    //删除dead的callback
    callbacks_.erase(it, callbacks_.end());  // calls unlinkToDeath on deleted callbacks.
    //释放锁
    lock.unlock();

    // Let HalHealthLoop::OnHealthInfoChanged() adjusts uevent / wakealarm periods
}

OnHealthInfoChanged调用了callback_->OnHealthInfoChanged(health_info),即调用了callback_对象即是Health类的OnHealthInfoChanged方法。

auto it = std::remove_if(callbacks_.begin(), callbacks_.end(), is_dead):对callbacks_数组进行遍历,从callbacks_.begin()开始,到callbacks_.end()结束,移除满足is_dead返回条件的元素。is_dead是个函数,用于检查是否满足条件,其返回true代码满足条件。在这里is_dead的作用是,当迭代callbacks_过程中会调用is_dead函数,is_dead函数调用linked->callback()->healthInfoChanged(health_info)把信息分发出去。如果linked->callback()->healthInfoChanged(health_info)返回的res的状态是STATUS_DEAD_OBJECT表明该callback是"dead"的,则is_dead函数返回true。这样后面callbacks_.erase会将"dead"的callback清除。"dead"的callback,可能是需要接收信息的进程不再运行。所以is_dead函数干了2件事,一个是调用linked->callback()->healthInfoChanged(health_info)发送信息,另一个是,标记不需要分发的元素,用于后面的清除工作。

这里又有个疑问,这个callbacks_集合是什么?里面的元素是什么?消息被分发到哪里去了?在源码中可以看到它的定义是: std::vector<std::unique_ptr> callbacks_。这是一个元素类型为LinkedCallback的集合。而它的元素的添加是在Health类的方法Health::registerCallback中实现。具体看一下元素的添加过程:

c 复制代码
ndk::ScopedAStatus Health::registerCallback(const std::shared_ptr<IHealthInfoCallback>& callback) {
    if (callback == nullptr) {
        // For now, this shouldn't happen because argument is not nullable.
        return ndk::ScopedAStatus::fromExceptionCode(EX_NULL_POINTER);
    }

    {
        std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);
        auto linked_callback_result = LinkedCallback::Make(ref<Health>(), callback);
        if (!linked_callback_result.ok()) {
            return ndk::ScopedAStatus::fromStatus(-linked_callback_result.error().code());
        }
        callbacks_.emplace_back(std::move(*linked_callback_result));
        // unlock
    }

    ...
    
    return ndk::ScopedAStatus::ok();
}

auto linked_callback_result = LinkedCallback::Make(ref(), callback):创建LinkedCallback对象,并赋给linked_callback_result变量。第2个callback参数为调用registerCallback时传入的值,类型为std::shared_ptr&。它被保存在LinkedCallback对象中的callback_变量中。

callbacks_.emplace_back(std::move(*linked_callback_result)): 创建好LinkedCallback对象后将它添加到callbacks_集合的尾部。

这里就解答了上面的疑问,callbacks_是LinkedCallback类的容器,添加在callbacks_中的元素是LinkedCallback实例对象。当函数is_dead中调用linked->callback()->healthInfoChanged(health_info)时,实际是调用的std::shared_ptr& callback的healthInfoChanged方法。也就是信息被发送到了registerCallback入参,IHealthInfoCallback:callback中去了。新的问题出现了。谁调用了registerCallback, std::shared_ptr& callback这个入参传入的实例在哪里?

这里我们必须回到上的第三节 "BatteryService电池管理" 中BatteryService类的 registerHealthCallback方法中找答案。忘记的可以回去再看看。

其中有个语句:mHealthServiceWrapper = HealthServiceWrapper.create(this::update)。 我看看这个create代码:

java 复制代码
public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback)
        throws RemoteException, NoSuchElementException {
    return create(
            healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback),
            new HealthServiceWrapperAidl.ServiceManagerStub() {},
            healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback),
            new HealthServiceWrapperHidl.IServiceManagerSupplier() {},
            new HealthServiceWrapperHidl.IHealthSupplier() {});
}
    
    
@VisibleForTesting
static @NonNull HealthServiceWrapper create(
        @Nullable HealthRegCallbackAidl aidlRegCallback,
        @NonNull HealthServiceWrapperAidl.ServiceManagerStub aidlServiceManager,
        @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback,
        @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier,
        @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier)
        throws RemoteException, NoSuchElementException {
    try {
        return new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager);
    } catch (NoSuchElementException e) {
        // Ignore, try HIDL
    }
    return new HealthServiceWrapperHidl(
            hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier);
}

一个参数的create方法调用了另一个5个参数的create方法,而5个参数的create方法,在正常情况下是AIDL实现,所以返回new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager)。所以这个aidlRegCallback实例是new HealthRegCallbackAidl(healthInfoCallback)。

首先看看HealthServiceWrapperAidl构造函数:

java 复制代码
    HealthServiceWrapperAidl(
            @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager)
            throws RemoteException, NoSuchElementException {

        traceBegin("HealthInitGetServiceAidl");
        IHealth newService;
        try {
            //SERVICE_NAME的定义是 @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default";
            newService = serviceManager.waitForDeclaredService(SERVICE_NAME);
        } finally {
            traceEnd();
        }
        if (newService == null) {
            throw new NoSuchElementException(
                    "IHealth service instance isn't available. Perhaps no permission?");
        }
        mLastService.set(newService);
        mRegCallback = regCallback;
        if (mRegCallback != null) {
            mRegCallback.onRegistration(null /* oldService */, newService);
        }

        traceBegin("HealthInitRegisterNotificationAidl");
        mHandlerThread.start();
        try {
            serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback);
        } finally {
            traceEnd();
        }
        Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL");
    }

serviceManager.waitForDeclaredService(SERVICE_NAME): 获取名为SERVICE_NAME的服务。SERVICE_NAME的值是IHealth.DESCRIPTOR + "/default",这个正是上面介绍初始化方法Health::OnInit中的,注册Health binder服务的名字。也就是在这里获取的newService正式Health binder服务。而newService的类型也正是IHealth。

mRegCallback.onRegistration(null /* oldService */, newService):获取newService后,调用此方法注册callback,看一下里面代码:

java 复制代码
public void onRegistration(@Nullable IHealth oldService, @NonNull IHealth newService) {
    if (mServiceInfoCallback == null) return;

    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthUnregisterCallbackAidl");
    try {
        unregisterCallback(oldService, mHalInfoCallback);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
    }

    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthRegisterCallbackAidl");
    try {
        registerCallback(newService, mHalInfoCallback);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
    }
}

//registerCallback(newService, mHalInfoCallback)方法
private static void registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) {
    try {
        newService.registerCallback(cb);
    } catch (RemoteException e) {
        Slog.e(
                TAG,
                "health: cannot register callback, framework may cease to"
                        + " receive updates on health / battery info!",
                e);
        return;
    }
    // registerCallback does NOT guarantee that update is called immediately, so request a
    // manual update here.
    try {
        newService.update();
    } catch (RemoteException e) {
        Slog.e(TAG, "health: cannot update after registering health info callback", e);
    }
}

在onRegistration中调用了registerCallback(newService, mHalInfoCallback);方法。其中 是mHalInfoCallback定义是:private final IHealthInfoCallback mHalInfoCallback = new HalInfoCallback();这个mHalInfoCallback在声明变量是被赋值,并定义为final 常量。

registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) 方法中调用了newService.registerCallback(cb), 这时就清楚了,Health服务的registerCallback方法是在这里调用的。在这里将IHealthInfoCallback 实例对象cb注册到服务中。

当电池状态更新时,最终回调的是cb的healthInfoChanged方法。

java 复制代码
private class HalInfoCallback extends IHealthInfoCallback.Stub {
    @Override
    public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
        mServiceInfoCallback.update(healthInfo);
    }
    @Override
    public String getInterfaceHash() {
        return IHealthInfoCallback.HASH;
    }
    @Override
    public int getInterfaceVersion() {
        return IHealthInfoCallback.VERSION;
    }
}

cb的healthInfoChanged方法中直接调用:mServiceInfoCallback.update(healthInfo); 而mServiceInfoCallback则是在上面HealthServiceWrapper类create方法中的 new HealthRegCallbackAidl(healthInfoCallback) 传入的参数healthInfoCallback,而这个参数对应的是mHealthServiceWrapper = HealthServiceWrapper.create(this::update)语句的参数this::update。

也就是说最终调用到了BatteryService类的update方法。在这个update方法中,接收Health服务传来的最新的电池信息android.hardware.health.HealthInfo info,然后做相应的处理。到这里就终于搞清了最开始提的,电量的改变是如何通知到BatteryService服务的,这个问题。

回调层层调用容易搞晕,画个图总结一下:

备注:有迷糊的地方欢迎提意见,另外在文章的写法上有什么好的建议也欢迎留言。

源码路径

PowerManagerService:frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

PowerManagerService.cpp: frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

SystemServer: frameworks/base/services/java/com/android/server/SystemServer.java

BatteryService.java: frameworks/base/services/core/java/com/android/server/BatteryService.java

main.cpp:hardware/interfaces/health/aidl/default/main.cpp HalHealthLoop.cpp : hardware/interfaces/health/aidl/default/HalHealthLoop.cpp

Health.cpp:hardware/interfaces/health/aidl/default/Health.cpp

LinkedCallback.cpp: hardware/interfaces/health/aidl/default/LinkedCallback.cpp

参考资料

《深入解析Android 5.0 系统》

Power Management:wladimir-tm4pda.github.io/porting/pow...

Android Power and Battery Management: A Look Underneath The Hood: khoshgozaran.com/android-pow...

Health2.0: source.android.com/docs/core/p...

Health AIDL HAL:android.googlesource.com/platform/ha...

相关推荐
工业互联网专业3 天前
Python毕业设计选题:基于python的豆瓣电影数据分析可视化系统-flask+spider
vue.js·python·数据分析·flask·毕业设计·源码·课程设计
工业互联网专业5 天前
Python毕业设计选题:基于Spark的国漫推荐系统的设计与实现-django+spider
vue.js·python·spark·django·毕业设计·源码·课程设计
工业互联网专业7 天前
Python毕业设计选题:基于django+vue的二手物品交易系统
vue.js·python·django·毕业设计·源码·课程设计
工业互联网专业7 天前
Python毕业设计选题:基于BS架构的在线学习与推荐系统的设计与实现-django
vue.js·python·django·毕业设计·源码·课程设计
xcLeigh8 天前
VUE3实现简洁的特色美食网站源码
前端·源码·vue3
程序猿麦小七9 天前
今天给在家介绍一篇基于jsp的旅游网站设计与实现
java·源码·旅游·景区·酒店
工业互联网专业9 天前
Python毕业设计选题:基于Django+uniapp的公司订餐系统小程序
vue.js·python·小程序·django·uni-app·源码·课程设计
程序员小海绵【vincewm】9 天前
【设计模式】结合Tomcat源码,分析外观模式/门面模式的特性和应用场景
设计模式·tomcat·源码·外观模式·1024程序员节·门面模式
工业互联网专业10 天前
Python毕业设计选题:基于django+vue的仓库管理系统设计
vue.js·python·django·毕业设计·源码·课程设计
北巷!!12 天前
SpringSecurity源码中核心类
源码·springsecurity