相关文章推荐
仗义的铅笔  ·  Flutter:flutter_local_ ...·  1 周前    · 
失望的扁豆  ·  android vlc解码优化 ...·  5 天前    · 
任性的烤面包  ·  No job functions ...·  6 月前    · 
时尚的牛肉面  ·  sys.syscommittab ...·  1 年前    · 
痴情的葫芦  ·  [.Net 5.0] ...·  1 年前    · 

1. 概要

手机耗电量主要涵盖两个方面:硬件层面的功耗和软件层面的电量。
本文介绍的电量统计的原理,并不涉及到硬件层面的功耗设计,仅从软件层面围绕以下几个问题进行分析:
Android如何启动电量统计服务?
电量统计涉及到哪一些硬件模块?
如何计算一个应用程序的耗电量?
电量统计需要完成哪些具体工作?
手机有很多硬件模块:CPU,蓝牙,GPS,显示屏,Wifi,射频(Cellular Radio)等,在手机使用过程中,这些硬件模块可能处于不同的状态,譬如Wifi打开或关闭,屏幕是亮还是暗,CPU运行或休眠。 硬件模块在不同的状态下的耗电量是不同的。Android在进行电量统计时,并不是采用直接记录电流消耗量的方式,而是跟踪硬件模块在不同状态下的使用时间,收集一些可用信息,用来近似的计算出电池消耗量。
从用户使用层面来看,Android需要统计出应用程序的耗电量。应用程序的耗电量由很多部分组成,可能使用了GPS,蓝牙等模块,可能应用程序要求长时间亮屏(譬如游戏、视频类应用)。 一个应用程序的电量统计,可以采用累计应用程序使用所有硬件模块时间这种方式近似计算出来。
举一个例子,假定某个APK的使用了GPS,使用时间用 t 表示。GPS模块单位时间的耗电量用 w 表示,那么,这个APK使用GPS的耗电量就可以按照如下方式计算:
耗电量 = 单位时间耗电量(w) × 使用时间(t)
Android框架层通过一个名为batterystats的系统服务,实现了电量统计的功能。batterystats获取电量的使用信息有两种方式:
被动(push):有些硬件模块(wifi, 蓝牙)在发生状态改变时,通知batterystats记录状态变更的时间点
主动(pull):有些硬件模块(cpu)需要batterystats主动记录时间点,譬如记录Activity的启动和终止时间,就能计算出Activity使用CPU的时间
电量统计服务的代码逻辑涉及到以下android源码:
frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
frameworks/base/core/java/android/os/BatteryStats.java
frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
frameworks/base/core/java/com/android/internal/os/BatteryStatsHelper.java
frameworks/base/core/res/res/xml/power_profile.xml

2.电量统计服务的启动过程

BatteryStasService的主要功能是收集系统中各模块和应用进程的用电情况。 因此,可以认为BatteryStatsService是Android中的“电表”。 只不过这个电表比较智能,不是单纯地统计整体的耗电,而是分门别类的统计每个部分的耗电情况

2.1  BSS的创建

与一般的系统服务不太一样, BSS的创建和发布是在ActivityManagerService中进行的

代码路径: /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)

2719    public ActivityManagerService(Context systemContext) {
       ...................................
2763        systemDir.mkdirs();
             //创建BSS对象,传入/data/system目录,同时传入ActivityManagerService的handler
2764        mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
2765        mBatteryStatsService.getActiveStatistics().readLocked();//调用BSS中BatteryStatsImpl对象的readLocked方法
2766        mBatteryStatsService.scheduleWriteToDisk();//将初始化得到的信息写入disk
2767        mOnBattery = DEBUG_POWER ? true
2768                : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
2769        mBatteryStatsService.getActiveStatistics().setCallback(this);
2771        mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
2849    }

BSS的构造函数:

代码路径: /frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

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

BSS维护的主要变量为一个BatteryStatsImpl对象,这个对象才是承担BSS实际工作的主体。

BatteryStatsImpl继承自BatteryStats,实现了Parcelable接口,因此可以通过Binder通信在进程间传递。 实际上,从设置中查到的用电信息就是来自BatteryStatsImpl。

BSS的getStatistics函数提供了查询系统用电的接口

代码路径: /frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

365    public byte[] getStatistics() {
366        mContext.enforceCallingPermission(
367                android.Manifest.permission.BATTERY_STATS, null);
368        //Slog.i("foo", "SENDING BATTERY INFO:");
369        //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
370        Parcel out = Parcel.obtain();
          //更新系统中其它组件的耗电情况,记录于BatteryStatsImpl中
371        updateExternalStatsSync("get-stats", BatteryStatsImpl.ExternalStatsSync.UPDATE_ALL);
372        synchronized (mStats) {
373            mStats.writeToParcel(out, 0);//将BatteryServiceImpl中的信息写入到数据包中
374        }
375        byte[] data = out.marshall();
376        out.recycle();
377        return data;
378    }

电量统计的核心类是BatteryStatsImpl,后文中简写为BSImpl

2.2 BSS的发布

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

代码路径: /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)

2863    private void start() {
2864        removeAllProcessGroups();
2865        mProcessCpuThread.start();
2867        mBatteryStatsService.publish(mContext);
2868        mAppOpsService.publish(mContext);
2869        Slog.d("AppOps", "AppOpsService published");
2870        LocalServices.addService(ActivityManagerInternal.class, new LocalService());
2871        // Wait for the synchronized block started in mProcessCpuThread,
2872        // so that any other acccess to mProcessCpuTracker from main thread
2873        // will be blocked during mProcessCpuTracker initialization.
2874        try {
2875            mProcessCpuInitLatch.await();
2876        } catch (InterruptedException e) {
2877            Slog.wtf(TAG, "Interrupted wait during start", e);
2878            Thread.currentThread().interrupt();
2879            throw new IllegalStateException("Interrupted wait during start");
2880        }
2881    }

