[Android Framework] 8.1 Battery系列(二) BatteryStatsService分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FightFightFight/article/details/82384336

概述

BatteryStatsService负责电池信息的收集,以及各个部分耗电量的统计,它继承于IBatteryStats.Stub,因此可以通过Binder机制和其他组件进行交互。在上一节中分析过BatteryService,它负责监听从底层传上来的电量信息,当有数据时,BatteryService会将数据传给BatteryStatsService,BatteryStatsService中所有功能最终又委托给BatteryStatsImpl去处理。

和BatteryService不同的是,BatteryStatsService并非继承于SystemService,因此不是一个SystemService类,不具有SystemService的生命周期方法。

BatteryStatsService的启动、初始化和发布都是在AMS中进行,当SystemServer启动AMS后,AMS在其构造方法中启动了BSS,同时AMS在后续调用中进行了BSS的注册,下面一一进行分析!

1.实例化

在AMS中,获取BatteryStatsService实例方式如下:

public ActivityManagerService(Context systemContext) {
    ......
    // TODO: Move creation of battery stats service outside of activity manager service.
    File dataDir = Environment.getDataDirectory();
    File systemDir = new File(dataDir, "system");
    //创建/data/system目录
    systemDir.mkdirs();
    //实例化BatteryStatsService
    mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
    //从/data/system/batterystas-daily.xml中读取信息
    mBatteryStatsService.getActiveStatistics().readLocked();
    //使用ExecutorService线程池将BatteryStatsImpl中统计的电池信息写入硬盘中 
    mBatteryStatsService.scheduleWriteToDisk();
    mOnBattery = DEBUG_POWER ? true
            : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
    //设置BatteryCallback接口
    mBatteryStatsService.getActiveStatistics().setCallback(this);
}

setCallback()方法设置了BatteryCallback回调,该接口如下:

public interface BatteryCallback {
    public void batteryNeedsCpuUpdate();
    public void batteryPowerChanged(boolean onBattery);
    public void batterySendBroadcast(Intent intent);
}

该接口负责当对应条件符合时,在Handler中调用此方法通知AMS,从而在AMS中进行回调这三个方法在AMS中的实现.

1.1.BatteryStatsService.Constructor()

按照上面的调用顺序,我们先看BatteryStatsService的构造方法:

BatteryStatsService(Context context, File systemDir, Handler handler) {
    //这里传入来自AMS中的Handler,并通过handler.getLooper()获取到AMS中的Looper
    mContext = context;
    //BSI中一个内部类,可以通过UserManagerInternal获取UserId
    mUserManagerUserInfoProvider = new BatteryStatsImpl.UserInfoProvider() {
        private UserManagerInternal umi;
        @Override
        public int[] getUserIds() {
            if (umi == null) {
                umi = LocalServices.getService(UserManagerInternal.class);
            }
            return (umi != null) ? umi.getUserIds() : null;
        }
    };
    //实例化BatteryStatsImpl
    mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider);
    //BatteryExternalStatsWorker负责在专用线程上同步BT/WIFI等外部电池信息到BSI中
    mWorker = new BatteryExternalStatsWorker(context, mStats);
    mStats.setExternalStatsSyncLocked(mWorker);
    //设置扫描信号超时时间
    mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
            com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
    //设置电源配置文件
    mStats.setPowerProfileLocked(new PowerProfile(context));
}

在BatteryStatsService的构造方法中,首先实例化了BatteryStatsImpl(后简称BSI)对象和BatteryExternalStatsWorker对象,BatteryStatsImpl是BSS的委托类,BSS的所有操作都交给它去处理;BatteryExternalStatsWorker负责在专用线程上拉取如蓝牙芯片、WIFI控制器、Modem控制器等外部设备电源使用信息。

由于BatteryStatsService中所有任务都交给BatteryStatsImpl去处理,所以继续从BatteryStatsImpl的构造方法进入分析,其构造方法如下:

