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