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 awake (when a wake lock is held).  This
51      * should be 0 on devices that can go into full CPU power collapse even
52      * when a wake lock is held.  Otherwise, this is the power consumption in
53      * addition to POWERR_CPU_IDLE due to a wake lock being held but with no
54      * CPU activity.
55      */
56     public static final String POWER_CPU_AWAKE = "cpu.awake";
57 
58     /**
59      * Power consumption when CPU is in power collapse mode.
60      */
61     public static final String POWER_CPU_ACTIVE = "cpu.active";
62 
63     /**
64      * Power consumption when WiFi driver is scanning for networks.
65      */
66     public static final String POWER_WIFI_SCAN = "wifi.scan";
67 
68     /**
69      * Power consumption when WiFi driver is on.
70      */
71     public static final String POWER_WIFI_ON = "wifi.on";
72 
73     /**
74      * Power consumption when WiFi driver is transmitting/receiving.
75      */
76     public static final String POWER_WIFI_ACTIVE = "wifi.active";
77 
78     /**
79      * Power consumption when GPS is on.
80      */
81     public static final String POWER_GPS_ON = "gps.on";
82 
83     /**
84      * Power consumption when Bluetooth driver is on.
85      */
86     public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
87 
88     /**
89      * Power consumption when Bluetooth driver is transmitting/receiving.
90      */
91     public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
92 
93     /**
94      * Power consumption when Bluetooth driver gets an AT command.
95      */
96     public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
97 
98     /**
99      * Power consumption when screen is on, not including the backlight power.
100      */
101     public static final String POWER_SCREEN_ON = "screen.on";
102 
103     /**
104      * Power consumption when cell radio is on but not on a call.
105      */
106     public static final String POWER_RADIO_ON = "radio.on";
107 
108     /**
109      * Power consumption when cell radio is hunting for a signal.
110      */
111     public static final String POWER_RADIO_SCANNING = "radio.scanning";
112 
113     /**
114      * Power consumption when talking on the phone.
115      */
116     public static final String POWER_RADIO_ACTIVE = "radio.active";
117 
118     /**
119      * Power consumption at full backlight brightness. If the backlight is at
120      * 50% brightness, then this should be multiplied by 0.5
121      */
122     public static final String POWER_SCREEN_FULL = "screen.full";
123 
124     /**
125      * Power consumed by the audio hardware when playing back audio content. This is in addition
126      * to the CPU power, probably due to a DSP and / or amplifier.
127      */
128     public static final String POWER_AUDIO = "dsp.audio";
129 
130     /**
131      * Power consumed by any media hardware when playing back video content. This is in addition
132      * to the CPU power, probably due to a DSP.
133      */
134     public static final String POWER_VIDEO = "dsp.video";
135 
136     /**
137      * Power consumption when camera flashlight is on.
138      */
139     public static final String POWER_FLASHLIGHT = "camera.flashlight";
140 
141     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
142 
143     /**
144      * Power consumed by wif batched scaning.  Broken down into bins by
145      * Channels Scanned per Hour.  May do 1-720 scans per hour of 1-100 channels
146      * for a range of 1-72,000.  Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)!
147      */
148     public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan";
149 
150     /**
151      * Battery capacity in milliAmpHour (mAh).
152      */
153     public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
154 
155     static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
156 
157     private static final String TAG_DEVICE = "device";
158     private static final String TAG_ITEM = "item";
159     private static final String TAG_ARRAY = "array";
160     private static final String TAG_ARRAYITEM = "value";
161     private static final String ATTR_NAME = "name";
162 
PowerProfile(Context context)163     public PowerProfile(Context context) {
164         // Read the XML file for the given profile (normally only one per
165         // device)
166         if (sPowerMap.size() == 0) {
167             readPowerValuesFromXml(context);
168         }
169     }
170 
readPowerValuesFromXml(Context context)171     private void readPowerValuesFromXml(Context context) {
172         int id = com.android.internal.R.xml.power_profile;
173         XmlResourceParser parser = context.getResources().getXml(id);
174         boolean parsingArray = false;
175         ArrayList<Double> array = new ArrayList<Double>();
176         String arrayName = null;
177 
178         try {
179             XmlUtils.beginDocument(parser, TAG_DEVICE);
180 
181             while (true) {
182                 XmlUtils.nextElement(parser);
183 
184                 String element = parser.getName();
185                 if (element == null) break;
186 
187                 if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
188                     // Finish array
189                     sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
190                     parsingArray = false;
191                 }
192                 if (element.equals(TAG_ARRAY)) {
193                     parsingArray = true;
194                     array.clear();
195                     arrayName = parser.getAttributeValue(null, ATTR_NAME);
196                 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
197                     String name = null;
198                     if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
199                     if (parser.next() == XmlPullParser.TEXT) {
200                         String power = parser.getText();
201                         double value = 0;
202                         try {
203                             value = Double.valueOf(power);
204                         } catch (NumberFormatException nfe) {
205                         }
206                         if (element.equals(TAG_ITEM)) {
207                             sPowerMap.put(name, value);
208                         } else if (parsingArray) {
209                             array.add(value);
210                         }
211                     }
212                 }
213             }
214             if (parsingArray) {
215                 sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
216             }
217         } catch (XmlPullParserException e) {
218             throw new RuntimeException(e);
219         } catch (IOException e) {
220             throw new RuntimeException(e);
221         } finally {
222             parser.close();
223         }
224     }
225 
226     /**
227      * Returns the average current in mA consumed by the subsystem
228      * @param type the subsystem type
229      * @return the average current in milliAmps.
230      */
getAveragePower(String type)231     public double getAveragePower(String type) {
232         if (sPowerMap.containsKey(type)) {
233             Object data = sPowerMap.get(type);
234             if (data instanceof Double[]) {
235                 return ((Double[])data)[0];
236             } else {
237                 return (Double) sPowerMap.get(type);
238             }
239         } else {
240             return 0;
241         }
242     }
243 
244     /**
245      * Returns the average current in mA consumed by the subsystem for the given level.
246      * @param type the subsystem type
247      * @param level the level of power at which the subsystem is running. For instance, the
248      *  signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
249      *  If there is no data for multiple levels, the level is ignored.
250      * @return the average current in milliAmps.
251      */
getAveragePower(String type, int level)252     public double getAveragePower(String type, int level) {
253         if (sPowerMap.containsKey(type)) {
254             Object data = sPowerMap.get(type);
255             if (data instanceof Double[]) {
256                 final Double[] values = (Double[]) data;
257                 if (values.length > level && level >= 0) {
258                     return values[level];
259                 } else if (level < 0) {
260                     return 0;
261                 } else {
262                     return values[values.length - 1];
263                 }
264             } else {
265                 return (Double) data;
266             }
267         } else {
268             return 0;
269         }
270     }
271 
272     /**
273      * Returns the battery capacity, if available, in milli Amp Hours. If not available,
274      * it returns zero.
275      * @return the battery capacity in mAh
276      */
getBatteryCapacity()277     public double getBatteryCapacity() {
278         return getAveragePower(POWER_BATTERY_CAPACITY);
279     }
280 
281     /**
282      * Returns the number of speeds that the CPU can be run at.
283      * @return
284      */
getNumSpeedSteps()285     public int getNumSpeedSteps() {
286         Object value = sPowerMap.get(POWER_CPU_SPEEDS);
287         if (value != null && value instanceof Double[]) {
288             return ((Double[])value).length;
289         }
290         return 1; // Only one speed
291     }
292 }
293