在2867行调用BatteryStatsService的publish函数

BatteryStatsService.publish
代码路径:/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

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

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

BSImpl设置了PowerProfile相关的内容

代码路径: /frameworks/base/core/java/com/android/internal/os/PowerProfile.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/PowerProfile.java)

在publish的240行中创建衡量硬件耗电能力的PowerProfile的构造函数

208    public PowerProfile(Context context) {
209        // Read the XML file for the given profile (normally only one per
210        // device)
211        if (sPowerMap.size() == 0) {
212            readPowerValuesFromXml(context);//从XML中得到设备硬件的配置情况
213        }
214        initCpuClusters();//得到CPU的性能指标
215    }

publish的240行中将创建衡量硬件耗电能力的PowerProfile文件交给BSImpl使用,实际使用的XML应该是和硬件相关的文件,存储各种操作的耗电情况(以mA.h为单位)。调用setPowerProfileLocked

BatteryStatsImpl.setPowerProfileLocked
代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

8709    public void setPowerProfileLocked(PowerProfile profile) {
8710        mPowerProfile = profile;
8712        // We need to initialize the KernelCpuSpeedReaders to read from
8713        // the first cpu of each core. Once we have the PowerProfile, we have access to this
8714        // information.
8715        final int numClusters = mPowerProfile.getNumCpuClusters();//构造每个CPU集群对应的KernelCpuSpeedReader
8716        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
8717        int firstCpuOfCluster = 0;
8718        for (int i = 0; i < numClusters; i++) {//轮询每个集群
8719            final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i);//得到CPU支持的频率值
8720            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
8721                    numSpeedSteps);//创建KernelCpuSpeedReader
8722            firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);//当前CPU集群的核数
8723        }
8724        //得到当前评估出的平均电量
8725        if (mEstimatedBatteryCapacity == -1) {
8726            // Initialize the estimated battery capacity to a known preset one.
8727            mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
8728        }
8729    }

在BSS发布之前,它持有的BSImpl对象已经得到整个终端电量相关的硬件信息

SystemServer调用了ActivityManagerService的initPowerManagement函数
代码路径:/frameworks/base/services/java/com/android/server/SystemServer.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/java/com/android/server/SystemServer.java)

491    private void startBootstrapServices() {
         ..................................
529        traceBeginAndSlog("InitPowerManagement");
530        mActivityManagerService.initPowerManagement();
531        traceEnd();
         ..................................
638    }

在ActivityManagerService中
代码路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)

2887    public void initPowerManagement() {
2888        mStackSupervisor.initPowerManagement();
2889        mBatteryStatsService.initPowerManagement();
2890        mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
2891        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
2892        mVoiceWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*voice*");
2893        mVoiceWakeLock.setReferenceCounted(false);
2894    }

在2889行调用BSS的initPowerManagement函数

BSS的initPowerManagement函数:

代码路径: /frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

249    public void initPowerManagement() {
250        final PowerManagerInternal powerMgr = LocalServices.getService(PowerManagerInternal.class);
           //向PowerManagerService注册低电模式的回调接口,即进入或退出低电模式时
           //调用BSS的onLowPowerModeChanged函数,在该函数中将调用BSImpl的notePowerSaveMode函数
251        powerMgr.registerLowPowerModeObserver(this);
252        synchronized (mStats) {
253            mStats.notePowerSaveModeLocked(
254                    powerMgr.getLowPowerState(ServiceType.BATTERY_STATS)
255                            .batterySaverEnabled);
256        }
257        (new WakeupReasonThread()).start();//启动WakeupReasonThread
258    }

WakeupReasonThread

代码路径:/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

1082    final class WakeupReasonThread extends Thread {
       .........................................
1092        public void run() {
           .............................................
1104            try {
1105                String reason;
1106                while ((reason = waitWakeup()) != null) {//等待终端被唤醒
1107                    synchronized (mStats) {
1108                        mStats.noteWakeupReasonLocked(reason);//记录终端变为唤醒状态的原因
1109                    }
1110                }
1111            } catch (RuntimeException e) {
1112                Slog.e(TAG, "Failure reading wakeup reasons", e);
1113            }
1114        }
1116        private String waitWakeup() {
................................
1121            int bytesWritten = nativeWaitWakeup(mUtf8Buffer);
1122            if (bytesWritten < 0) {
1123                return null;
1124            } else if (bytesWritten == 0) {
1125                return "unknown";
1126            }
.....................................
1138        }
1139    }

WakeupReasonThread就是负责监控终端的状态,当终端变为唤醒状态时,利用BSImpl记录唤醒的原因

代码路径:/frameworks/base/services/core/jni/com_android_server_am_BatteryStatsService.cpp                 (http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/jni/com_android_server_am_BatteryStatsService.cpp)

