午夜稻草人

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

转载自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 }

 

posted on 2016-12-14 11:27  午夜稻草人  阅读(2436)  评论(0编辑  收藏  举报