转载自http://blog.csdn.net/green1900/article/details/42427871
现在诸多关于电池管理的应用做的极其绚烂,可实现如耗电应用排行、剩余时间计算、关闭耗电程序以节省电量等功能,这两天对此模块进行了研究,总结如下。
首先解释下各软硬件耗电量的计算。假设设备(如WIFI)单位时间内消耗的电量为w,运行时间为t,则其在这段时间内的耗电量为W=w*t。根据物理学中的知识,电功率(即所谓电量)计算公式为W=UIt,其中U为电压值,I为电流值,t为运行时间。由于在一部机器中,电压值U是恒定不变的(一般如此),因此可以忽略掉参数U,单独通过电流及时间即可表示电量(比如电池容量为2000mA、2500mA等,以mA为单位进行恒量)。根据以上描述,只要我们获得了某程序或某设备运行的时间,以及其运行时所需要电流值,则可以计算出其消耗的电量(以上理论会在代码中体现)。
某程序或硬件设备的运行时间可以分别通过BatteryStats.Uid.Proc和BatteryStatsImpl中的相关接口获得(后文分析代码时会见到),本文主要讲下电流值(即单位时间消耗电量)的获取。
1. PowerProfile.Java
OK,不多废话,直接给出系统中提供的接口--./frameworks/base/core/java/com/Android/internal/os/PowerProfile.java。此类提供了如下接口:
(1)public double getAveragePower(String type)
此方法返回在type子系统下消耗的电流值,单位为mA。type可取PowerProfile中定义的常量值,包括POWER_CPU_IDLE(CPU空闲时),POWER_CPU_ACTIVE(CPU处于活动时),POWER_WIFI_ON(WIFI开启时)等各种状态。例如,如下调用getAveragePower(POWER_CPU_ACTIVE)将返回CPU处于活动时的电流值;getAveragePower(POWER_WIFI_ON)将返回维持WIFI启动状态所需的电流值。结合之前的描述,假设WIFI开启的时间为t(假设此段时间未使用WIFI传输数据,因为WIFI传输数据需要额外的电能消耗),那么在此段时间内WIFI所消耗的电量为W=t*getAveragePower(POWER_WIFI_ON)。
type可取如下值:
/** * No power consumption, or accounted for elsewhere. */ public static final String POWER_NONE = "none"; /** * Power consumption when CPU is in power collapse mode. */ public static final String POWER_CPU_IDLE = "cpu.idle"; /** * Power consumption when CPU is awake (when a wake lock is held). This * should be 0 on devices that can go into full CPU power collapse even * when a wake lock is held. Otherwise, this is the power consumption in * addition to POWERR_CPU_IDLE due to a wake lock being held but with no * CPU activity. */ public static final String POWER_CPU_AWAKE = "cpu.awake"; /** * Power consumption when CPU is in power collapse mode. */ public static final String POWER_CPU_ACTIVE = "cpu.active"; /** * Power consumption when WiFi driver is scanning for networks. */ public static final String POWER_WIFI_SCAN = "wifi.scan"; /** * Power consumption when WiFi driver is on. */ public static final String POWER_WIFI_ON = "wifi.on"; /** * Power consumption when WiFi driver is transmitting/receiving. */ public static final String POWER_WIFI_ACTIVE = "wifi.active"; /** * Power consumption when GPS is on. */ public static final String POWER_GPS_ON = "gps.on"; /** * Power consumption when Bluetooth driver is on. */ public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; /** * Power consumption when Bluetooth driver is transmitting/receiving. */ public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; /** * Power consumption when Bluetooth driver gets an AT command. */ public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; /** * Power consumption when screen is on, not including the backlight power. */ public static final String POWER_SCREEN_ON = "screen.on"; /** * Power consumption when cell radio is on but not on a call. */ public static final String POWER_RADIO_ON = "radio.on"; /** * Power consumption when cell radio is hunting for a signal. */ public static final String POWER_RADIO_SCANNING = "radio.scanning"; /** * Power consumption when talking on the phone. */ public static final String POWER_RADIO_ACTIVE = "radio.active"; /** * Power consumption at full backlight brightness. If the backlight is at * 50% brightness, then this should be multiplied by 0.5 */ public static final String POWER_SCREEN_FULL = "screen.full"; /** * Power consumed by the audio hardware when playing back audio content. This is in addition * to the CPU power, probably due to a DSP and / or amplifier. */ public static final String POWER_AUDIO = "dsp.audio"; /** * Power consumed by any media hardware when playing back video content. This is in addition * to the CPU power, probably due to a DSP. */ public static final String POWER_VIDEO = "dsp.video";
代码段1 PowerProfile.java中定义的子系统类型
(2) public double getAveragePower(String type, int level)
相比于方法(1),此接口需要传入参数level,现在来解释下level的含义。我们知道,android系统中CPU可以以多种速度运行(假设分别为600MHz,800MHz,1GHZ等),速度不同时CPU消耗的电量也不同,参数level即代表不同的运行频率,显然,方法getAveragePower(String type, int level)将返回type子系统在CPU运行速度级别为level时单位时间内所消耗的电量(即电流值)。
(3)public double getBatteryCapacity() 获取电池总电量。
(4)public int getNumSpeedSteps() 获取CPU可以以几种速度运行。
2. power_profile.xml
事实上,通过阅读PowerProfile.java代码及相关注释即可知,此类中各接口返回的电流等数值都是通过读取power_profile.xml文件获得的,即各种子系统消耗的电量值、CPU运行速度值、总电量等信息都是以固定值的形式存储在power_profile.xml中。由于硬件之间的差异,各子系统耗电信息是不同的,因此此文件需要各生产厂商进行定制。android系统原生的power_profile.xml文件的存放路径为:frameworks/base/core/java/com/android/internal/os/PowerProfile.java,经过各硬件厂商定制后,存放路径可能发生变化,如三星某型号的power_profile.xml路径:device/samsung/maguro/overlay/frameworks/base/core/res/res/xml/power_profile.xml,其内容如下:
<device name="Android"> <!-- All values are in mA except as noted --> <item name="none">0</item> <item name="screen.on">200</item> <!-- min brite --> <item name="bluetooth.active">150</item> <item name="bluetooth.on">1</item> <item name="bluetooth.at">1</item> <!-- TBD --> <item name="screen.full">160</item> <!-- 360 max on calendar --> <item name="wifi.on">1</item> <!-- wifi处于开启状态,但未传输数据 --> <item name="wifi.active">150</item> <!-- wifi处于传输数据状态 --> <item name="wifi.scan">200</item> <!-- wifi处于搜索热点状态 --> <item name="dsp.audio">150</item> <item name="dsp.video">200</item> <item name="radio.active">150</item> <item name="gps.on">55</item> <item name="battery.capacity">1750</item> <item name="radio.scanning">90</item> <!-- TBD --> <!-- Current consumed by the radio at different signal strengths, when paging --> <array name="radio.on"> <!-- 1 entry per signal strength bin, TBD --> <value>3.0</value> <value>3.0</value> </array> <array name="cpu.speeds"> <value>350000</value> <!-- 3.5MHz --> <value>700000</value> <!-- 7.0MHz --> <value>920000</value> <!-- 9.2MHz --> <value>1200000</value> <!-- 1.2GHz --> </array> <!-- Power consumption in suspend --> <item name="cpu.idle">7</item> <!-- Power consumption due to wake lock held --> <item name="cpu.awake">20</item> <!-- Power consumption at different speeds --> <array name="cpu.active"> <value>120</value> <value>228</value> <value>299</value> <value>397</value> </array> </device>
代码段2 power_profile.xml内容
显然,从power_profile.xml可知,此型号机器可以以3.5MHz、7.0MHz、9.2MHz、1.2GHz四种速度运行(<array name="cpu.speeds">定义),且在此四种运行速度下CPU的耗电量分别为120mAh,228mAh,299mAh及397mAh(<array name="cpu.active">)。通过对比代码段1可知,PowerProfile.java中定义的常量即对应于power_profile.xml中各属性名。因此,PowerProfile.java只是用于读取power_profile.xml的接口而已,后者才是存储系统耗电信息的核心文件。
通过上述分析可知,android系统对于电池电量信息统计还是提供了数据与接口的(本人菜鸟,之前一直认为耗电及剩余时间信息是由先前一定时间内的耗电量统计而来的,分析了settings中的电池相关的代码后才知是有据可依的)。根据PowerProfile.java及power_profile.xml,我们可以计算出各应用或设备的耗电量、电池剩余时间等信息,相关内容将在后续文章中描述。
PowerProfile.java源码
1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.os; 18 19 20 import android.content.Context; 21 import android.content.res.XmlResourceParser; 22 23 import com.android.internal.util.XmlUtils; 24 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.HashMap; 31 32 /** 33 * Reports power consumption values for various device activities. Reads values from an XML file. 34 * Customize the XML file for different devices. 35 * [hidden] 36 */ 37 public class PowerProfile { 38 39 /** 40 * No power consumption, or accounted for elsewhere. 41 */ 42 public static final String POWER_NONE = "none"; 43 44 /** 45 * Power consumption when CPU is in power collapse mode. 46 */ 47 public static final String POWER_CPU_IDLE = "cpu.idle"; 48 49 /** 50 * Power consumption when CPU is in power collapse mode. 51 */ 52 public static final String POWER_CPU_ACTIVE = "cpu.active"; 53 54 /** 55 * Power consumption when WiFi driver is scanning for networks. 56 */ 57 public static final String POWER_WIFI_SCAN = "wifi.scan"; 58 59 /** 60 * Power consumption when WiFi driver is on. 61 */ 62 public static final String POWER_WIFI_ON = "wifi.on"; 63 64 /** 65 * Power consumption when WiFi driver is transmitting/receiving. 66 */ 67 public static final String POWER_WIFI_ACTIVE = "wifi.active"; 68 69 /** 70 * Power consumption when GPS is on. 71 */ 72 public static final String POWER_GPS_ON = "gps.on"; 73 74 /** 75 * Power consumption when Bluetooth driver is on. 76 */ 77 public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; 78 79 /** 80 * Power consumption when Bluetooth driver is transmitting/receiving. 81 */ 82 public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; 83 84 /** 85 * Power consumption when Bluetooth driver gets an AT command. 86 */ 87 public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; 88 89 /** 90 * Power consumption when screen is on, not including the backlight power. 91 */ 92 public static final String POWER_SCREEN_ON = "screen.on"; 93 94 /** 95 * Power consumption when cell radio is on but not on a call. 96 */ 97 public static final String POWER_RADIO_ON = "radio.on"; 98 99 /** 100 * Power consumption when cell radio is hunting for a signal. 101 */ 102 public static final String POWER_RADIO_SCANNING = "radio.scanning"; 103 104 /** 105 * Power consumption when talking on the phone. 106 */ 107 public static final String POWER_RADIO_ACTIVE = "radio.active"; 108 109 /** 110 * Power consumption at full backlight brightness. If the backlight is at 111 * 50% brightness, then this should be multiplied by 0.5 112 */ 113 public static final String POWER_SCREEN_FULL = "screen.full"; 114 115 /** 116 * Power consumed by the audio hardware when playing back audio content. This is in addition 117 * to the CPU power, probably due to a DSP and / or amplifier. 118 */ 119 public static final String POWER_AUDIO = "dsp.audio"; 120 121 /** 122 * Power consumed by any media hardware when playing back video content. This is in addition 123 * to the CPU power, probably due to a DSP. 124 */ 125 public static final String POWER_VIDEO = "dsp.video"; 126 127 public static final String POWER_CPU_SPEEDS = "cpu.speeds"; 128 129 static final HashMap<String, Object> sPowerMap = new HashMap(); 130 131 private static final String TAG_DEVICE = "device"; 132 private static final String TAG_ITEM = "item"; 133 private static final String TAG_ARRAY = "array"; 134 private static final String TAG_ARRAYITEM = "value"; 135 private static final String ATTR_NAME = "name"; 136 137 public PowerProfile(Context context) { 138 // Read the XML file for the given profile (normally only one per 139 // device) 140 if (sPowerMap.size() == 0) { 141 readPowerValuesFromXml(context); 142 } 143 } 144 145 private void readPowerValuesFromXml(Context context) { 146 int id = com.android.internal.R.xml.power_profile; 147 XmlResourceParser parser = context.getResources().getXml(id); 148 boolean parsingArray = false; 149 ArrayList<Double> array = new ArrayList(); 150 String arrayName = null; 151 152 try { 153 XmlUtils.beginDocument(parser, TAG_DEVICE); 154 155 while (true) { 156 XmlUtils.nextElement(parser); 157 158 String element = parser.getName(); 159 if (element == null) break; 160 161 if (parsingArray && !element.equals(TAG_ARRAYITEM)) { 162 // Finish array 163 sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); 164 parsingArray = false; 165 } 166 if (element.equals(TAG_ARRAY)) { 167 parsingArray = true; 168 array.clear(); 169 arrayName = parser.getAttributeValue(null, ATTR_NAME); 170 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { 171 String name = null; 172 if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); 173 if (parser.next() == XmlPullParser.TEXT) { 174 String power = parser.getText(); 175 double value = 0; 176 try { 177 value = Double.valueOf(power); 178 } catch (NumberFormatException nfe) { 179 } 180 if (element.equals(TAG_ITEM)) { 181 sPowerMap.put(name, value); 182 } else if (parsingArray) { 183 array.add(value); 184 } 185 } 186 } 187 } 188 if (parsingArray) { 189 sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); 190 } 191 } catch (XmlPullParserException e) { 192 throw new RuntimeException(e); 193 } catch (IOException e) { 194 throw new RuntimeException(e); 195 } finally { 196 parser.close(); 197 } 198 } 199 200 /** 201 * Returns the average current in mA consumed by the subsystem 202 * @param type the subsystem type 203 * @return the average current in milliAmps. 204 */ 205 public double getAveragePower(String type) { 206 if (sPowerMap.containsKey(type)) { 207 Object data = sPowerMap.get(type); 208 if (data instanceof Double[]) { 209 return ((Double[])data)[0]; 210 } else { 211 return (Double) sPowerMap.get(type); 212 } 213 } else { 214 return 0; 215 } 216 } 217 218 /** 219 * Returns the average current in mA consumed by the subsystem for the given level. 220 * @param type the subsystem type 221 * @param level the level of power at which the subsystem is running. For instance, the 222 * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) 223 * If there is no data for multiple levels, the level is ignored. 224 * @return the average current in milliAmps. 225 */ 226 public double getAveragePower(String type, int level) { 227 if (sPowerMap.containsKey(type)) { 228 Object data = sPowerMap.get(type); 229 if (data instanceof Double[]) { 230 final Double[] values = (Double[]) data; 231 if (values.length > level && level >= 0) { 232 return values[level]; 233 } else if (level < 0) { 234 return 0; 235 } else { 236 return values[values.length - 1]; 237 } 238 } else { 239 return (Double) data; 240 } 241 } else { 242 return 0; 243 } 244 } 245 246 public int getNumSpeedSteps() { 247 Object value = sPowerMap.get(POWER_CPU_SPEEDS); 248 if (value != null && value instanceof Double[]) { 249 return ((Double[])value).length; 250 } 251 return 1; // Only one speed 252 } 253 }