74static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
76    if (outBuf == NULL) {
77        jniThrowException(env, "java/lang/NullPointerException", "null argument");
78        return -1;
81    // Register our wakeup callback if not yet done.
82    if (!wakeup_init) {
83        wakeup_init = true;
84        ALOGV("Creating semaphore...");
85        int ret = sem_init(&wakeup_sem, 0, 0);//创建旗语
86        if (ret < 0) {
87            char buf[80];
88            strerror_r(errno, buf, sizeof(buf));
89            ALOGE("Error creating semaphore: %s\n", buf);
90            jniThrowException(env, "java/lang/IllegalStateException", buf);
91            return -1;
93        ALOGV("Registering callback...");
94        set_wakeup_callback(&wakeup_callback);//从挂起变为唤醒时,wakeup_callback将调用sem_post向wakeup_sem写入数据
97    // Wait for wakeup.
98    ALOGV("Waiting for wakeup...");
99    int ret = sem_wait(&wakeup_sem);//唤醒时,结束等待
100    if (ret < 0) {
101        char buf[80];
102        strerror_r(errno, buf, sizeof(buf));
103        ALOGE("Error waiting on semaphore: %s\n", buf);
104        // Return 0 here to let it continue looping but not return results.
105        return 0;
106    }
108    FILE *fp = fopen(LAST_RESUME_REASON, "r");//打开文件
109    if (fp == NULL) {
110        ALOGE("Failed to open %s", LAST_RESUME_REASON);
111        return -1;
112    }
114    char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
115    int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
117    ALOGV("Reading wakeup reasons");
118    char* mergedreasonpos = mergedreason;
119    char reasonline[128];
120    int i = 0;
121    while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {//读取内容
122        char* pos = reasonline;
123        char* endPos;
124        int len;
125        // First field is the index or 'Abort'.
126        int irq = (int)strtol(pos, &endPos, 10);
127        if (pos != endPos) {
128            // Write the irq number to the merged reason string.
129            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
130        } else {
131            // The first field is not an irq, it may be the word Abort.
132            const size_t abortPrefixLen = strlen("Abort:");
133            if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
134                // Ooops.
135                ALOGE("Bad reason line: %s", reasonline);
136                continue;
137            }
139            // Write 'Abort' to the merged reason string.
140            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
141            endPos = pos + abortPrefixLen;
142        }
143        pos = endPos;
145        if (len >= 0 && len < remainreasonlen) {
146            mergedreasonpos += len;
147            remainreasonlen -= len;
148        }
150        // Skip whitespace; rest of the buffer is the reason string.
151        while (*pos == ' ') {
152            pos++;
153        }
155        // Chop newline at end.
156        char* endpos = pos;
157        while (*endpos != 0) {
158            if (*endpos == '\n') {
159                *endpos = 0;
160                break;
161            }
162            endpos++;
163        }
165        len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
166        if (len >= 0 && len < remainreasonlen) {
167            mergedreasonpos += len;
168            remainreasonlen -= len;
169        }
170        i++;
171    }
173    ALOGV("Got %d reasons", i);
174    if (i > 0) {
175        *mergedreasonpos = 0;
176    }
178    if (fclose(fp) != 0) {//关闭文件
179        ALOGE("Failed to close %s", LAST_RESUME_REASON);
180        return -1;
181    }
182    return mergedreasonpos - mergedreason;
183}

在BSS中,initPowerManagement的工作主要包括两个:

一是监控终端低电模式的状态,当发生改变时进行相应的记录; 二是监控终端从挂起状态变为唤醒状态,并记录唤醒的原因

2.3 BSImpl的构造函数

BatteryStatsImpl.BatteryStatsImpl
代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

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

2.3.1、init初始化

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

637    private void init(Clocks clocks) {
638        mClocks = clocks;
639    }

2.3.2、创建统计所需要的文件

8606 if ( systemDir != null ) { 8607 mFile = new JournaledFile (new File ( systemDir , "batterystats.bin" ), 8608 new File ( systemDir , "batterystats.bin.tmp" ));//创建于data/system/目录下的文件,分别作为原始文件和临时文件 8609 } else { 8610 mFile = null ; 8611 } 8612 mCheckinFile = new AtomicFile (new File ( systemDir , "batterystats-checkin.bin" ));/mCheckinFile存储BSImpl的snapShot,当充电状态从充电变为未充电,且电池累积耗电量足够大时,会将snapShot写入"batterystats-checkin.bin" 8613 mDailyFile = new AtomicFile (new File ( systemDir , " batterystats-daily.xml " ));//mDailyFile应该是用于记录每天的电量统计信息 8614 mExternalSync = externalSync ; 8615 mHandler = new MyHandler ( handler . getLooper ()); 8616 mStartCount ++;
上述代码首先创建了一个JournaledFile,其内部包含两个创建于data/system/目录下的文件,分别作为原始文件和临时文件。这么做的目的是进行备份, 防止在读写过程中文件信息丢失或出错。 这部分文件用于保存BatteryStats电量的统计信息。系统会不定时的将电量统计信息BatteryStats写入到文件中。从代码中操作mDailyFile的接口来看,mDailyFile应该是用于记录每天的电量统计信息。 

2.3.3、创建各模块电量的统计工具

8617        mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
8618        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
8619            mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
8620                    mOnBatteryTimeBase);
8621        }
8622        mInteractiveTimer = new StopwatchTimer(mClocks, null, -10, null, mOnBatteryTimeBase);
8623        mPowerSaveModeEnabledTimer = new StopwatchTimer(mClocks, null, -2, null,
8624                mOnBatteryTimeBase);
8625        mDeviceIdleModeLightTimer = new StopwatchTimer(mClocks, null, -11, null,
8626                mOnBatteryTimeBase);
8627        mDeviceIdleModeFullTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
8628        mDeviceLightIdlingTimer = new StopwatchTimer(mClocks, null, -15, null, mOnBatteryTimeBase);
8629        mDeviceIdlingTimer = new StopwatchTimer(mClocks, null, -12, null, mOnBatteryTimeBase);
8630        mPhoneOnTimer = new StopwatchTimer(mClocks, null, -3, null, mOnBatteryTimeBase);
8631        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
8632            mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -200-i, null,
8633                    mOnBatteryTimeBase);
8634        }
8635        mPhoneSignalScanningTimer = new StopwatchTimer(mClocks, null, -200+1, null,
8636                mOnBatteryTimeBase);
8637        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
8638            mPhoneDataConnectionsTimer[i] = new StopwatchTimer(mClocks, null, -300-i, null,
8639                    mOnBatteryTimeBase);
8640        }
8641        for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
8642            mNetworkByteActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
8643            mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mOnBatteryTimeBase);
8644        }
8645        mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_WIFI_TX_LEVELS);
8646        mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
8647                NUM_BT_TX_LEVELS);
8648        mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
8649                ModemActivityInfo.TX_POWER_LEVELS);
8650        mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
8651        mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
8652                mOnBatteryTimeBase);
8653        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
8654        mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
8655        mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
8656        mWifiOnTimer = new StopwatchTimer(mClocks, null, -4, null, mOnBatteryTimeBase);
8657        mGlobalWifiRunningTimer = new StopwatchTimer(mClocks, null, -5, null, mOnBatteryTimeBase);
8658        for (int i=0; i<NUM_WIFI_STATES; i++) {
8659            mWifiStateTimer[i] = new StopwatchTimer(mClocks, null, -600-i, null,
8660                    mOnBatteryTimeBase);
8661        }
8662        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
8663            mWifiSupplStateTimer[i] = new StopwatchTimer(mClocks, null, -700-i, null,
8664                    mOnBatteryTimeBase);
8665        }
8666        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
8667            mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
8668                    mOnBatteryTimeBase);
8669        }
8670        mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
8671        mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
8672        mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
8673        mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
8674        mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
8675        mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
8676        mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);