/**
 * @param clocks 包括系统的uptime时长的elapsedrealtime时长
 * @param systemDir AMS中传入的/data/system
 * @param handler AMS中传入的handler
 * @param cb 用于读取低电量状态的接口,BSS实现了该接口
 * @param userInfoProvider 通过UserManagerService获取UserId,BSS中实例化了该接口的匿名类
 */
private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
        PlatformIdleStateCallback cb,
        UserInfoProvider userInfoProvider) {
    init(clocks);
    //在/data/system目录下创建文件
    if (systemDir != null) {
        mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
                new File(systemDir, "batterystats.bin.tmp"));
    } else {
        mFile = null;
    }
    mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
    mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
    mHandler = new MyHandler(handler.getLooper());
    //创建一系列的StopwatchTimer和LongSamplingCounter,并将这些对象全部
    //添加到TimeBaseObserver集合中
    mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, 
      mOnBatteryTimeBase);
    mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, 
      mOnBatteryTimeBase);
     .............
    mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, 
       NUM_WIFI_TX_LEVELS);
    mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
            NUM_BT_TX_LEVELS);
    mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
    //创建一系列的LongSamplingCounter
    mMobileRadioActiveAdjustedTime = new 
                  LongSamplingCounter(mOnBatteryTimeBase);
    mMobileRadioActiveUnknownTime = new 
                  LongSamplingCounter(mOnBatteryTimeBase);
    mMobileRadioActiveUnknownCount = new 
                  LongSamplingCounter(mOnBatteryTimeBase);
    mOnBattery = mOnBatteryInternal = false;
    long uptime = mClocks.uptimeMillis() * 1000;
    long realtime = mClocks.elapsedRealtime() * 1000;
    //初始化一些计算电量相关时间值
    initTimes(uptime, realtime);
    mStartPlatformVersion = mEndPlatformVersion = Build.ID;
    mDischargeStartLevel = 0;
    mDischargeUnplugLevel = 0;
    mDischargePlugLevel = -1;
    mDischargeCurrentLevel = 0;
    mCurrentBatteryLevel = 0;
    //初始化放电时参数
    initDischarge();
    //清除历史信息
    clearHistoryLocked();
    //更新下次记录截止时间
    updateDailyDeadlineLocked();
    mPlatformIdleStateCallback = cb;
    mUserInfoProvider = userInfoProvider;
}

在BatteryStatsImpl构造函数中,使用AtomicFile类创建了一个batterystats-daily.xml文件,BatteryStatsImpl每次统计的电量都会写在这个文件中,AtmoicFile是一个进行File操作的帮助类,利用备份文件的操作对文件进行原子操作;此外还实例化了负责记录各个组建时间信息的StopwatchTimer对象和LongSamlingCounter对象。

BatteryStatsImpl的构造函数执行完毕。当实例化BSI完毕后,BSS构造方法中剩余内容就是给BSI设置相关的属性值,在注释中已经给出。不再去分析了。

扫描二维码关注公众号,回复: 3241217 查看本文章

2.发布BatteryStatsService

当AMS实例化完毕BSS,且BSS实例化完毕BSI后,整个实例化过程完毕了,现在回到AMS中,来看看剩余的操作:

//AMS的start()中发布了BSS
private void start() {
......
    mBatteryStatsService.publish();
    ......
}
//BSS中:
public void publish() {
    ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}

这里通过ServiceManager.addService()对BSS进行了发布,这样其他应用可以通过BatteryStats.SERVICE_NAME获取一个BSS的代理对象(IBatteryStats.Stub的代理对象更加确切一点).

这里需要额外说明一点,对于继承于SystemService的系统服务(如PowerManagerService、BatteryService等),提供了publishBinderService()方法进行发布,其内部也是通过ServiceManager.addService()实现。

AMS中执行完start()后,还有一处和BSS相关:

public void initPowerManagement() {
    ......
    mBatteryStatsService.initPowerManagement();
    ......
}

在SystemServer启动AMS后,在startBootStrapService()方法中调用了AMS的initPowerManagerment(),初始化一些和PM相关的东西,AMS又调用了BSS的initPowerManagerment();来看看这个方法中做了什么:

