Android7.0 BatteryStatsService

BatteryStatsService是Android系统中用于收集各模块电量使用情况的服务,它详细记录了各部分的耗电状况。本文深入分析了BatteryStatsService的启动流程,包括构造函数、发布服务、初始化电源管理和BSImpl的构造。BatteryStatsImpl是核心统计类,通过StopwatchTimer等工具统计电量消耗。此外,文章还探讨了电量计算方法和BatteryStatsHelper的使用,展示了Android如何精细地统计和计算电量。
摘要由CSDN通过智能技术生成

BatteryStasService的主要功能是收集系统中各模块和应用进程的用电情况。
因此,我们可以认为BatteryStatsService是Android中的“电表”。
只不过这个电表比较智能,不是单纯地统计整体的耗电,而是分门别类的统计每个部分的耗电情况。
接下来我们就分析一下BatteryStatsService的主要流程。为了方便叙述,后文中我们将BatteryStatsService简称为BSS。

一、BSS启动
1、构造函数
与一般的系统服务不太一样,BSS的创建和发布是在ActivityManagerService中进行的,相关代码如下:

public ActivityManagerService(Context systemContext) {
    ..........
    File dataDir = Environment.getDataDirectory();
    File systemDir = new File(dataDir, "system");
    systemDir.mkdirs();
    //创建BSS对象,传入/data/system目录,同时传入ActivityManagerService的handler
    mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
    //调用BSS中BatteryStatsImpl对象的readLocked方法
    mBatteryStatsService.getActiveStatistics().readLocked();
    //将初始化得到的信息写入disk
    mBatteryStatsService.scheduleWriteToDisk();
    ..........
}

接下来我们先看看BatteryStatsService的构造函数:

BatteryStatsService(File systemDir, Handler handler) {
    // Our handler here will be accessing the disk, use a different thread than
    // what the ActivityManagerService gave us (no I/O on that one!).
    //创建一个本地服务线程,用于访问硬盘数据
    final ServiceThread thread = new ServiceThread("batterystats-sync",
            Process.THREAD_PRIORITY_DEFAULT, true);
    thread.start();
    mHandler = new BatteryStatsHandler(thread.getLooper());

    // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
    //创建BatteryStatsImpl类
    mStats = new BatteryStatsImpl(systemDir, handler, mHandler, this);
}

从代码可以看出,BSS维护的主要变量为一个BatteryStatsImpl对象,这个对象才是承担BSS实际工作的主体。
上文中的readLocked函数就是由BatteryStatsImpl实际执行。

BSS与BatteryServiceImpl的关系如上图所示。
从图中可以看出,BatteryStatsImpl继承自BatteryStats,实现了Parcelable接口,因此可以通过Binder通信在进程间传递。
实际上,从设置中查到的用电信息就是来自BatteryStatsImpl。
BSS的getStatistics函数提供了查询系统用电的接口,该接口的代码如下:

public byte[] getStatistics() {
    ...............
    Parcel out = Parcel.obtain();
    //更新系统中其它组件的耗电情况,记录于BatteryStatsImpl中
    updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
    synchronized (mStats) {
        //将BatteryServiceImpl中的信息写入到数据包中
        mStats.writeToParcel(out, 0);
    }
    byte[] data = out.marshall();
    out.recycle();
    return data;
}

由此可以看出,电量统计的核心类是BatteryStatsImpl,后文中简写为BSImpl。

2、BSS的发布
BSS创建完毕后,在ActivityManagerService的start函数中,BSS完成发布的工作:

private void start() {
    ..........
    mBatteryStatsService.publish(mContext);
    ..........
}

跟进一下BatteryStatsService的publish函数:

public void publish(Context context) {
    mContext = context;
    //BSImpl设置mPhoneSignalScanningTimer的超时时间(用于统计搜索手机信号消耗的时间)
    mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
            com.android.internal.R.integer.config_radioScanningTimeout)
            * 1000L);
    //创建衡量硬件耗电能力的PowerProfile,并交给BSImpl使用
    mStats.setPowerProfile(new PowerProfile(context));
    //将自己注册到service manager进程中
    ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
}

BSS发布相关的代码比较简单,但BSImpl做了两件与发布服务无关的事。
首先,BSImpl设置了mPhoneSignalScanningTimer的超时时间。mPhoneSignalScanningTimer是BSImpl的一个统计工具,实际类型为StopwatchTimer。BSImpl作为统计电量的实际类,定义了许多工具分别统计终端不同部分、场景的耗电情况。后文再详细分析这一部分。
此外,BSImpl设置了PowerProfile相关的内容。