BSImpl需要统计各个模块的耗电情况,甚至需要统计一个模块在不同场景下的耗电情况。 

BSImpl在其构造函数中创建了许多对象进行分类统计。 BSImpl在构造函数中创建的工具类就有三种,即StopwatchTimer、LongSamplingCounter和ControllerActivityCounterImpl。 ControllerActivityCounterImpl包含了多个LongSamplingCounter对象。

Counter相关的主要用于计数,Timer相关的用于计时。 电量的统计主要依赖于Timer;Counter相关的对象,多进行统计发包数量、分组数量这样的工作。 

在Android系统中,手机的电压是一定的,具体操作的耗电量在构造PowerProfile时已经得到了,因此手机中各模块的电量计算,就是利用模块使用时间*模块对应操作的单位耗电量。

耗电量 = 单位时间的耗电量(w) × 使用时间(t) = 电压(U) × 单位时间电流量(I) × 使用时间(t)

这些工具类均实现了TimeBaseObs接口定义的onTimeStarted函数和onTimeStopped。 

2.3.4、初始化剩余变量

8677        mOnBattery = mOnBatteryInternal = false;
8678        long uptime = mClocks.uptimeMillis() * 1000;
8679        long realtime = mClocks.elapsedRealtime() * 1000;
8680        initTimes(uptime, realtime);//初始化统计时间
8681        mStartPlatformVersion = mEndPlatformVersion = Build.ID;
8682        mDischargeStartLevel = 0;
8683        mDischargeUnplugLevel = 0;
8684        mDischargePlugLevel = -1;
8685        mDischargeCurrentLevel = 0;
8686        mCurrentBatteryLevel = 0;
8687        initDischarge();
8688        clearHistoryLocked();//删除用电统计的记录
8689        updateDailyDeadlineLocked();
8690        mPlatformIdleStateCallback = cb;
8691    }

BSImpl的构造函数最后会初始化大量的变量。

系统时间分为uptime和realtime。uptime和realtime的时间起点都从系统启动开始算, 但是uptime不包括系统休眠时间,而realtime包括系统休眠时间。

2.4 、BSS及BSImpl主要流程分析 

2.4.1、BSImpl的readLocked 

代码路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)

2761        File dataDir = Environment.getDataDirectory();
2762        File systemDir = new File(dataDir, "system");
2763        systemDir.mkdirs();
2764        mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
2765        mBatteryStatsService.getActiveStatistics().readLocked();//getActiveStatistics返回BSImpl
2766        mBatteryStatsService.scheduleWriteToDisk();

ActivityManagerService创建出BatteryStatsService后

2.4.2、BSImpl.readLocked

代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

11133
11134    public void readLocked() {
11135        if (mDailyFile != null) {
11136            readDailyStatsLocked();//读取BSImpl维护的mDailyFile,解析其中的数据保存到mDailyItems中
11137        }
11138        //mFile为"batterystats.bin"对应的JournaledFile
11139        if (mFile == null) {
11140            Slog.w("BatteryStats", "readLocked: no file associated with this instance");
11141            return;
11142        }
11143        //mUidStats保存了BatteryStatsImpl.Uid,BSImpl定义的数据结构,继承BSS.Uid,记录每个uid对应的耗电情况
11144        mUidStats.clear();
11145
11146        try {
11147            File file = mFile.chooseForRead();//从JournaledFile选择实际文件和备份文件中的一个读取
11148            if (!file.exists()) {
11149                return;
11150            }
11151            FileInputStream stream = new FileInputStream(file);
11152
11153            byte[] raw = BatteryStatsHelper.readFully(stream);
11154            Parcel in = Parcel.obtain();
11155            in.unmarshall(raw, 0, raw.length);
11156            in.setDataPosition(0);
11157            stream.close();
11158
11159            readSummaryFromParcel(in);//利用从"batterystats.bin"中读取的数据初始化BSImpl的变量
11160        } catch(Exception e) {
11161            Slog.e("BatteryStats", "Error reading battery statistics", e);
11162            resetAllStatsLocked();
11163        }
11164
11165        mEndPlatformVersion = Build.ID;
11166        //开始记录历史信息
11167        if (mHistoryBuffer.dataPosition() > 0) {
11168            mRecordingHistory = true;
11169            final long elapsedRealtime = mClocks.elapsedRealtime();
11170            final long uptime = mClocks.uptimeMillis();
11171            if (USE_OLD_HISTORY) {
11172                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
11173            }
11174            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
11175            startRecordingHistory(elapsedRealtime, uptime, false);
11176        }
11177
11178        recordDailyStatsIfNeededLocked(false);//当时间满足条件时,记录每日的信息
11179    }

readLocked主要的功能是读取之前的统计信息,然后初始化BSImpl对象。

