• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Resources;
22 import android.content.res.XmlResourceParser;
23 
24 import com.android.internal.util.XmlUtils;
25 
26 import org.xmlpull.v1.XmlPullParser;
27 import org.xmlpull.v1.XmlPullParserException;
28 
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 
33 /**
34  * Reports power consumption values for various device activities. Reads values from an XML file.
35  * Customize the XML file for different devices.
36  * [hidden]
37  */
38 public class PowerProfile {
39 
40     /**
41      * No power consumption, or accounted for elsewhere.
42      */
43     public static final String POWER_NONE = "none";
44 
45     /**
46      * Power consumption when CPU is in power collapse mode.
47      */
48     public static final String POWER_CPU_IDLE = "cpu.idle";
49 
50     /**
51      * Power consumption when CPU is awake (when a wake lock is held).  This
52      * should be 0 on devices that can go into full CPU power collapse even
53      * when a wake lock is held.  Otherwise, this is the power consumption in
54      * addition to POWERR_CPU_IDLE due to a wake lock being held but with no
55      * CPU activity.
56      */
57     public static final String POWER_CPU_AWAKE = "cpu.awake";
58 
59     /**
60      * Power consumption when CPU is in power collapse mode.
61      */
62     @Deprecated
63     public static final String POWER_CPU_ACTIVE = "cpu.active";
64 
65     /**
66      * Power consumption when WiFi driver is scanning for networks.
67      */
68     public static final String POWER_WIFI_SCAN = "wifi.scan";
69 
70     /**
71      * Power consumption when WiFi driver is on.
72      */
73     public static final String POWER_WIFI_ON = "wifi.on";
74 
75     /**
76      * Power consumption when WiFi driver is transmitting/receiving.
77      */
78     public static final String POWER_WIFI_ACTIVE = "wifi.active";
79 
80     //
81     // Updated power constants. These are not estimated, they are real world
82     // currents and voltages for the underlying bluetooth and wifi controllers.
83     //
84 
85     public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
86     public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
87     public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
88     public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
89 
90     public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
91     public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
92     public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
93     public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
94             "bluetooth.controller.voltage";
95 
96     /**
97      * Power consumption when GPS is on.
98      */
99     public static final String POWER_GPS_ON = "gps.on";
100 
101     /**
102      * Power consumption when Bluetooth driver is on.
103      */
104     public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
105 
106     /**
107      * Power consumption when Bluetooth driver is transmitting/receiving.
108      */
109     public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
110 
111     /**
112      * Power consumption when Bluetooth driver gets an AT command.
113      */
114     public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
115 
116 
117     /**
118      * Power consumption when screen is on, not including the backlight power.
119      */
120     public static final String POWER_SCREEN_ON = "screen.on";
121 
122     /**
123      * Power consumption when cell radio is on but not on a call.
124      */
125     public static final String POWER_RADIO_ON = "radio.on";
126 
127     /**
128      * Power consumption when cell radio is hunting for a signal.
129      */
130     public static final String POWER_RADIO_SCANNING = "radio.scanning";
131 
132     /**
133      * Power consumption when talking on the phone.
134      */
135     public static final String POWER_RADIO_ACTIVE = "radio.active";
136 
137     /**
138      * Power consumption at full backlight brightness. If the backlight is at
139      * 50% brightness, then this should be multiplied by 0.5
140      */
141     public static final String POWER_SCREEN_FULL = "screen.full";
142 
143     /**
144      * Power consumed by the audio hardware when playing back audio content. This is in addition
145      * to the CPU power, probably due to a DSP and / or amplifier.
146      */
147     public static final String POWER_AUDIO = "dsp.audio";
148 
149     /**
150      * Power consumed by any media hardware when playing back video content. This is in addition
151      * to the CPU power, probably due to a DSP.
152      */
153     public static final String POWER_VIDEO = "dsp.video";
154 
155     /**
156      * Average power consumption when camera flashlight is on.
157      */
158     public static final String POWER_FLASHLIGHT = "camera.flashlight";
159 
160     /**
161      * Average power consumption when the camera is on over all standard use cases.
162      *
163      * TODO: Add more fine-grained camera power metrics.
164      */
165     public static final String POWER_CAMERA = "camera.avg";
166 
167     @Deprecated
168     public static final String POWER_CPU_SPEEDS = "cpu.speeds";
169 
170     /**
171      * Power consumed by wif batched scaning.  Broken down into bins by
172      * Channels Scanned per Hour.  May do 1-720 scans per hour of 1-100 channels
173      * for a range of 1-72,000.  Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)!
174      */
175     public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan";
176 
177     /**
178      * Battery capacity in milliAmpHour (mAh).
179      */
180     public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
181 
182     static final HashMap<String, Object> sPowerMap = new HashMap<>();
183 
184     private static final String TAG_DEVICE = "device";
185     private static final String TAG_ITEM = "item";
186     private static final String TAG_ARRAY = "array";
187     private static final String TAG_ARRAYITEM = "value";
188     private static final String ATTR_NAME = "name";
189 
PowerProfile(Context context)190     public PowerProfile(Context context) {
191         // Read the XML file for the given profile (normally only one per
192         // device)
193         if (sPowerMap.size() == 0) {
194             readPowerValuesFromXml(context);
195         }
196         initCpuClusters();
197     }
198 
readPowerValuesFromXml(Context context)199     private void readPowerValuesFromXml(Context context) {
200         int id = com.android.internal.R.xml.power_profile;
201         final Resources resources = context.getResources();
202         XmlResourceParser parser = resources.getXml(id);
203         boolean parsingArray = false;
204         ArrayList<Double> array = new ArrayList<Double>();
205         String arrayName = null;
206 
207         try {
208             XmlUtils.beginDocument(parser, TAG_DEVICE);
209 
210             while (true) {
211                 XmlUtils.nextElement(parser);
212 
213                 String element = parser.getName();
214                 if (element == null) break;
215 
216                 if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
217                     // Finish array
218                     sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
219                     parsingArray = false;
220                 }
221                 if (element.equals(TAG_ARRAY)) {
222                     parsingArray = true;
223                     array.clear();
224                     arrayName = parser.getAttributeValue(null, ATTR_NAME);
225                 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
226                     String name = null;
227                     if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
228                     if (parser.next() == XmlPullParser.TEXT) {
229                         String power = parser.getText();
230                         double value = 0;
231                         try {
232                             value = Double.valueOf(power);
233                         } catch (NumberFormatException nfe) {
234                         }
235                         if (element.equals(TAG_ITEM)) {
236                             sPowerMap.put(name, value);
237                         } else if (parsingArray) {
238                             array.add(value);
239                         }
240                     }
241                 }
242             }
243             if (parsingArray) {
244                 sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
245             }
246         } catch (XmlPullParserException e) {
247             throw new RuntimeException(e);
248         } catch (IOException e) {
249             throw new RuntimeException(e);
250         } finally {
251             parser.close();
252         }
253 
254         // Now collect other config variables.
255         int[] configResIds = new int[]{
256                 com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
257                 com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
258                 com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
259                 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
260                 com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
261                 com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
262                 com.android.internal.R.integer.config_wifi_tx_cur_ma,
263                 com.android.internal.R.integer.config_wifi_operating_voltage_mv,
264         };
265 
266         String[] configResIdKeys = new String[]{
267                 POWER_BLUETOOTH_CONTROLLER_IDLE,
268                 POWER_BLUETOOTH_CONTROLLER_RX,
269                 POWER_BLUETOOTH_CONTROLLER_TX,
270                 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
271                 POWER_WIFI_CONTROLLER_IDLE,
272                 POWER_WIFI_CONTROLLER_RX,
273                 POWER_WIFI_CONTROLLER_TX,
274                 POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
275         };
276 
277         for (int i = 0; i < configResIds.length; i++) {
278             int value = resources.getInteger(configResIds[i]);
279             if (value > 0) {
280                 sPowerMap.put(configResIdKeys[i], (double) value);
281             }
282         }
283     }
284 
285     private CpuClusterKey[] mCpuClusters;
286 
287     private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
288     private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
289     private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
290 
291     @SuppressWarnings("deprecated")
initCpuClusters()292     private void initCpuClusters() {
293         // Figure out how many CPU clusters we're dealing with
294         final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
295         if (obj == null || !(obj instanceof Double[])) {
296             // Default to single.
297             mCpuClusters = new CpuClusterKey[1];
298             mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
299 
300         } else {
301             final Double[] array = (Double[]) obj;
302             mCpuClusters = new CpuClusterKey[array.length];
303             for (int cluster = 0; cluster < array.length; cluster++) {
304                 int numCpusInCluster = (int) Math.round(array[cluster]);
305                 mCpuClusters[cluster] = new CpuClusterKey(
306                         POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
307                         POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
308                         numCpusInCluster);
309             }
310         }
311     }
312 
313     public static class CpuClusterKey {
314         private final String timeKey;
315         private final String powerKey;
316         private final int numCpus;
317 
CpuClusterKey(String timeKey, String powerKey, int numCpus)318         private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
319             this.timeKey = timeKey;
320             this.powerKey = powerKey;
321             this.numCpus = numCpus;
322         }
323     }
324 
getNumCpuClusters()325     public int getNumCpuClusters() {
326         return mCpuClusters.length;
327     }
328 
getNumCoresInCpuCluster(int index)329     public int getNumCoresInCpuCluster(int index) {
330         return mCpuClusters[index].numCpus;
331     }
332 
getNumSpeedStepsInCpuCluster(int index)333     public int getNumSpeedStepsInCpuCluster(int index) {
334         Object value = sPowerMap.get(mCpuClusters[index].timeKey);
335         if (value != null && value instanceof Double[]) {
336             return ((Double[])value).length;
337         }
338         return 1; // Only one speed
339     }
340 
getAveragePowerForCpu(int cluster, int step)341     public double getAveragePowerForCpu(int cluster, int step) {
342         if (cluster >= 0 && cluster < mCpuClusters.length) {
343             return getAveragePower(mCpuClusters[cluster].powerKey, step);
344         }
345         return 0;
346     }
347 
348     /**
349      * Returns the average current in mA consumed by the subsystem, or the given
350      * default value if the subsystem has no recorded value.
351      * @param type the subsystem type
352      * @param defaultValue the value to return if the subsystem has no recorded value.
353      * @return the average current in milliAmps.
354      */
getAveragePowerOrDefault(String type, double defaultValue)355     public double getAveragePowerOrDefault(String type, double defaultValue) {
356         if (sPowerMap.containsKey(type)) {
357             Object data = sPowerMap.get(type);
358             if (data instanceof Double[]) {
359                 return ((Double[])data)[0];
360             } else {
361                 return (Double) sPowerMap.get(type);
362             }
363         } else {
364             return defaultValue;
365         }
366     }
367 
368     /**
369      * Returns the average current in mA consumed by the subsystem
370      * @param type the subsystem type
371      * @return the average current in milliAmps.
372      */
getAveragePower(String type)373     public double getAveragePower(String type) {
374         return getAveragePowerOrDefault(type, 0);
375     }
376 
377     /**
378      * Returns the average current in mA consumed by the subsystem for the given level.
379      * @param type the subsystem type
380      * @param level the level of power at which the subsystem is running. For instance, the
381      *  signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
382      *  If there is no data for multiple levels, the level is ignored.
383      * @return the average current in milliAmps.
384      */
getAveragePower(String type, int level)385     public double getAveragePower(String type, int level) {
386         if (sPowerMap.containsKey(type)) {
387             Object data = sPowerMap.get(type);
388             if (data instanceof Double[]) {
389                 final Double[] values = (Double[]) data;
390                 if (values.length > level && level >= 0) {
391                     return values[level];
392                 } else if (level < 0 || values.length == 0) {
393                     return 0;
394                 } else {
395                     return values[values.length - 1];
396                 }
397             } else {
398                 return (Double) data;
399             }
400         } else {
401             return 0;
402         }
403     }
404 
405     /**
406      * Returns the battery capacity, if available, in milli Amp Hours. If not available,
407      * it returns zero.
408      * @return the battery capacity in mAh
409      */
getBatteryCapacity()410     public double getBatteryCapacity() {
411         return getAveragePower(POWER_BATTERY_CAPACITY);
412     }
413 }
414