![]() |
年轻有为的热带鱼 · 2022年5月31日外交部发言人赵立坚主持例 ...· 1 年前 · |
![]() |
大气的铁板烧 · 24 大力出奇迹-宿舍里的动物园-🌈️包子漫畫· 1 年前 · |
![]() |
潇洒的充电器 · 智谷趋势是怎样的一个组织机构? - 知乎· 1 年前 · |
![]() |
含蓄的草稿纸 · 蓝鲤镇 - 搜狗百科· 1 年前 · |
![]() |
另类的眼镜 · 病娇暴君改拿绿茶剧本漫画免费 - ...· 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
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为true4515 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中整个耗电统计的架构,大概如下图所示:
1. 概要手机耗电量主要涵盖两个方面:硬件层面的功耗和软件层面的电量。本文介绍的电量统计的原理,并不涉及到硬件层面的功耗设计,仅从软件层面围绕以下几个问题进行分析: Android如何启动电量统计服务? 电量统计涉及到哪一些硬件模块? 如何计算一个应用程序的耗电量? 电量统计需要完成哪些具体工作?手机有很多硬件模块:CPU,蓝牙,GPS,显示... 从BatteryStatsService的启动时序图可以看出,BatteryStatsService服务是在ActivityManagerService服务中启动的 1. 在SystemServer中startBoots
每个模块都会监控自己的关键状态。 当状态发生改变时,直接或间接地通知到BBS。 BSS收到通知后,通过各种以note开头的函数,将通知递交给BSImpl进行实际的处理。 BSImpl完成启动或停止对应模块的StopWatchTimer,完成对该模块耗电时间的统计。SystemServer在启动BatteryService时,一次会调用它的构造函数,onStart,onBootPhase方法;解析来我们分析一下这些方法: 1.Constructor方法: public BatteryService(Context context) { super(context); mContext = context; mHandler = new Handler(true /*async*/); //电源呼吸灯 mLed = new LedBatteryStasService的主要功能是收集系统中各模块和应用进程的用电情况。 因此,我们可以认为BatteryStatsService是Android中的“电表”。 只不过这个电表比较智能,不是单纯地统计整体的耗电,而是分门别类的统计每个部分的耗电情况。 接下来我们就分析一下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 --------- beginningJava语言的执行环境是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 plaincopyFusionCompute 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架构,具有通用性和可扩展性,且具有优异的性能表现,可以满足各种不同行业的需求。同时,该平台具有智能化的管理功能和严格的安全保障机制,可以为企业客户提供更加安全可靠、高效的云计算服务。
![]() |
大气的铁板烧 · 24 大力出奇迹-宿舍里的动物园-🌈️包子漫畫 1 年前 |
![]() |
潇洒的充电器 · 智谷趋势是怎样的一个组织机构? - 知乎 1 年前 |
![]() |
含蓄的草稿纸 · 蓝鲤镇 - 搜狗百科 1 年前 |