BSImpl中的Uid类继承自BatteryStats的Uid类。 其中: 
    WakeLock用于统计该Uid对应进程使用某个WakeLock的用电情况; 
    Proc用于统计该Uid对应的某个进程的用电情况; 
    Pkg用于统计某个Pacakge的电量使用情况,其内部类Serv用于统计该Package内某个服务的用电情况; 
    Sensor用于统计传感器的用电情况

2.4.3 BSS的scheduleWriteToDisk 

代码路径:/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

306    public void scheduleWriteToDisk() {
307        mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK);
308    }

MSG_WRITE_TO_DISK对应的处理流程为

 class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
           ..............................................
121        public static final int MSG_WRITE_TO_DISK = 2;
           ..........................................
126        public BatteryStatsHandler(Looper looper) {
127            super(looper);
128        }
130        @Override
131        public void handleMessage(Message msg) {
132            switch (msg.what) {
            ..................................
156                case MSG_WRITE_TO_DISK:
157                    updateExternalStatsSync("write", UPDATE_ALL);//进行信息更新
158                    if (DBG) Slog.d(TAG, "begin writeAsyncLocked");
159                    synchronized (mStats) {
160                        mStats.writeAsyncLocked();//完成信息写入
161                    }
162                    if (DBG) Slog.d(TAG, "end writeAsyncLocked");
163                    break;
164            }
165        }
             .........................................
188    }

2.4.3.1 updateExternalStatsSync信息更新

代码路径:/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

1484    void updateExternalStatsSync(final String reason, int updateFlags) {
1485        SynchronousResultReceiver wifiReceiver = null;//SynchronousResultReceiver用于接收消息
1486        SynchronousResultReceiver bluetoothReceiver = null;
1487        SynchronousResultReceiver modemReceiver = null;
1489        if (DBG) Slog.d(TAG, "begin updateExternalStatsSync reason=" + reason);
1490        synchronized (mExternalStatsLock) {
1491            if (mContext == null) {
1492                // Don't do any work yet.
1493                if (DBG) Slog.d(TAG, "end updateExternalStatsSync");
1494                return;
1495            }
1497            if ((updateFlags & UPDATE_WIFI) != 0) {
1498                if (mWifiManager == null) {
1499                    mWifiManager = IWifiManager.Stub.asInterface(
1500                            ServiceManager.getService(Context.WIFI_SERVICE));
1501                }
1503                if (mWifiManager != null) {
1504                    try {
1505                        wifiReceiver = new SynchronousResultReceiver();//获取WiFi的信息,传入SynchronousResultReceiver对象
1506                        mWifiManager.requestActivityInfo(wifiReceiver);
1507                    } catch (RemoteException e) {
1508                        // Oh well.
1509                    }
1510                }
1511            }
1513            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_BT) != 0) {
1514                final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1515                if (adapter != null) {
1516                    bluetoothReceiver = new SynchronousResultReceiver();//获取蓝牙的信息,传入SynchronousResultReceiver对象
1517                    adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
1518                }
1519            }
1521            if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
1522                if (mTelephony == null) {
1523                    mTelephony = TelephonyManager.from(mContext);
1524                }
1526                if (mTelephony != null) {
1527                    modemReceiver = new SynchronousResultReceiver();//获取modem的信息,传入SynchronousResultReceiver对象
1528                    mTelephony.requestModemActivityInfo(modemReceiver);//对于Telephony而言,就是通过RIL发送消息给modem,获取返回结果
1529                }
1530            }
1532            WifiActivityEnergyInfo wifiInfo = null;
1533            BluetoothActivityEnergyInfo bluetoothInfo = null;
1534            ModemActivityInfo modemInfo = null;
1535            try {
1536                wifiInfo = awaitControllerInfo(wifiReceiver);//awaitControllerInfo内部调用SynchronousResultReceiver对象的awaitResult函数,在获得消息前,会等待一段时间直到超时,获取到结果时,退出等待
1537            } catch (TimeoutException e) {
1538                Slog.w(TAG, "Timeout reading wifi stats");
1539            }
1541            try {
1542                bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
1543            } catch (TimeoutException e) {
1544                Slog.w(TAG, "Timeout reading bt stats");
1545            }
1547            try {
1548                modemInfo = awaitControllerInfo(modemReceiver);
1549            } catch (TimeoutException e) {
1550                Slog.w(TAG, "Timeout reading modem stats");
1551            }
1553            synchronized (mStats) {//更新相关的信息
1554                mStats.addHistoryEventLocked(
1555                        SystemClock.elapsedRealtime(),
1556                        SystemClock.uptimeMillis(),
1557                        BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
1558                        reason, 0);
1560                if ((updateFlags & UPDATE_CPU) != 0) {
1561                    mStats.updateCpuTimeLocked(true /* updateCpuFreqData */);
1562                }
1563                mStats.updateKernelWakelocksLocked();
1564                mStats.updateKernelMemoryBandwidthLocked();
1566                if (bluetoothInfo != null) {
1567                    if (bluetoothInfo.isValid()) {
1568                        mStats.updateBluetoothStateLocked(bluetoothInfo);//更新蓝牙的耗电情况
1569                    } else {
1570                        Slog.e(TAG, "bluetooth info is invalid: " + bluetoothInfo);
1571                    }
1572                }
1573            }
1575            if (wifiInfo != null) {
1576                if (wifiInfo.isValid()) {
1577                    mStats.updateWifiState(extractDelta(wifiInfo));//更新WiFi的耗电情况
1578                } else {
1579                    Slog.e(TAG, "wifi info is invalid: " + wifiInfo);
1580                }
1581            }
1583            if (modemInfo != null) {
1584                if (modemInfo.isValid()) {
1585                    mStats.updateMobileRadioState(modemInfo);
1586                } else {
1587                    Slog.e(TAG, "modem info is invalid: " + modemInfo);
1588                }
1589            }
1590        }
1591        if (DBG) Slog.d(TAG, "end updateExternalStatsSync");
1592    }

