Home | History | Annotate | Download | only in os
      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