blob: ad14a2071330c34386f8da0e0eda57f7cf00e6da [file] [log] [blame]
Amith Yamasani244fa5c2009-05-22 14:36:07 -07001/*
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
17package com.android.internal.os;
18
19
20import android.content.Context;
Adam Lesinskie08af192015-03-25 16:42:59 -070021import android.content.res.Resources;
Amith Yamasani244fa5c2009-05-22 14:36:07 -070022import android.content.res.XmlResourceParser;
23
Dianne Hackborn2269d1572010-02-24 19:54:22 -080024import com.android.internal.util.XmlUtils;
Amith Yamasani244fa5c2009-05-22 14:36:07 -070025
26import org.xmlpull.v1.XmlPullParser;
27import org.xmlpull.v1.XmlPullParserException;
28
29import java.io.IOException;
Amith Yamasani3718aaa2009-06-09 06:32:35 -070030import java.util.ArrayList;
Amith Yamasani244fa5c2009-05-22 14:36:07 -070031import 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 */
38public 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 /**
Dianne Hackborn0c8b4d32010-11-08 19:10:58 -080051 * 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
Adam Lesinski76a267b2016-04-20 13:04:59 -070054 * addition to POWER_CPU_IDLE due to a wake lock being held but with no
Dianne Hackborn0c8b4d32010-11-08 19:10:58 -080055 * CPU activity.
56 */
57 public static final String POWER_CPU_AWAKE = "cpu.awake";
58
59 /**
Amith Yamasanie43530a2009-08-21 13:11:37 -070060 * Power consumption when CPU is in power collapse mode.
Amith Yamasani244fa5c2009-05-22 14:36:07 -070061 */
Adam Lesinski6832f392015-09-05 18:05:40 -070062 @Deprecated
Amith Yamasanie43530a2009-08-21 13:11:37 -070063 public static final String POWER_CPU_ACTIVE = "cpu.active";
Amith Yamasani244fa5c2009-05-22 14:36:07 -070064
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
Adam Lesinskie08af192015-03-25 16:42:59 -070080 //
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";
Roshan Pius2a91e4a2016-03-31 16:33:54 -070088 public static final String POWER_WIFI_CONTROLLER_TX_LEVELS = "wifi.controller.tx_levels";
Adam Lesinskie08af192015-03-25 16:42:59 -070089 public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
90
91 public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
92 public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
93 public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
94 public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
95 "bluetooth.controller.voltage";
Adam Lesinski33dac552015-03-09 15:24:48 -070096
Adam Lesinski21f76aa2016-01-25 12:27:06 -080097 public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
98 public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
99 public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
100 public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE =
101 "modem.controller.voltage";
102
Adam Lesinski33dac552015-03-09 15:24:48 -0700103 /**
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700104 * Power consumption when GPS is on.
105 */
106 public static final String POWER_GPS_ON = "gps.on";
107
108 /**
109 * Power consumption when Bluetooth driver is on.
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800110 * @deprecated
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700111 */
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800112 @Deprecated
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700113 public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
114
115 /**
116 * Power consumption when Bluetooth driver is transmitting/receiving.
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800117 * @deprecated
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700118 */
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800119 @Deprecated
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700120 public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
121
122 /**
Amith Yamasani3f7e35c2009-07-13 16:02:45 -0700123 * Power consumption when Bluetooth driver gets an AT command.
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800124 * @deprecated
Amith Yamasani3f7e35c2009-07-13 16:02:45 -0700125 */
Adam Lesinski21f76aa2016-01-25 12:27:06 -0800126 @Deprecated
Amith Yamasani3f7e35c2009-07-13 16:02:45 -0700127 public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
128
Adam Lesinski33dac552015-03-09 15:24:48 -0700129
130 /**
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700131 * Power consumption when screen is on, not including the backlight power.
132 */
133 public static final String POWER_SCREEN_ON = "screen.on";
134
135 /**
136 * Power consumption when cell radio is on but not on a call.
137 */
138 public static final String POWER_RADIO_ON = "radio.on";
139
140 /**
Amith Yamasanif37447b2009-10-08 18:28:01 -0700141 * Power consumption when cell radio is hunting for a signal.
142 */
143 public static final String POWER_RADIO_SCANNING = "radio.scanning";
144
145 /**
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700146 * Power consumption when talking on the phone.
147 */
148 public static final String POWER_RADIO_ACTIVE = "radio.active";
149
150 /**
151 * Power consumption at full backlight brightness. If the backlight is at
152 * 50% brightness, then this should be multiplied by 0.5
153 */
154 public static final String POWER_SCREEN_FULL = "screen.full";
155
156 /**
157 * Power consumed by the audio hardware when playing back audio content. This is in addition
158 * to the CPU power, probably due to a DSP and / or amplifier.
159 */
160 public static final String POWER_AUDIO = "dsp.audio";
161
162 /**
163 * Power consumed by any media hardware when playing back video content. This is in addition
164 * to the CPU power, probably due to a DSP.
165 */
166 public static final String POWER_VIDEO = "dsp.video";
167
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700168 /**
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700169 * Average power consumption when camera flashlight is on.
Dianne Hackbornabc7c492014-06-30 16:57:46 -0700170 */
171 public static final String POWER_FLASHLIGHT = "camera.flashlight";
172
Ruben Brunk5b1308f2015-06-03 18:49:27 -0700173 /**
174 * Average power consumption when the camera is on over all standard use cases.
175 *
176 * TODO: Add more fine-grained camera power metrics.
177 */
178 public static final String POWER_CAMERA = "camera.avg";
179
Adam Lesinski6832f392015-09-05 18:05:40 -0700180 @Deprecated
Amith Yamasanie43530a2009-08-21 13:11:37 -0700181 public static final String POWER_CPU_SPEEDS = "cpu.speeds";
182
Amith Yamasani169741b2010-05-27 10:37:54 -0700183 /**
Robert Greenwalta029ea12013-09-25 16:38:12 -0700184 * Power consumed by wif batched scaning. Broken down into bins by
185 * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels
186 * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)!
187 */
188 public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan";
189
190 /**
Amith Yamasani169741b2010-05-27 10:37:54 -0700191 * Battery capacity in milliAmpHour (mAh).
192 */
193 public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
194
Adam Lesinskie08af192015-03-25 16:42:59 -0700195 static final HashMap<String, Object> sPowerMap = new HashMap<>();
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700196
197 private static final String TAG_DEVICE = "device";
198 private static final String TAG_ITEM = "item";
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700199 private static final String TAG_ARRAY = "array";
200 private static final String TAG_ARRAYITEM = "value";
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700201 private static final String ATTR_NAME = "name";
202
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700203 public PowerProfile(Context context) {
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700204 // Read the XML file for the given profile (normally only one per
205 // device)
206 if (sPowerMap.size() == 0) {
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700207 readPowerValuesFromXml(context);
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700208 }
Adam Lesinski6832f392015-09-05 18:05:40 -0700209 initCpuClusters();
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700210 }
211
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700212 private void readPowerValuesFromXml(Context context) {
213 int id = com.android.internal.R.xml.power_profile;
Adam Lesinskie08af192015-03-25 16:42:59 -0700214 final Resources resources = context.getResources();
215 XmlResourceParser parser = resources.getXml(id);
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700216 boolean parsingArray = false;
217 ArrayList<Double> array = new ArrayList<Double>();
218 String arrayName = null;
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700219
220 try {
221 XmlUtils.beginDocument(parser, TAG_DEVICE);
222
223 while (true) {
224 XmlUtils.nextElement(parser);
225
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700226 String element = parser.getName();
227 if (element == null) break;
Robert Greenwalta029ea12013-09-25 16:38:12 -0700228
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700229 if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
230 // Finish array
231 sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
232 parsingArray = false;
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700233 }
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700234 if (element.equals(TAG_ARRAY)) {
235 parsingArray = true;
236 array.clear();
237 arrayName = parser.getAttributeValue(null, ATTR_NAME);
238 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
239 String name = null;
240 if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
241 if (parser.next() == XmlPullParser.TEXT) {
242 String power = parser.getText();
243 double value = 0;
244 try {
245 value = Double.valueOf(power);
246 } catch (NumberFormatException nfe) {
247 }
248 if (element.equals(TAG_ITEM)) {
249 sPowerMap.put(name, value);
250 } else if (parsingArray) {
251 array.add(value);
252 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700253 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700254 }
255 }
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700256 if (parsingArray) {
257 sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
258 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700259 } catch (XmlPullParserException e) {
260 throw new RuntimeException(e);
261 } catch (IOException e) {
262 throw new RuntimeException(e);
263 } finally {
264 parser.close();
265 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700266
267 // Now collect other config variables.
Adam Lesinski6832f392015-09-05 18:05:40 -0700268 int[] configResIds = new int[]{
Adam Lesinskie08af192015-03-25 16:42:59 -0700269 com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
270 com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
271 com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
272 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
273 com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
274 com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
275 com.android.internal.R.integer.config_wifi_tx_cur_ma,
276 com.android.internal.R.integer.config_wifi_operating_voltage_mv,
277 };
278
Adam Lesinski6832f392015-09-05 18:05:40 -0700279 String[] configResIdKeys = new String[]{
Adam Lesinskie08af192015-03-25 16:42:59 -0700280 POWER_BLUETOOTH_CONTROLLER_IDLE,
281 POWER_BLUETOOTH_CONTROLLER_RX,
282 POWER_BLUETOOTH_CONTROLLER_TX,
283 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
284 POWER_WIFI_CONTROLLER_IDLE,
285 POWER_WIFI_CONTROLLER_RX,
286 POWER_WIFI_CONTROLLER_TX,
287 POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
288 };
289
290 for (int i = 0; i < configResIds.length; i++) {
Roshan Pius2a91e4a2016-03-31 16:33:54 -0700291 String key = configResIdKeys[i];
292 // if we already have some of these parameters in power_profile.xml, ignore the
293 // value in config.xml
294 if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) {
295 continue;
296 }
Adam Lesinskie08af192015-03-25 16:42:59 -0700297 int value = resources.getInteger(configResIds[i]);
298 if (value > 0) {
Roshan Pius2a91e4a2016-03-31 16:33:54 -0700299 sPowerMap.put(key, (double) value);
Adam Lesinskie08af192015-03-25 16:42:59 -0700300 }
301 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700302 }
303
Adam Lesinski6832f392015-09-05 18:05:40 -0700304 private CpuClusterKey[] mCpuClusters;
305
306 private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
307 private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
308 private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
309
Ian Rogerse09ac052016-05-27 14:31:19 -0700310 @SuppressWarnings("deprecation")
Adam Lesinski6832f392015-09-05 18:05:40 -0700311 private void initCpuClusters() {
312 // Figure out how many CPU clusters we're dealing with
313 final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
314 if (obj == null || !(obj instanceof Double[])) {
315 // Default to single.
316 mCpuClusters = new CpuClusterKey[1];
317 mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
318
319 } else {
320 final Double[] array = (Double[]) obj;
321 mCpuClusters = new CpuClusterKey[array.length];
322 for (int cluster = 0; cluster < array.length; cluster++) {
323 int numCpusInCluster = (int) Math.round(array[cluster]);
324 mCpuClusters[cluster] = new CpuClusterKey(
325 POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
326 POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
327 numCpusInCluster);
328 }
329 }
330 }
331
332 public static class CpuClusterKey {
333 private final String timeKey;
334 private final String powerKey;
335 private final int numCpus;
336
337 private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
338 this.timeKey = timeKey;
339 this.powerKey = powerKey;
340 this.numCpus = numCpus;
341 }
342 }
343
344 public int getNumCpuClusters() {
345 return mCpuClusters.length;
346 }
347
348 public int getNumCoresInCpuCluster(int index) {
349 return mCpuClusters[index].numCpus;
350 }
351
352 public int getNumSpeedStepsInCpuCluster(int index) {
353 Object value = sPowerMap.get(mCpuClusters[index].timeKey);
354 if (value != null && value instanceof Double[]) {
355 return ((Double[])value).length;
356 }
357 return 1; // Only one speed
358 }
359
360 public double getAveragePowerForCpu(int cluster, int step) {
361 if (cluster >= 0 && cluster < mCpuClusters.length) {
362 return getAveragePower(mCpuClusters[cluster].powerKey, step);
363 }
364 return 0;
365 }
366
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700367 /**
Adam Lesinski33dac552015-03-09 15:24:48 -0700368 * Returns the average current in mA consumed by the subsystem, or the given
369 * default value if the subsystem has no recorded value.
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700370 * @param type the subsystem type
Adam Lesinski33dac552015-03-09 15:24:48 -0700371 * @param defaultValue the value to return if the subsystem has no recorded value.
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700372 * @return the average current in milliAmps.
373 */
Adam Lesinski33dac552015-03-09 15:24:48 -0700374 public double getAveragePowerOrDefault(String type, double defaultValue) {
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700375 if (sPowerMap.containsKey(type)) {
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700376 Object data = sPowerMap.get(type);
377 if (data instanceof Double[]) {
378 return ((Double[])data)[0];
379 } else {
380 return (Double) sPowerMap.get(type);
381 }
382 } else {
Adam Lesinski33dac552015-03-09 15:24:48 -0700383 return defaultValue;
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700384 }
385 }
Adam Lesinski33dac552015-03-09 15:24:48 -0700386
387 /**
388 * Returns the average current in mA consumed by the subsystem
389 * @param type the subsystem type
390 * @return the average current in milliAmps.
391 */
392 public double getAveragePower(String type) {
393 return getAveragePowerOrDefault(type, 0);
394 }
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700395
396 /**
Amith Yamasanie43530a2009-08-21 13:11:37 -0700397 * Returns the average current in mA consumed by the subsystem for the given level.
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700398 * @param type the subsystem type
399 * @param level the level of power at which the subsystem is running. For instance, the
Amith Yamasanie43530a2009-08-21 13:11:37 -0700400 * signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700401 * If there is no data for multiple levels, the level is ignored.
402 * @return the average current in milliAmps.
403 */
404 public double getAveragePower(String type, int level) {
405 if (sPowerMap.containsKey(type)) {
406 Object data = sPowerMap.get(type);
Amith Yamasani32dbefd2009-06-19 09:21:17 -0700407 if (data instanceof Double[]) {
408 final Double[] values = (Double[]) data;
409 if (values.length > level && level >= 0) {
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700410 return values[level];
Ruchi Kandoibe9cbb12014-07-02 17:47:40 -0700411 } else if (level < 0 || values.length == 0) {
Amith Yamasani32dbefd2009-06-19 09:21:17 -0700412 return 0;
Amith Yamasani3718aaa2009-06-09 06:32:35 -0700413 } else {
414 return values[values.length - 1];
415 }
416 } else {
417 return (Double) data;
418 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700419 } else {
420 return 0;
421 }
422 }
Amith Yamasanie43530a2009-08-21 13:11:37 -0700423
Amith Yamasani169741b2010-05-27 10:37:54 -0700424 /**
425 * Returns the battery capacity, if available, in milli Amp Hours. If not available,
426 * it returns zero.
427 * @return the battery capacity in mAh
428 */
429 public double getBatteryCapacity() {
430 return getAveragePower(POWER_BATTERY_CAPACITY);
431 }
Amith Yamasani244fa5c2009-05-22 14:36:07 -0700432}