updateExternalStatsSync函数用于同步当前终端的信息, 主要是获取WiFi、BT和modem的耗电情况,并保存到BSImpl中。

2.4.3.2 BSImpl的writeAsyncLocked函数

代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

11069    public void writeAsyncLocked() {
11070        writeLocked(false);//传入的值为false,表示异步
11071    }
11072
11073    public void writeSyncLocked() {
11074        writeLocked(true);
11075    }
11076
11077    void writeLocked(boolean sync) {
11078        if (mFile == null) {
11079            Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
11080            return;
11081        }
11082
11083        if (mShuttingDown) {
11084            return;
11085        }
11086
11087        Parcel out = Parcel.obtain();
11088        writeSummaryToParcel(out, true);//将BSImpl中的信息写入到Parcel对象
11089        mLastWriteTime = mClocks.elapsedRealtime();
11090
11091        if (mPendingWrite != null) {
11092            mPendingWrite.recycle();
11093        }
11094        mPendingWrite = out;//Parcel对象保存到mPendingWrite
11095
11096        if (sync) {//同步时用主线程写
11097            commitPendingDataToDisk();
11098        } else {//异步时利用后台线程写
11099            BackgroundThread.getHandler().post(new Runnable() {
11100                @Override public void run() {
11101                    commitPendingDataToDisk();
11102                }
11103            });
11104        }
11105    }

从11097行和11101行commitPendingDataToDisk才是进行实际的写操作

11107    public void commitPendingDataToDisk() {
11108        final Parcel next;
11109        synchronized (this) {
11110            next = mPendingWrite;
11111            mPendingWrite = null;
11112            if (next == null) {
11113                return;
11114            }
11115        }
11116
11117        mWriteLock.lock();
11118        try {
11119            FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());//写入的文件还是"batterystats.bin"
11120            stream.write(next.marshall()); //next为mPendingWrite,即当前待写入的内容
11121            stream.flush();
11122            FileUtils.sync(stream);
11123            stream.close();
11124            mFile.commit();
11125        } catch (IOException e) {
11126            Slog.w("BatteryStats", "Error writing battery statistics", e);
11127            mFile.rollback();
11128        } finally {
11129            next.recycle();
11130            mWriteLock.unlock();
11131        }
11132    }

scheduleWriteToDisk的作用就是将手机中最新的信息保存到硬盘中

3、BSImpl耗电时间统计 
了解BSS和BSImpl初始的一些流程后,我们来看看BSImpl到底是如何统计和计算电量的。

3.1 楔子 
在BSS中定义了许多以note开头的方法,这些方法就是不同模块用于通知BSS记录用电信息的。 以数据业务为例,看看统计电量的过程。

 代码路径: /frameworks/base/services/core/java/com/android/server/ConnectivityService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/ConnectivityService.java)

3.1.1 ConnectivityService的构造函数:

677    @VisibleForTesting
678    protected ConnectivityService(Context context, INetworkManagementService netManager,
679            INetworkStatsService statsService, INetworkPolicyManager policyManager,
680            IpConnectivityLog logger) {
            ...........................................
818        //创建出DataConnectionStats,该类继承自BroadcastReceiver
819        mDataConnectionStats = new DataConnectionStats(mContext);
820        mDataConnectionStats.startMonitoring();
            .....................................
841    }

3.1.2DataConnectionStats类:

代码路径:/frameworks/base/services/core/java/com/android/server/connectivity/DataConnectionStats.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/connectivity/DataConnectionStats.java)

