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 POWER_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_TX_LEVELS = "wifi.controller.tx_levels"; 89 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"; 96 97 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 103 /** 104 * 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. 110 * @deprecated 111 */ 112 @Deprecated 113 public static final String POWER_BLUETOOTH_ON = "bluetooth.on"; 114 115 /** 116 * Power consumption when Bluetooth driver is transmitting/receiving. 117 * @deprecated 118 */ 119 @Deprecated 120 public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active"; 121 122 /** 123 * Power consumption when Bluetooth driver gets an AT command. 124 * @deprecated 125 */ 126 @Deprecated 127 public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; 128 129 130 /** 131 * 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 /** 141 * Power consumption when cell radio is hunting for a signal. 142 */ 143 public static final String POWER_RADIO_SCANNING = "radio.scanning"; 144 145 /** 146 * 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 168 /** 169 * Average power consumption when camera flashlight is on. 170 */ 171 public static final String POWER_FLASHLIGHT = "camera.flashlight"; 172 173 /** 174 * Power consumption when DDR is being used. 175 */ 176 public static final String POWER_MEMORY = "memory.bandwidths"; 177 178 /** 179 * Average power consumption when the camera is on over all standard use cases. 180 * 181 * TODO: Add more fine-grained camera power metrics. 182 */ 183 public static final String POWER_CAMERA = "camera.avg"; 184 185 @Deprecated 186 public static final String POWER_CPU_SPEEDS = "cpu.speeds"; 187 188 /** 189 * Power consumed by wif batched scaning. Broken down into bins by 190 * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels 191 * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)! 192 */ 193 public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan"; 194 195 /** 196 * Battery capacity in milliAmpHour (mAh). 197 */ 198 public static final String POWER_BATTERY_CAPACITY = "battery.capacity"; 199 200 static final HashMap<String, Object> sPowerMap = new HashMap<>(); 201 202 private static final String TAG_DEVICE = "device"; 203 private static final String TAG_ITEM = "item"; 204 private static final String TAG_ARRAY = "array"; 205 private static final String TAG_ARRAYITEM = "value"; 206 private static final String ATTR_NAME = "name"; 207 208 public PowerProfile(Context context) { 209 // Read the XML file for the given profile (normally only one per 210 // device) 211 if (sPowerMap.size() == 0) { 212 readPowerValuesFromXml(context); 213 } 214 initCpuClusters(); 215 } 216 217 private void readPowerValuesFromXml(Context context) { 218 int id = com.android.internal.R.xml.power_profile; 219 final Resources resources = context.getResources(); 220 XmlResourceParser parser = resources.getXml(id); 221 boolean parsingArray = false; 222 ArrayList<Double> array = new ArrayList<Double>(); 223 String arrayName = null; 224 225 try { 226 XmlUtils.beginDocument(parser, TAG_DEVICE); 227 228 while (true) { 229 XmlUtils.nextElement(parser); 230 231 String element = parser.getName(); 232 if (element == null) break; 233 234 if (parsingArray && !element.equals(TAG_ARRAYITEM)) { 235 // Finish array 236 sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); 237 parsingArray = false; 238 } 239 if (element.equals(TAG_ARRAY)) { 240 parsingArray = true; 241 array.clear(); 242 arrayName = parser.getAttributeValue(null, ATTR_NAME); 243 } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { 244 String name = null; 245 if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); 246 if (parser.next() == XmlPullParser.TEXT) { 247 String power = parser.getText(); 248 double value = 0; 249 try { 250 value = Double.valueOf(power); 251 } catch (NumberFormatException nfe) { 252 } 253 if (element.equals(TAG_ITEM)) { 254 sPowerMap.put(name, value); 255 } else if (parsingArray) { 256 array.add(value); 257 } 258 } 259 } 260 } 261 if (parsingArray) { 262 sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); 263 } 264 } catch (XmlPullParserException e) { 265 throw new RuntimeException(e); 266 } catch (IOException e) { 267 throw new RuntimeException(e); 268 } finally { 269 parser.close(); 270 } 271 272 // Now collect other config variables. 273 int[] configResIds = new int[]{ 274 com.android.internal.R.integer.config_bluetooth_idle_cur_ma, 275 com.android.internal.R.integer.config_bluetooth_rx_cur_ma, 276 com.android.internal.R.integer.config_bluetooth_tx_cur_ma, 277 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv, 278 com.android.internal.R.integer.config_wifi_idle_receive_cur_ma, 279 com.android.internal.R.integer.config_wifi_active_rx_cur_ma, 280 com.android.internal.R.integer.config_wifi_tx_cur_ma, 281 com.android.internal.R.integer.config_wifi_operating_voltage_mv, 282 }; 283 284 String[] configResIdKeys = new String[]{ 285 POWER_BLUETOOTH_CONTROLLER_IDLE, 286 POWER_BLUETOOTH_CONTROLLER_RX, 287 POWER_BLUETOOTH_CONTROLLER_TX, 288 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE, 289 POWER_WIFI_CONTROLLER_IDLE, 290 POWER_WIFI_CONTROLLER_RX, 291 POWER_WIFI_CONTROLLER_TX, 292 POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE, 293 }; 294 295 for (int i = 0; i < configResIds.length; i++) { 296 String key = configResIdKeys[i]; 297 // if we already have some of these parameters in power_profile.xml, ignore the 298 // value in config.xml 299 if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) { 300 continue; 301 } 302 int value = resources.getInteger(configResIds[i]); 303 if (value > 0) { 304 sPowerMap.put(key, (double) value); 305 } 306 } 307 } 308 309 private CpuClusterKey[] mCpuClusters; 310 311 private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; 312 private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster"; 313 private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster"; 314 315 @SuppressWarnings("deprecation") 316 private void initCpuClusters() { 317 // Figure out how many CPU clusters we're dealing with 318 final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT); 319 if (obj == null || !(obj instanceof Double[])) { 320 // Default to single. 321 mCpuClusters = new CpuClusterKey[1]; 322 mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1); 323 324 } else { 325 final Double[] array = (Double[]) obj; 326 mCpuClusters = new CpuClusterKey[array.length]; 327 for (int cluster = 0; cluster < array.length; cluster++) { 328 int numCpusInCluster = (int) Math.round(array[cluster]); 329 mCpuClusters[cluster] = new CpuClusterKey( 330 POWER_CPU_CLUSTER_SPEED_PREFIX + cluster, 331 POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster, 332 numCpusInCluster); 333 } 334 } 335 } 336 337 public static class CpuClusterKey { 338 private final String timeKey; 339 private final String powerKey; 340 private final int numCpus; 341 342 private CpuClusterKey(String timeKey, String powerKey, int numCpus) { 343 this.timeKey = timeKey; 344 this.powerKey = powerKey; 345 this.numCpus = numCpus; 346 } 347 } 348 349 public int getNumCpuClusters() { 350 return mCpuClusters.length; 351 } 352 353 public int getNumCoresInCpuCluster(int index) { 354 return mCpuClusters[index].numCpus; 355 } 356 357 public int getNumSpeedStepsInCpuCluster(int index) { 358 Object value = sPowerMap.get(mCpuClusters[index].timeKey); 359 if (value != null && value instanceof Double[]) { 360 return ((Double[])value).length; 361 } 362 return 1; // Only one speed 363 } 364 365 public double getAveragePowerForCpu(int cluster, int step) { 366 if (cluster >= 0 && cluster < mCpuClusters.length) { 367 return getAveragePower(mCpuClusters[cluster].powerKey, step); 368 } 369 return 0; 370 } 371 372 /** 373 * Returns the number of memory bandwidth buckets defined in power_profile.xml, or a 374 * default value if the subsystem has no recorded value. 375 * @return the number of memory bandwidth buckets. 376 */ 377 public int getNumElements(String key) { 378 if (sPowerMap.containsKey(key)) { 379 Object data = sPowerMap.get(key); 380 if (data instanceof Double[]) { 381 final Double[] values = (Double[]) data; 382 return values.length; 383 } else { 384 return 1; 385 } 386 } 387 return 0; 388 } 389 390 /** 391 * Returns the average current in mA consumed by the subsystem, or the given 392 * default value if the subsystem has no recorded value. 393 * @param type the subsystem type 394 * @param defaultValue the value to return if the subsystem has no recorded value. 395 * @return the average current in milliAmps. 396 */ 397 public double getAveragePowerOrDefault(String type, double defaultValue) { 398 if (sPowerMap.containsKey(type)) { 399 Object data = sPowerMap.get(type); 400 if (data instanceof Double[]) { 401 return ((Double[])data)[0]; 402 } else { 403 return (Double) sPowerMap.get(type); 404 } 405 } else { 406 return defaultValue; 407 } 408 } 409 410 /** 411 * Returns the average current in mA consumed by the subsystem 412 * @param type the subsystem type 413 * @return the average current in milliAmps. 414 */ 415 public double getAveragePower(String type) { 416 return getAveragePowerOrDefault(type, 0); 417 } 418 419 /** 420 * Returns the average current in mA consumed by the subsystem for the given level. 421 * @param type the subsystem type 422 * @param level the level of power at which the subsystem is running. For instance, the 423 * signal strength of the cell network between 0 and 4 (if there are 4 bars max.) 424 * If there is no data for multiple levels, the level is ignored. 425 * @return the average current in milliAmps. 426 */ 427 public double getAveragePower(String type, int level) { 428 if (sPowerMap.containsKey(type)) { 429 Object data = sPowerMap.get(type); 430 if (data instanceof Double[]) { 431 final Double[] values = (Double[]) data; 432 if (values.length > level && level >= 0) { 433 return values[level]; 434 } else if (level < 0 || values.length == 0) { 435 return 0; 436 } else { 437 return values[values.length - 1]; 438 } 439 } else { 440 return (Double) data; 441 } 442 } else { 443 return 0; 444 } 445 } 446 447 /** 448 * Returns the battery capacity, if available, in milli Amp Hours. If not available, 449 * it returns zero. 450 * @return the battery capacity in mAh 451 */ 452 public double getBatteryCapacity() { 453 return getAveragePower(POWER_BATTERY_CAPACITY); 454 } 455 } 456