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.annotations.VisibleForTesting; 25 import com.android.internal.util.XmlUtils; 26 27 import org.xmlpull.v1.XmlPullParser; 28 import org.xmlpull.v1.XmlPullParserException; 29 30 import java.io.IOException; 31 import java.util.ArrayList; 32 import java.util.HashMap; 33 34 /** 35 * Reports power consumption values for various device activities. Reads values from an XML file. 36 * Customize the XML file for different devices. 37 * [hidden] 38 */ 39 public class PowerProfile { 40 41 /* 42 * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode. 43 * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should 44 * be zero on devices that can go into full CPU power collapse even when a wake 45 * lock is held. Otherwise, this is the power consumption in addition to 46 * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity. 47 * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters 48 * and cores. 49 * 50 * CPU Power Equation (assume two clusters): 51 * Total power = POWER_CPU_SUSPEND (always added) 52 * + POWER_CPU_IDLE (skip this and below if in power collapse mode) 53 * + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock 54 * is held) 55 * + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running) 56 * + core_power.cluster0 * num running cores in cluster 0 57 * + core_power.cluster1 * num running cores in cluster 1 58 */ 59 public static final String POWER_CPU_SUSPEND = "cpu.suspend"; 60 public static final String POWER_CPU_IDLE = "cpu.idle"; 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 // Updated power constants. These are not estimated, they are real world 80 // currents and voltages for the underlying bluetooth and wifi controllers. 81 // 82 public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle"; 83 public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx"; 84 public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx"; 85 public static final String POWER_WIFI_CONTROLLER_TX_LEVELS = "wifi.controller.tx_levels"; 86 public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage"; 87 88 public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle"; 89 public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx"; 90 public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx"; 91 public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE = 92 "bluetooth.controller.voltage"; 93 94 public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep"; 95 public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle"; 96 public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx"; 97 public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx"; 98 public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE = 99 "modem.controller.voltage"; 100 101 /** 102 * Power consumption when GPS is on. 103 */ 104 public static final String POWER_GPS_ON = "gps.on"; 105 106 /** 107 * GPS power parameters based on signal quality 108 */ 109 public static final String POWER_GPS_SIGNAL_QUALITY_BASED = "gps.signalqualitybased"; 110 public static final String POWER_GPS_OPERATING_VOLTAGE = "gps.voltage"; 111 112 /** 113 * Power consumption when Bluetooth driver is on. 114 * 115 * @deprecated 116 */ 117 @Deprecated 118 public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; 119 120 /** 121 * Power consumption when Bluetooth driver is transmitting/receiving. 122 * 123 * @deprecated 124 */ 125 @Deprecated 126 public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; 127 128 /** 129 * Power consumption when Bluetooth driver gets an AT command. 130 * 131 * @deprecated 132 */ 133 @Deprecated 134 public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; 135 136 /** 137 * Power consumption when screen is in doze/ambient/always-on mode, including backlight power. 138 */ 139 public static final String POWER_AMBIENT_DISPLAY = "ambient.on"; 140 141 /** 142 * Power consumption when screen is on, not including the backlight power. 143 */ 144 public static final String POWER_SCREEN_ON = "screen.on"; 145 146 /** 147 * Power consumption when cell radio is on but not on a call. 148 */ 149 public static final String POWER_RADIO_ON = "radio.on"; 150 151 /** 152 * Power consumption when cell radio is hunting for a signal. 153 */ 154 public static final String POWER_RADIO_SCANNING = "radio.scanning"; 155 156 /** 157 * Power consumption when talking on the phone. 158 */ 159 public static final String POWER_RADIO_ACTIVE = "radio.active"; 160 161 /** 162 * Power consumption at full backlight brightness. If the backlight is at 163 * 50% brightness, then this should be multiplied by 0.5 164 */ 165 public static final String POWER_SCREEN_FULL = "screen.full"; 166 167 /** 168 * Power consumed by the audio hardware when playing back audio content. This is in addition 169 * to the CPU power, probably due to a DSP and / or amplifier. 170 */ 171 public static final String POWER_AUDIO = "audio"; 172 173 /** 174 * Power consumed by any media hardware when playing back video content. This is in addition 175 * to the CPU power, probably due to a DSP. 176 */ 177 public static final String POWER_VIDEO = "video"; 178 179 /** 180 * Average power consumption when camera flashlight is on. 181 */ 182 public static final String POWER_FLASHLIGHT = "camera.flashlight"; 183 184 /** 185 * Power consumption when DDR is being used. 186 */ 187 public static final String POWER_MEMORY = "memory.bandwidths"; 188 189 /** 190 * Average power consumption when the camera is on over all standard use cases. 191 * 192 * TODO: Add more fine-grained camera power metrics. 193 */ 194 public static final String POWER_CAMERA = "camera.avg"; 195 196 /** 197 * Power consumed by wif batched scaning. Broken down into bins by 198 * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels 199 * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)! 200 */ 201 public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan"; 202 203 /** 204 * Battery capacity in milliAmpHour (mAh). 205 */ 206 public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; 207 208 /** 209 * A map from Power Use Item to its power consumption. 210 */ 211 static final HashMap<String, Double> sPowerItemMap = new HashMap<>(); 212 /** 213 * A map from Power Use Item to an array of its power consumption 214 * (for items with variable power e.g. CPU). 215 */ 216 static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>(); 217 218 private static final String TAG_DEVICE = "device"; 219 private static final String TAG_ITEM = "item"; 220 private static final String TAG_ARRAY = "array"; 221 private static final String TAG_ARRAYITEM = "value"; 222 private static final String ATTR_NAME = "name"; 223 224 private static final Object sLock = new Object(); 225 226 @VisibleForTesting PowerProfile(Context context)227 public PowerProfile(Context context) { 228 this(context, false); 229 } 230 231 /** 232 * For PowerProfileTest 233 */ 234 @VisibleForTesting PowerProfile(Context context, boolean forTest)235 public PowerProfile(Context context, boolean forTest) { 236 // Read the XML file for the given profile (normally only one per device) 237 synchronized (sLock) { 238 if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) { 239 readPowerValuesFromXml(context, forTest); 240 } 241 initCpuClusters(); 242 } 243 } 244 readPowerValuesFromXml(Context context, boolean forTest)245 private void readPowerValuesFromXml(Context context, boolean forTest) { 246 final int id = forTest ? com.android.internal.R.xml.power_profile_test : 247 com.android.internal.R.xml.power_profile; 248 final Resources resources = context.getResources(); 249 XmlResourceParser parser = resources.getXml(id); 250 boolean parsingArray = false; 251 ArrayList<Double> array = new ArrayList<>(); 252 String arrayName = null; 253 254 try { 255 XmlUtils.beginDocument(parser, TAG_DEVICE); 256 257 while (true) { 258 XmlUtils.nextElement(parser); 259 260 String element = parser.getName(); 261 if (element == null) break; 262 263 if (parsingArray && !element.equals(TAG_ARRAYITEM)) { 264 // Finish array 265 sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); 266 parsingArray = false; 267 } 268 if (element.equals(TAG_ARRAY)) { 269 parsingArray = true; 270 array.clear(); 271 arrayName = parser.getAttributeValue(null, ATTR_NAME); 272 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { 273 String name = null; 274 if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); 275 if (parser.next() == XmlPullParser.TEXT) { 276 String power = parser.getText(); 277 double value = 0; 278 try { 279 value = Double.valueOf(power); 280 } catch (NumberFormatException nfe) { 281 } 282 if (element.equals(TAG_ITEM)) { 283 sPowerItemMap.put(name, value); 284 } else if (parsingArray) { 285 array.add(value); 286 } 287 } 288 } 289 } 290 if (parsingArray) { 291 sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()])); 292 } 293 } catch (XmlPullParserException e) { 294 throw new RuntimeException(e); 295 } catch (IOException e) { 296 throw new RuntimeException(e); 297 } finally { 298 parser.close(); 299 } 300 301 // Now collect other config variables. 302 int[] configResIds = new int[]{ 303 com.android.internal.R.integer.config_bluetooth_idle_cur_ma, 304 com.android.internal.R.integer.config_bluetooth_rx_cur_ma, 305 com.android.internal.R.integer.config_bluetooth_tx_cur_ma, 306 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv, 307 }; 308 309 String[] configResIdKeys = new String[]{ 310 POWER_BLUETOOTH_CONTROLLER_IDLE, 311 POWER_BLUETOOTH_CONTROLLER_RX, 312 POWER_BLUETOOTH_CONTROLLER_TX, 313 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, 314 }; 315 316 for (int i = 0; i < configResIds.length; i++) { 317 String key = configResIdKeys[i]; 318 // if we already have some of these parameters in power_profile.xml, ignore the 319 // value in config.xml 320 if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) { 321 continue; 322 } 323 int value = resources.getInteger(configResIds[i]); 324 if (value > 0) { 325 sPowerItemMap.put(key, (double) value); 326 } 327 } 328 } 329 330 private CpuClusterKey[] mCpuClusters; 331 332 private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; 333 private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster"; 334 private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster"; 335 private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster"; 336 initCpuClusters()337 private void initCpuClusters() { 338 if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { 339 final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT); 340 mCpuClusters = new CpuClusterKey[data.length]; 341 for (int cluster = 0; cluster < data.length; cluster++) { 342 int numCpusInCluster = (int) Math.round(data[cluster]); 343 mCpuClusters[cluster] = new CpuClusterKey( 344 CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster, 345 CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster); 346 } 347 } else { 348 // Default to single. 349 mCpuClusters = new CpuClusterKey[1]; 350 int numCpus = 1; 351 if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) { 352 numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT)); 353 } 354 mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0, 355 CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus); 356 } 357 } 358 359 public static class CpuClusterKey { 360 private final String freqKey; 361 private final String clusterPowerKey; 362 private final String corePowerKey; 363 private final int numCpus; 364 CpuClusterKey(String freqKey, String clusterPowerKey, String corePowerKey, int numCpus)365 private CpuClusterKey(String freqKey, String clusterPowerKey, 366 String corePowerKey, int numCpus) { 367 this.freqKey = freqKey; 368 this.clusterPowerKey = clusterPowerKey; 369 this.corePowerKey = corePowerKey; 370 this.numCpus = numCpus; 371 } 372 } 373 getNumCpuClusters()374 public int getNumCpuClusters() { 375 return mCpuClusters.length; 376 } 377 getNumCoresInCpuCluster(int cluster)378 public int getNumCoresInCpuCluster(int cluster) { 379 return mCpuClusters[cluster].numCpus; 380 } 381 getNumSpeedStepsInCpuCluster(int cluster)382 public int getNumSpeedStepsInCpuCluster(int cluster) { 383 if (cluster < 0 || cluster >= mCpuClusters.length) { 384 return 0; // index out of bound 385 } 386 if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) { 387 return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length; 388 } 389 return 1; // Only one speed 390 } 391 getAveragePowerForCpuCluster(int cluster)392 public double getAveragePowerForCpuCluster(int cluster) { 393 if (cluster >= 0 && cluster < mCpuClusters.length) { 394 return getAveragePower(mCpuClusters[cluster].clusterPowerKey); 395 } 396 return 0; 397 } 398 getAveragePowerForCpuCore(int cluster, int step)399 public double getAveragePowerForCpuCore(int cluster, int step) { 400 if (cluster >= 0 && cluster < mCpuClusters.length) { 401 return getAveragePower(mCpuClusters[cluster].corePowerKey, step); 402 } 403 return 0; 404 } 405 406 /** 407 * Returns the number of memory bandwidth buckets defined in power_profile.xml, or a 408 * default value if the subsystem has no recorded value. 409 * 410 * @return the number of memory bandwidth buckets. 411 */ getNumElements(String key)412 public int getNumElements(String key) { 413 if (sPowerItemMap.containsKey(key)) { 414 return 1; 415 } else if (sPowerArrayMap.containsKey(key)) { 416 return sPowerArrayMap.get(key).length; 417 } 418 return 0; 419 } 420 421 /** 422 * Returns the average current in mA consumed by the subsystem, or the given 423 * default value if the subsystem has no recorded value. 424 * 425 * @param type the subsystem type 426 * @param defaultValue the value to return if the subsystem has no recorded value. 427 * @return the average current in milliAmps. 428 */ getAveragePowerOrDefault(String type, double defaultValue)429 public double getAveragePowerOrDefault(String type, double defaultValue) { 430 if (sPowerItemMap.containsKey(type)) { 431 return sPowerItemMap.get(type); 432 } else if (sPowerArrayMap.containsKey(type)) { 433 return sPowerArrayMap.get(type)[0]; 434 } else { 435 return defaultValue; 436 } 437 } 438 439 /** 440 * Returns the average current in mA consumed by the subsystem 441 * 442 * @param type the subsystem type 443 * @return the average current in milliAmps. 444 */ getAveragePower(String type)445 public double getAveragePower(String type) { 446 return getAveragePowerOrDefault(type, 0); 447 } 448 449 /** 450 * Returns the average current in mA consumed by the subsystem for the given level. 451 * 452 * @param type the subsystem type 453 * @param level the level of power at which the subsystem is running. For instance, the 454 * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) 455 * If there is no data for multiple levels, the level is ignored. 456 * @return the average current in milliAmps. 457 */ getAveragePower(String type, int level)458 public double getAveragePower(String type, int level) { 459 if (sPowerItemMap.containsKey(type)) { 460 return sPowerItemMap.get(type); 461 } else if (sPowerArrayMap.containsKey(type)) { 462 final Double[] values = sPowerArrayMap.get(type); 463 if (values.length > level && level >= 0) { 464 return values[level]; 465 } else if (level < 0 || values.length == 0) { 466 return 0; 467 } else { 468 return values[values.length - 1]; 469 } 470 } else { 471 return 0; 472 } 473 } 474 475 /** 476 * Returns the battery capacity, if available, in milli Amp Hours. If not available, 477 * it returns zero. 478 * 479 * @return the battery capacity in mAh 480 */ getBatteryCapacity()481 public double getBatteryCapacity() { 482 return getAveragePower(POWER_BATTERY_CAPACITY); 483 } 484 } 485