48    public DataConnectionStats(Context context) {
49        mContext = context;
50        mBatteryStats = BatteryStatsService.getService();//获取到BatteryStatsService的代理对象
53    public void startMonitoring() {
54        TelephonyManager phone =
55                (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
56        phone.listen(mPhoneStateListener,//listen phone状态的变化
57                PhoneStateListener.LISTEN_SERVICE_STATE
58              | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
59              | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
60              | PhoneStateListener.LISTEN_DATA_ACTIVITY);
61        //监听以下广播
62        IntentFilter filter = new IntentFilter();
63        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
64        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
65        filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
66        mContext.registerReceiver(this, filter);
69    @Override
70    public void onReceive(Context context, Intent intent) {
71        final String action = intent.getAction();
72        if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
73            updateSimState(intent);
74            notePhoneDataConnectionState(); //注意notePhoneDataConnectionState
75        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
76                action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
77            notePhoneDataConnectionState();
81    private void notePhoneDataConnectionState() {
82        if (mServiceState == null) {
83            return;
85        boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
86                || mSimState == IccCardConstants.State.UNKNOWN;
87        boolean visible = (simReadyOrUnknown || isCdma()) // we only check the sim state for GSM
88                && hasService()
89                && mDataState == TelephonyManager.DATA_CONNECTED;
90        int networkType = mServiceState.getDataNetworkType();
91        if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
92                networkType, visible ? "" : "not "));
93        try {
94            mBatteryStats.notePhoneDataConnectionState(networkType, visible);
95        } catch (RemoteException e) {
96            Log.w(TAG, "Error noting data connection state", e);
            ................................................
131    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
132        @Override
133        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
134            mSignalStrength = signalStrength;
135        }
137        @Override
138        public void onServiceStateChanged(ServiceState state) {
139            mServiceState = state;
140            notePhoneDataConnectionState();//同样会有notePhoneDataConnectionState
141        }
143        @Override
144        public void onDataConnectionStateChanged(int state, int networkType) {
145            mDataState = state;
146            notePhoneDataConnectionState();//同样会有notePhoneDataConnectionState
147        }
149        @Override
150        public void onDataActivity(int direction) {
151            notePhoneDataConnectionState();//同样会有notePhoneDataConnectionState
152        }
153    };
154}

DataConnectionStats监控了整个终端中与数据相关的比较重要的状态,例如SIM State、Service State等。当状态发生变化时,就会调用notePhoneDataConnectionState函数。

3.1.3 DataConnectionStats.notePhoneDataConnectionState:

代码路径:/frameworks/base/services/core/java/com/android/server/connectivity/DataConnectionStats.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/connectivity/DataConnectionStats.java)

   81 private void notePhoneDataConnectionState() {

82        if (mServiceState == null) {
83            return;
85        boolean simReadyOrUnknown = mSimState == IccCardConstants.State.READY
86                || mSimState == IccCardConstants.State.UNKNOWN;
87        boolean visible = (simReadyOrUnknown || isCdma()) //基本上可以简单的认为有服务并且数据脸上时,visible为true
88                && hasService()
89                && mDataState == TelephonyManager.DATA_CONNECTED;
90        int networkType = mServiceState.getDataNetworkType();
91        if (DEBUG) Log.d(TAG, String.format("Noting data connection for network type %s: %svisible",
92                networkType, visible ? "" : "not "));
93        try {
94            mBatteryStats.notePhoneDataConnectionState(networkType, visible);//进入到BSS中
95        } catch (RemoteException e) {
96            Log.w(TAG, "Error noting data connection state", e);

3.1.4 BatteryStatsService.notePhoneDataConnectionState

代码路径:/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java)

646    public void notePhoneDataConnectionState(int dataType, boolean hasData) {
647        enforceCallingPermission();
648        synchronized (mStats) {
649            mStats.notePhoneDataConnectionStateLocked(dataType, hasData); //委托给BSImpl处理
650        }
651    }

3.1.5 BSImpl.notePhoneDataConnectionStateLocked:

代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

4512    public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData) {
4513        int bin = DATA_CONNECTION_NONE;
4514        if (hasData) {//数据连接上时,hasData为true
4515            switch (dataType) {//dataType对应与networkType
4516                case TelephonyManager.NETWORK_TYPE_EDGE://根据NetworkType, 给bin赋予不同的值
4517                    bin = DATA_CONNECTION_EDGE;
4518                    break;
        .......................................
4564            }
4565        }
4566        if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
4567        if (mPhoneDataConnectionType != bin) {//数据网络的类型发生改变时
4568            final long elapsedRealtime = mClocks.elapsedRealtime();//实际消逝的时间
4569            final long uptime = mClocks.uptimeMillis();//系统处于唤醒的状态消逝的时间
4570            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
4571                    | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
4572            if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
4573                    + Integer.toHexString(mHistoryCur.states));
4574            addHistoryRecordLocked(elapsedRealtime, uptime);//记录信息
4575            if (mPhoneDataConnectionType >= 0) {
4576                mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
4577                        elapsedRealtime);//旧networkType对应的StopWatchTime停止记录时间
4578            }
4579            mPhoneDataConnectionType = bin;
                //新networkType对应的StopWatchTimer开始记录时间
4580            mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtime);
4581        }
4582    }

对于数据业务而言,BSImpl初始化时创建了一个StopWatchTimer数组mPhoneDataConnectionsTimer,用于记录不同NetworkType下,数据业务的耗电时间。 因此,每次NetworkType发生改变时,就停止旧有的NetworkType对应StopWatchTimer,这样就可以得到旧有NetworkType下耗电的时间。同时,启动新的NetworkType的StopWatchTimer,这样当下次NetworkType发生改变时,就可以得到数据业务在这个NetworkType下耗电的时间。

3.2 StopWatchTimer的startRunningLocked

代码路径:/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
(http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java)

2037        public void startRunningLocked(long elapsedRealtimeMs) {
2038            if (mNesting++ == 0) {
2039                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
2040                mUpdateTime = batteryRealtime;
2041                if (mTimerPool != null) {
2042                    // Accumulate time to all currently active timers before adding
2043                    // this new one to the pool.
2044                    refreshTimersLocked(batteryRealtime, mTimerPool, null);
2045                    // Add this timer to the active pool
2046                    mTimerPool.add(this);
2047                }
2048                if (mTimeBase.isRunning()) {
2049                    // Increment the count
2050                    mCount++;
2051                    mAcquireTime = mTotalTime;
2052                } else {
2053                    mAcquireTime = -1;
2054                }
2055                if (DEBUG && mType < 0) {
2056                    Log.v(TAG, "start #" + mType + ": mUpdateTime=" + mUpdateTime
2057                            + " mTotalTime=" + mTotalTime + " mCount=" + mCount
2058                            + " mAcquireTime=" + mAcquireTime);
2059                }
2060            }
2061        }

在startRunningLocked函数中,我们只需要知道StopWatchTimer中的mUpdateTime记录者本次的启动时间。

 StopWatchTimer.stopRunningLocked

2067        public void stopRunningLocked(long elapsedRealtimeMs) {
2068            // Ignore attempt to stop a timer that isn't running
2069            if (mNesting == 0) {
2070                return;
2071            }
2072            if (--mNesting == 0) {
2073                final long batteryRealtime = mTimeBase.getRealtime(elapsedRealtimeMs * 1000);
2074                if (mTimerPool != null) {
2075                    // Accumulate time to all active counters, scaled by the total
2076                    // active in the pool, before taking this one out of the pool.
2077                    refreshTimersLocked(batteryRealtime, mTimerPool, null);
2078                    // Remove this timer from the active pool
2079                    mTimerPool.remove(this);
2080                } else {
2081                    mNesting = 1;
2082                    mTotalTime = computeRunTimeLocked(batteryRealtime);
2083                    mNesting = 0;
2084                }
2086                if (DEBUG && mType < 0) {
2087                    Log.v(TAG, "stop #" + mType + ": mUpdateTime=" + mUpdateTime
2088                            + " mTotalTime=" + mTotalTime + " mCount=" + mCount
2089                            + " mAcquireTime=" + mAcquireTime);
2090                }
2092                if (mAcquireTime >= 0 && mTotalTime == mAcquireTime) {
2093                    // If there was no change in the time, then discard this
2094                    // count.  A somewhat cheezy strategy, but hey.
2095                    mCount--;
2096                }
2097            }
2098        }
        ............................................................................