/**
* Reports power consumption values for various device activities. Reads values from an XML file.
* Customize the XML file for different devices.
* /
public PowerProfile(Context context) {
    // Read the XML file for the given profile (normally only one per
    // device)
    if (sPowerMap.size() == 0) {
        //从XML中得到设备硬件的配置情况
        readPowerValuesFromXml(context);
    }
    //得到CPU的性能指标
    initCpuClusters();
}

实际使用的XML应该是和硬件相关的文件,存储各种操作的耗电情况(以mA.h为单位),此处具体的解析过程不做赘述。
我们回过头来看一下BSImpl的setPowerProfile函数:

public void setPowerProfile(PowerProfile profile) {
    synchronized (this) {
        mPowerProfile = profile;

        // We need to initialize the KernelCpuSpeedReaders to read from
        // the first cpu of each core. Once we have the PowerProfile, we have access to this
        // information.
        //以下是构造每个CPU集群对应的KernelCpuSpeedReader
        final int numClusters = mPowerProfile.getNumCpuClusters();
        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
        int firstCpuOfCluster = 0;
        //轮询每个集群
        for (int i = 0; i < numClusters; i++) {
            //得到CPU支持的频率值
            final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);
            //创建KernelCpuSpeedReader
            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
                    numSpeedSteps);
            //当前CPU集群的核数
            firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
        }

        //得到当前评估出的平均电量
        if (mEstimatedBatteryCapacity == -1) {
            // Initialize the estimated battery capacity to a known preset one.
            mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
        }
    }
}

至此我们知道了,在BSS发布之前,它持有的BSImpl对象已经得到整个终端电量相关的硬件信息。

3、initPowerManagement
BSS发布后,SystemServer调用了ActivityManagerService的initPowerManagement函数:

..............
// Now that the power manager has been started, let the activity manager
// initialize power management features.
mActivityManagerService.initPowerManagement();
.............

在ActivityManagerService中:

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

我们进入到BSS的initPowerManagement函数:

/**
* At the time when the constructor runs, the power manager has not yet been
* initialized.  So we initialize the low power observer later.
*/
//SystemServer先启动ActivityManagerService,在其中创建出BSS
//后面才创建PowerManagerService
public void initPowerManagement() {
    final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
    //向PowerManagerService注册低电模式的回调接口,即进入或退出低电模式时
    //调用BSS的onLowPowerModeChanged函数,在该函数中将调用BSImpl的notePowerSaveMode函数
    powerMgr.registerLowPowerModeObserver(this);

    //进入低电模式时启动mPowerSaveModeEnabledTimer
    //离开低电模式时停止mPowerSaveModeEnabledTimer
    //并记录对应的信息 (mPowerSaveModeEnabledTimer也是BSImpl定义的统计工具)
    mStats.notePowerSaveMode(powerMgr.getLowPowerModeEnabled());

    //启动WakeupReasonThread
    (new WakeupReasonThread()).start();
}

在这一部分的最后,我们看一下WakeupReasonThread的主要部分:

final class WakeupReasonThread extends Thread {
   
    ........
    public void run() {
        ..........
        try {
            String reason;
            //等待终端被唤醒
            while ((reason = waitWakeup()) != null) {
                synchronized (mStats) {
                    //记录终端变为唤醒状态的原因
                    mStats.noteWakeupReasonLocked(reason);
                }
            }
        } catch (RuntimeException e) {
            Slog.e(TAG, "Failure reading wakeup reasons", e);
        }
    }

    private String waitWakeup() {
        ..........
        int bytesWritten = nativeWaitWakeup(mUtf8Buffer);
        ..........
        // Create a String from the UTF-16 buffer.
        return mUtf16Buffer.toString();
    }
}

从上面的代码,我们可以看出对于BSS而言,WakeupReasonThread就是负责监控终端的状态,当终端变为唤醒状态时,利用BSImpl记录唤醒的原因。

利用这个机会,我们看看BSS底层是如何监控终端是否唤醒的。
在com_android_server_am_BatteryStatsService中:

static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) {
    ...........
    // Register our wakeup callback if not yet done.
    if (!wakeup_init) {
        wakeup_init = true;
        ALOGV("Creating semaphore...");
        //创建旗语
        int ret = sem_init(&wakeup_sem, 0, 0);
        ..........
        ALOGV("Registering callback...");
        //从挂起变为唤醒时,wakeup_callback将调用sem_post向wakeup_sem写入数据
        set_wakeup_callback(&wakeup_callback);
    }

    // Wait for wakeup.
    ALOGV("Waiting for wakeup...");
    //唤醒时,结束等待
    int ret = sem_wait(&wakeup_sem);
    .........
    //打开文件
    FILE *fp = fopen(LAST_RESUME_REASON, "r");
    .........
    //读取内容
    while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
        ..........
    }
    //关闭文件
    if (fclose(fp) != 0) {
        ...........
    }

    //return reason
    ........
}

现在我们明白了,对于BSS而言,initPowerManagement的工作主要包括两个:
一是监控终端低电模式的状态,当发生改变时进行相应的记录;
二是监控终端从挂起状态变为唤醒状态,并记录唤醒的原因。

BSS启动时主要的流程基本结束,接下来我们看一下BSImpl相关的内容。

二、BSImpl的构造函数
BSImpl的构造函数内容较多,我们分段来看:

public BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
        ExternalStatsSync externalSync, PlatformIdleStateCallback cb) {
    init(clocks);
    ..........

1、init初始化
首先BSImpl在构造函数中,调用init进行初始化工作:

private void init(Clocks clocks) {
    mClocks = clocks;
    mMobileNetworkStats = new NetworkStats[] {
            new NetworkStats(mClocks.elapsedRealtime(), 50),
            new NetworkStats(mClocks.elapsedRealtime(), 50),
            new NetworkStats(mClocks.elapsedRealtime(), 50)
    };
    mWifiNetworkStats = 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值