public void initPowerManagement() {
    mStackSupervisor.initPowerManagement();
    mBatteryStatsService.initPowerManagement();//调用BSS中的initPowerManager
    //获取PMS本地对象
    mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
    PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
    //准备申请wakelock锁
    mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
    //wakeloc类型设置为非计数锁,默认计数锁
    mVoiceWakeLock.setReferenceCounted(false);
}

再看看BSS中的initPowerManagement():

public void initPowerManagement() {
    //获取PMS本地服务对象
    final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
    //注册低电量监听
    powerMgr.registerLowPowerModeObserver(this);
    synchronized (mStats) {
   //省电模式相关
        mStats.notePowerSaveModeLocked(
                powerMgr.getLowPowerState(ServiceType.BATTERY_STATS)
                        .batterySaverEnabled);
    }
    //开启一个线程
    (new WakeupReasonThread()).start();
}

在这个方法中,首先会获取PowerManagerService的本地服务对象。PowerManagerService.LocalService继承自PowerManagerInternal,并且在PMS中注册本地服务时将PowerManagerInternal.class作为参数,因此这里实际上得到的是PowerManagerService.LocalService对象。

获取到PMS.LocalService后,使用PMS.LocalService对象注册低电量时的监听接口PowerManagerInternal.LowPowerModeListener。当PMS中有低电量相关逻辑触发时,回调BSS中的实现方法。该接口定义如下:
在PowerManagerInternal中:

public interface LowPowerModeListener {
    int getServiceType();
    void onLowPowerModeChanged(PowerSaveState state);
}

BatteryStatsService实现了该接口:

@Override
public int getServiceType() {
    return ServiceType.BATTERY_STATS;
}
@Override
public void onLowPowerModeChanged(PowerSaveState result) {
    synchronized (mStats) {
        mStats.notePowerSaveModeLocked(result.batterySaverEnabled);
    }
}

PowerManagerService.LocalService中注册如下:

@Override
public void registerLowPowerModeObserver(LowPowerModeListener listener) {
    synchronized (mLock) {
        mLowPowerModeListeners.add(listener);
    }
}

此时,BSS的启动流程分析完毕。

BSS.setBatteryState()

由于BSS负责电池信息的收集、耗电量的统计,因此提供了许多的noteXXX()方法进行不同模块的统计,而这些方法或许只有在实际场景中使用时才会有更加深刻的理解,因此,这些方法不再进行分析(其实搞不懂),接下来分析BSS的数据源方法——setBatteryState().

该方法的调用在BatteryService中,这里直接看其方法体:

@Override
public void setBatteryState(final int status, final int health, final int plugType,
        final int level, final int temp, final int volt, final int chargeUAh,
        final int chargeFullUAh) {
    enforceCallingPermission();

    // BatteryService calls us here and we may update external state. It would be wrong
    // to block such a low level service like BatteryService on external stats like WiFi.
    mWorker.scheduleRunnable(() -> {
        synchronized (mStats) {
            final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
            //如果没有发生变化,则直接更新同步
            if (mStats.isOnBattery() == onBattery) {
                // The battery state has not changed, so we don't need to sync external
                // stats immediately.
                mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
                        chargeUAh, chargeFullUAh);
                return;
            }
        }

        // Sync external stats first as the battery has changed states. If we don't sync
        // before changing the state, we may not collect the relevant data later.
        // Order here is guaranteed since we're scheduling from the same thread and we are
        // using a single threaded executor.
        //如果onBattery改变,则首选需要同步外部设备电池信息,然后才同步内部电池信息
        mWorker.scheduleSync("battery-state", BatteryExternalStatsWorker.UPDATE_ALL);
        mWorker.scheduleRunnable(() -> {
            synchronized (mStats) {
                mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
                        chargeUAh, chargeFullUAh);
            }
        });
    });
}

这里使用了ExecutorService开启了一个异步线程处理任务,而这个任务就是在BatteryStatsImpl.setBatteryStateLocked(),这个方法是统计电量信息的主要方法,详细分析将在电量充满时间计算中进行分析。

猜你喜欢

转载自blog.csdn.net/FightFightFight/article/details/82384336