2128        @Override
2129        protected long computeRunTimeLocked(long curBatteryRealtime) {
2130            if (mTimeout > 0 && curBatteryRealtime > mUpdateTime + mTimeout) {
2131                curBatteryRealtime = mUpdateTime + mTimeout;
2132            }
2133            return mTotalTime + (mNesting > 0
2134                    ? (curBatteryRealtime - mUpdateTime)
2135                            / (mTimerPool != null ? mTimerPool.size() : 1)
2136                    : 0);
2137        }

BSS中整个耗电统计的架构,大概如下图所示: 

每个模块都会监控自己的关键状态。 当状态发生改变时,直接或间接地通知到BBS。 BSS收到通知后,通过各种以note开头的函数,将通知递交给BSImpl进行实际的处理。 BSImpl完成启动或停止对应模块的StopWatchTimer,完成对该模块耗电时间的统计。

1. 概要手机耗电量主要涵盖两个方面:硬件层面的功耗和软件层面的电量。本文介绍的电量统计的原理,并不涉及到硬件层面的功耗设计,仅从软件层面围绕以下几个问题进行分析:        Android如何启动电量统计服务?        电量统计涉及到哪一些硬件模块?        如何计算一个应用程序的耗电量?        电量统计需要完成哪些具体工作?手机有很多硬件模块:CPU,蓝牙,GPS,显示... 从BatteryStatsService的启动时序图可以看出,BatteryStatsService服务是在ActivityManagerService服务中启动的 1. 在SystemServer中startBoots
SystemServer在启动BatteryService时,一次会调用它的构造函数,onStart,onBootPhase方法;解析来我们分析一下这些方法: 1.Constructor方法: public BatteryService(Context context) { super(context); mContext = context; mHandler = new Handler(true /*async*/); //电源呼吸灯 mLed = new Led
BatteryStasService的主要功能是收集系统中各模块和应用进程的用电情况。 因此,我们可以认为BatteryStatsServiceAndroid中的“电表”。 只不过这个电表比较智能,不是单纯地统计整体的耗电,而是分门别类的统计每个部分的耗电情况。 接下来我们就分析一下BatteryStatsService的主要流程。
--------- beginning of system 08-26 13:31:37.070 10872 10872 E VgcUtil : getFile name :theme_dir_pathnot exit in vgc_path_config.xml 08-30 15:34:43.657 20328 20328 E VgcUtil : getFile name :theme_dir_pathnot exit in vgc_path_config.xml --------- beginning
Java语言的执行环境是JVM(JAVA虚拟机),JVM其实是主机环境中的一个进程,每个JVM虚拟机进程在本地环境中都对应有一个JavaVM的结构体,用来记录当前JVM进程的相关环境数据。该结构体在创建Java虚拟机时被返回,在JNI中创建JVM的函数为JNI_CreateJavaVM。 在JNI的双向调用(JavaC/C++)过程中,JNIEnv是一个很
文章目录Android性能优化——电量1. 理解电池消耗2. Battery Historian3. 充电状态和BatteryManager4. Wakelock和电池消耗5. 网络和电池消耗6. 使用JobScheduler Android性能优化——电量 原Udacity视频链接 1. 理解电池消耗 手机各个硬件模块的耗电量是不一样的,有些模块非常耗电,而有些模块则相对显得耗电量小很多。 Android Framework 电源子系统 的分析主要分为以下部分: Android Framework 电源子系统(01)PowerManagerService启动分析 Android Framework 电源子系统(02)系统休眠wakelock机制 Android Framework 电源子系统(03)核心方法updatePowerStateLoc...
BatteryStatsHelper这个类主要是统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计。 一般使用BatteryStatsHelper这个类,先要new一个实例,然后再调用create函数: 下面我们就先从create分析,两种create方法,其中sStatsXfer是静态的 [java] view plaincopy
FusionCompute CNA-8.0.0-arm是华为公司推出的一款基于ARM架构的新一代云计算虚拟化平台,该平台具有稳定性高、性能强、适用范围广等优点,在云计算领域具有广泛的应用价值。该平台提供了高效的虚拟化能力,能够为企业客户提供更加安全和可靠的虚拟化解决方案。 作为一种云计算服务提供平台,FusionCompute CNA-8.0.0-arm具有一些独特的特点。首先,该平台采用最新的ARM架构,可以支持更加广泛的应用场景,具有很强的通用性和可扩展性。其次,该平台提供了高度智能化的管理功能,可以自动化分配计算资源,优化资源的运用效率,并且具有严格的安全保障机制,可以有效保护客户的数据安全。 同时,FusionCompute CNA-8.0.0-arm采用了独特的分布式架构,可以快速响应客户的需求,为客户提供更加优质的服务。其虚拟化能力卓越,可以满足客户在不同行业的需求,例如金融、交通、医疗、教育等领域的应用需求,并可提供高效的云计算解决方案。 总之,FusionCompute CNA-8.0.0-arm是一款非常优秀的云计算虚拟化平台,是企业客户在建立云计算服务时的绝佳选择。该平台采用最新的ARM架构,具有通用性和可扩展性,且具有优异的性能表现,可以满足各种不同行业的需求。同时,该平台具有智能化的管理功能和严格的安全保障机制,可以为企业客户提供更加安全可靠、高效的云计算服务。