浅谈Android系统的电量统计机制

背景

为了完善产品的工具属性,进行了Android统计电量相关特性的调研。

提出问题

  • Android 对于每个进程电量是如何统计的
  • 第三方app如何读取系统的电量数据呢

解决问题

问题1 : Android如何计算电量

主要分为软件层硬件层
先看软件层的计算规则:
软件层的电量是以Uid为单位统计的,分别计算每一个Uid的如下几项(源代码代码位于com.android.settings.fuelgauge.BatteryStatsHelper 的processAppUsage 接口)

  • process cpu time

SpeedStep 机制可以动态调整CPU频率,当CPU使用率低下或接近零的时候动态降低CPU的倍率,令其工作频率下降,从而降低电压、功耗以及发热;而一旦监测到CPU使用率很高的时候,立即恢复到原始的速率工作
--引自百度百科

//Returns the number of speeds that the CPU can be run at. 
final int speedSteps = mPowerProfile.getNumSpeedSteps();  
//存储不同频率下的平均电流值(mA)
 final double[] powerCpuNormal = new double[speedSteps]; 
final long[] cpuSpeedStepTimes = new long[speedSteps];
 for (int p = 0; p < speedSteps; p++)
 {
 powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); 
}  
//cpu 耗电 计算部分 
for (int step = 0; step < speedSteps; step++) 
{ 
double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; 
processPower += ratio * tmpCpuTime * powerCpuNormal[step]; 
} 

从而不难理解
计算公式: power = CPU每个频率等级下工作的时间比例 / CPU工作总时间 * 应用运行总时间

  • process wake lock usage

计算公式 :power = wakelockTime *averagePower(POWER_CPU_AWAKE)

  • Add cost of mobile traffic (*注:R:receive(接收) T: transmit (传输) 下同 *)

计算公式
power=(mobileRx + mobileTx) * mobilePowerPerByte

  • Add cost of wifi traffic

计算公式: power = (wifiRx + wifiTx) * wifiPowerPerByte;

  • Add cost of keeping WIFI running.

计算公式: power = wifiRunningTimeMs * averagePower(POWER_WIFI_ON)

  • Add cost of WIFI scans wifi

计算公式:power = wifiScanTimeMs * averagerPower(POWER_WIFI_SCAN)

  • Process Sensor usage 计算uid的每个传感器的耗电

计算公式 : power = averagePower(不同传感器的耗电) * sensorTime

硬件层主要包括如下几项:

  • 通话时间

计算公式
power = phoneOnTimeMs * averagerPower(POWER_RADIO_ACTIVE)

  • 待机耗电

计算公式
power = idleTimeMs * averagePower(POWER_CPU_IDLE)

  • wifi 开启时的耗电量

计算公式
power = runningTimeMs * averagePower(POWER_WIFI_ON) + wifi uid 耗电

  • 蓝牙耗电量

计算公式:power = btOnTimeMs * averagePower(POWER_BLUETOOTH_ON) + btPingCount * averagePower(POWER_BLUETOOTH_AT_CMD)+ bluetooth uid 耗电

  • 手机基站信号耗电量

手机信号有五个信号强度,耗电量是每个信号强度的耗电量的累加。
计算公式:power = strengthTimeMs * averagePower(POWER_RADIO_ON) + scanningTimeMs * averagePower(POWER_RADIO_SCANNING)

  • 屏幕耗电

计算公式 :
power = screenOnTimeMs * averagePower(POWER_SCREEN_ON) + brightnessTime(不同亮度的时间)* screenBinPower(不同亮度的电流强度)

问题2:如何访问系统的电量的数据

通过阅读Settings.apk的源代码发现电量的接口主要依赖于以下几个类 参考为Kitkat 版本源码

  • com.android.server.am.BatteryStatsService
  • com.android.internal.os.BatteryStatsImpl
  • com.android.internal.os.PowerProfile
  • com.android.settings.fuelgauge.BatteryStatsHelper
    由此可知有关访问电量数据的接口是internal 的,作为第三方无法正常的调用,因此想到反射。
    在BatteryStatsHelper类中可以看到 ,是通过如下代码访问电量信息的,
    byte[] data = mBatteryInfo.getStatistics();
    实际调用的是BatteryStatsService 类的getStatistics() 接口,接着看下这个接口的实现:
//这个接口需要系统权限,第三方app无法正常调用,为了测试把app放置在system/priv-app目录下 
public byte[] getStatistics() { 
mContext.enforceCallingPermission( android.Manifest.permission.BATTERY_STATS, null); 
Parcel out = Parcel.obtain(); 
mStats.writeToParcel(out, 0); 
byte[] data = out.marshall(); 
out.recycle(); return data;
}

可以看出这个接口需要系统权限,即便在AndroidManifext.xml申请第三方app也是无法正常调用,为了测试方便把app放置在system/priv-app目录下,绕过这层权限限制。

具体实现

主要逻辑集中在compat层,实现了对应原生系统每个类的反射逻辑以调用getStatistics 为例:

private byte[] getStatics() { 
try {
 Class<?> serviceManagerClass = Class.forName("android.os.ServiceManager"); 
Method getServiceMethod = serviceManagerClass.getMethod("getService", java.lang.String.class); 
Object batteryStatsService = getServiceMethod.invoke(null, "batterystats"); 
Class<?> batteryStatsStubClass = Class.forName("com.android.internal.app.IBatteryStats$Stub"); 
Object batteryStatsStubObject = batteryStatsStubClass.getMethod("asInterface", android.os.IBinder.class).invoke(null, batteryStatsService);
 return (byte[]) batteryStatsStubClass.getMethod("getStatistics").invoke(batteryStatsStubObject); 
} catch (NoSuchMethodException e) { e.printStackTrace(); 
} catch (IllegalAccessException e) { e.printStackTrace(); 
} catch (InvocationTargetException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } throw new UnsupportedOperationException("getStatics unsupport operation"); 
}

运行结果

原生系统设置

Paste_Image.png

BatteryDemo:

Paste_Image.png

由此可见,排序与系统的电量排名和电量占比基本保持一致

总结

本文主要记录了笔者在调研Android 电量计算特性时的一些收获,分享给大家,距离真实的产品化还有很多工作要做,这篇文章暂不一一赘述。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容