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
17package com.android.internal.os;
18
19
20import android.content.Context;
21import android.content.res.XmlResourceParser;
22
23import com.android.internal.util.XmlUtils;
24
25import org.xmlpull.v1.XmlPullParser;
26import org.xmlpull.v1.XmlPullParserException;
27
28import java.io.IOException;
29import java.util.ArrayList;
30import java.util.HashMap;
31
32/**
33 * Reports power consumption values for various device activities. Reads values from an XML file.
34 * Customize the XML file for different devices.
35 * [hidden]
36 */
37public class PowerProfile {
38
39    /**
40     * No power consumption, or accounted for elsewhere.
41     */
42    public static final String POWER_NONE = "none";
43
44    /**
45     * Power consumption when CPU is in power collapse mode.
46     */
47    public static final String POWER_CPU_IDLE = "cpu.idle";
48
49    /**
50     * Power consumption when CPU is awake (when a wake lock is held).  This
51     * should be 0 on devices that can go into full CPU power collapse even
52     * when a wake lock is held.  Otherwise, this is the power consumption in
53     * addition to POWERR_CPU_IDLE due to a wake lock being held but with no
54     * CPU activity.
55     */
56    public static final String POWER_CPU_AWAKE = "cpu.awake";
57
58    /**
59     * Power consumption when CPU is in power collapse mode.
60     */
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     * Power consumption when GPS is on.
80     */
81    public static final String POWER_GPS_ON = "gps.on";
82
83    /**
84     * Power consumption when Bluetooth driver is on.
85     */
86    public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
87
88    /**
89     * Power consumption when Bluetooth driver is transmitting/receiving.
90     */
91    public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
92
93    /**
94     * Power consumption when Bluetooth driver gets an AT command.
95     */
96    public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
97
98    /**
99     * Power consumption when screen is on, not including the backlight power.
100     */
101    public static final String POWER_SCREEN_ON = "screen.on";
102
103    /**
104     * Power consumption when cell radio is on but not on a call.
105     */
106    public static final String POWER_RADIO_ON = "radio.on";
107
108    /**
109     * Power consumption when cell radio is hunting for a signal.
110     */
111    public static final String POWER_RADIO_SCANNING = "radio.scanning";
112
113    /**
114     * Power consumption when talking on the phone.
115     */
116    public static final String POWER_RADIO_ACTIVE = "radio.active";
117
118    /**
119     * Power consumption at full backlight brightness. If the backlight is at
120     * 50% brightness, then this should be multiplied by 0.5
121     */
122    public static final String POWER_SCREEN_FULL = "screen.full";
123
124    /**
125     * Power consumed by the audio hardware when playing back audio content. This is in addition
126     * to the CPU power, probably due to a DSP and / or amplifier.
127     */
128    public static final String POWER_AUDIO = "dsp.audio";
129
130    /**
131     * Power consumed by any media hardware when playing back video content. This is in addition
132     * to the CPU power, probably due to a DSP.
133     */
134    public static final String POWER_VIDEO = "dsp.video";
135
136    public static final String POWER_CPU_SPEEDS = "cpu.speeds";
137
138    /**
139     * Battery capacity in milliAmpHour (mAh).
140     */
141    public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
142
143    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
144
145    private static final String TAG_DEVICE = "device";
146    private static final String TAG_ITEM = "item";
147    private static final String TAG_ARRAY = "array";
148    private static final String TAG_ARRAYITEM = "value";
149    private static final String ATTR_NAME = "name";
150
151    public PowerProfile(Context context) {
152        // Read the XML file for the given profile (normally only one per
153        // device)
154        if (sPowerMap.size() == 0) {
155            readPowerValuesFromXml(context);
156        }
157    }
158
159    private void readPowerValuesFromXml(Context context) {
160        int id = com.android.internal.R.xml.power_profile;
161        XmlResourceParser parser = context.getResources().getXml(id);
162        boolean parsingArray = false;
163        ArrayList<Double> array = new ArrayList<Double>();
164        String arrayName = null;
165
166        try {
167            XmlUtils.beginDocument(parser, TAG_DEVICE);
168
169            while (true) {
170                XmlUtils.nextElement(parser);
171
172                String element = parser.getName();
173                if (element == null) break;
174
175                if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
176                    // Finish array
177                    sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
178                    parsingArray = false;
179                }
180                if (element.equals(TAG_ARRAY)) {
181                    parsingArray = true;
182                    array.clear();
183                    arrayName = parser.getAttributeValue(null, ATTR_NAME);
184                } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
185                    String name = null;
186                    if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
187                    if (parser.next() == XmlPullParser.TEXT) {
188                        String power = parser.getText();
189                        double value = 0;
190                        try {
191                            value = Double.valueOf(power);
192                        } catch (NumberFormatException nfe) {
193                        }
194                        if (element.equals(TAG_ITEM)) {
195                            sPowerMap.put(name, value);
196                        } else if (parsingArray) {
197                            array.add(value);
198                        }
199                    }
200                }
201            }
202            if (parsingArray) {
203                sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
204            }
205        } catch (XmlPullParserException e) {
206            throw new RuntimeException(e);
207        } catch (IOException e) {
208            throw new RuntimeException(e);
209        } finally {
210            parser.close();
211        }
212    }
213
214    /**
215     * Returns the average current in mA consumed by the subsystem
216     * @param type the subsystem type
217     * @return the average current in milliAmps.
218     */
219    public double getAveragePower(String type) {
220        if (sPowerMap.containsKey(type)) {
221            Object data = sPowerMap.get(type);
222            if (data instanceof Double[]) {
223                return ((Double[])data)[0];
224            } else {
225                return (Double) sPowerMap.get(type);
226            }
227        } else {
228            return 0;
229        }
230    }
231
232    /**
233     * Returns the average current in mA consumed by the subsystem for the given level.
234     * @param type the subsystem type
235     * @param level the level of power at which the subsystem is running. For instance, the
236     *  signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
237     *  If there is no data for multiple levels, the level is ignored.
238     * @return the average current in milliAmps.
239     */
240    public double getAveragePower(String type, int level) {
241        if (sPowerMap.containsKey(type)) {
242            Object data = sPowerMap.get(type);
243            if (data instanceof Double[]) {
244                final Double[] values = (Double[]) data;
245                if (values.length > level && level >= 0) {
246                    return values[level];
247                } else if (level < 0) {
248                    return 0;
249                } else {
250                    return values[values.length - 1];
251                }
252            } else {
253                return (Double) data;
254            }
255        } else {
256            return 0;
257        }
258    }
259
260    /**
261     * Returns the battery capacity, if available, in milli Amp Hours. If not available,
262     * it returns zero.
263     * @return the battery capacity in mAh
264     */
265    public double getBatteryCapacity() {
266        return getAveragePower(POWER_BATTERY_CAPACITY);
267    }
268
269    /**
270     * Returns the number of speeds that the CPU can be run at.
271     * @return
272     */
273    public int getNumSpeedSteps() {
274        Object value = sPowerMap.get(POWER_CPU_SPEEDS);
275        if (value != null && value instanceof Double[]) {
276            return ((Double[])value).length;
277        }
278        return 1; // Only one speed
279    }
280}
281