summaryrefslogtreecommitdiff
path: root/tools/powermodel/src
diff options
context:
space:
mode:
authorJoe Onorato <joeo@google.com>2018-11-16 15:56:37 -0800
committerJoe Onorato <joeo@google.com>2018-11-28 12:06:31 -0800
commit9112a5e07a82d0e27d91e34971dc7afce53542f9 (patch)
treebff058569e07a5a2dcbadea6f37e112222939c92 /tools/powermodel/src
parent5af89dfbd56d6ca829c1c5c31a3e364f1eab0561 (diff)
Off-device library for the power model.
This first CL adds a class, PowerProfile that parses the power profile xml file into a set of individual *Profile classes, one for each of the hardware "components." There will be more to come. This library will be used to compute the power model from a batterystats or statsd dump, with abstractions so clients don't need to know all of the nuances of batterystats' old versions, or statsd's configs. Test: atest frameworks/base/tools/powermodel --host Change-Id: I79802f91234b09539072d10f15534cef391fe04a
Diffstat (limited to 'tools/powermodel/src')
-rw-r--r--tools/powermodel/src/com/android/powermodel/Component.java34
-rw-r--r--tools/powermodel/src/com/android/powermodel/ComponentProfile.java20
-rw-r--r--tools/powermodel/src/com/android/powermodel/ParseException.java35
-rw-r--r--tools/powermodel/src/com/android/powermodel/PowerProfile.java527
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/AudioProfile.java27
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java29
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/CameraProfile.java27
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/CpuProfile.java145
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java27
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/GpsProfile.java53
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/ModemProfile.java92
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java29
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/VideoProfile.java28
-rw-r--r--tools/powermodel/src/com/android/powermodel/component/WifiProfile.java29
-rw-r--r--tools/powermodel/src/com/android/powermodel/util/Conversion.java43
15 files changed, 1145 insertions, 0 deletions
diff --git a/tools/powermodel/src/com/android/powermodel/Component.java b/tools/powermodel/src/com/android/powermodel/Component.java
new file mode 100644
index 000000000000..baae6d784c47
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+/**
+ * The hardware components that use power on a device.
+ */
+public enum Component {
+ CPU,
+ SCREEN,
+ MODEM,
+ WIFI,
+ BLUETOOTH,
+ VIDEO,
+ AUDIO,
+ FLASHLIGHT,
+ CAMERA,
+ GPS,
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentProfile.java b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
new file mode 100644
index 000000000000..e76e5fb52481
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+public class ComponentProfile {
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java
new file mode 100644
index 000000000000..e1f232bfc44f
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ParseException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+public class ParseException extends Exception {
+ public final int line;
+
+ public ParseException(int line, String message, Throwable th) {
+ super(message, th);
+ this.line = line;
+ }
+
+ public ParseException(int line, String message) {
+ this(line, message, null);
+ }
+
+ public ParseException(String message) {
+ this(0, message, null);
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
new file mode 100644
index 000000000000..373a9c981ec5
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import com.android.powermodel.util.Conversion;
+
+public class PowerProfile {
+
+ // Remaining fields from the android code for which the actual usage is unclear.
+ // battery.capacity
+ // bluetooth.controller.voltage
+ // modem.controller.voltage
+ // gps.voltage
+ // wifi.controller.voltage
+ // radio.on
+ // radio.scanning
+ // radio.active
+ // memory.bandwidths
+ // wifi.batchedscan
+ // wifi.scan
+ // wifi.on
+ // wifi.active
+ // wifi.controller.tx_levels
+
+ private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)");
+ private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)");
+ private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)");
+
+ private HashMap<Component, ComponentProfile> mComponents = new HashMap();
+
+ /**
+ * Which element we are currently parsing.
+ */
+ enum ElementState {
+ BEGIN,
+ TOP,
+ ITEM,
+ ARRAY,
+ VALUE
+ }
+
+ /**
+ * Implements the reading and power model logic.
+ */
+ private static class Parser {
+ private final InputStream mStream;
+ private final PowerProfile mResult;
+
+ // Builders for the ComponentProfiles.
+ private final AudioProfile mAudio = new AudioProfile();
+ private final BluetoothProfile mBluetooth = new BluetoothProfile();
+ private final CameraProfile mCamera = new CameraProfile();
+ private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder();
+ private final FlashlightProfile mFlashlight = new FlashlightProfile();
+ private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder();
+ private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder();
+ private final ScreenProfile mScreen = new ScreenProfile();
+ private final VideoProfile mVideo = new VideoProfile();
+ private final WifiProfile mWifi = new WifiProfile();
+
+ /**
+ * Constructor to capture the parameters to read.
+ */
+ Parser(InputStream stream) {
+ mStream = stream;
+ mResult = new PowerProfile();
+ }
+
+ /**
+ * Read the stream, parse it, and apply the power model.
+ * Do not call this more than once.
+ */
+ PowerProfile parse() throws ParseException {
+ final SAXParserFactory factory = SAXParserFactory.newInstance();
+ AndroidResourceHandler handler = null;
+ try {
+ final SAXParser saxParser = factory.newSAXParser();
+
+ handler = new AndroidResourceHandler() {
+ @Override
+ public void onItem(Locator locator, String name, float value)
+ throws SAXParseException {
+ Parser.this.onItem(locator, name, value);
+ }
+
+ @Override
+ public void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException {
+ Parser.this.onArray(locator, name, value);
+ }
+ };
+
+ saxParser.parse(mStream, handler);
+ } catch (ParserConfigurationException ex) {
+ // Coding error, not runtime error.
+ throw new RuntimeException(ex);
+ } catch (SAXParseException ex) {
+ throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex);
+ } catch (SAXException | IOException ex) {
+ // Make a guess about the line number.
+ throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex);
+ }
+
+ // TODO: This doesn't cover the multiple algorithms. Some refactoring will
+ // be necessary.
+ mResult.mComponents.put(Component.AUDIO, mAudio);
+ mResult.mComponents.put(Component.BLUETOOTH, mBluetooth);
+ mResult.mComponents.put(Component.CAMERA, mCamera);
+ mResult.mComponents.put(Component.CPU, mCpuBuilder.build());
+ mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight);
+ mResult.mComponents.put(Component.GPS, mGpsBuilder.build());
+ mResult.mComponents.put(Component.MODEM, mModemBuilder.build());
+ mResult.mComponents.put(Component.SCREEN, mScreen);
+ mResult.mComponents.put(Component.VIDEO, mVideo);
+ mResult.mComponents.put(Component.WIFI, mWifi);
+
+ return mResult;
+ }
+
+ /**
+ * Handles an item tag in the power_profile.xml.
+ */
+ public void onItem(Locator locator, String name, float value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("ambient.on".equals(name)) {
+ mScreen.ambientMa = value;
+ } else if ("audio".equals(name)) {
+ mAudio.onMa = value;
+ } else if ("bluetooth.controller.idle".equals(name)) {
+ mBluetooth.idleMa = value;
+ } else if ("bluetooth.controller.rx".equals(name)) {
+ mBluetooth.rxMa = value;
+ } else if ("bluetooth.controller.tx".equals(name)) {
+ mBluetooth.txMa = value;
+ } else if ("camera.avg".equals(name)) {
+ mCamera.onMa = value;
+ } else if ("camera.flashlight".equals(name)) {
+ mFlashlight.onMa = value;
+ } else if ("cpu.suspend".equals(name)) {
+ mCpuBuilder.setSuspendMa(value);
+ } else if ("cpu.idle".equals(name)) {
+ mCpuBuilder.setIdleMa(value);
+ } else if ("cpu.active".equals(name)) {
+ mCpuBuilder.setActiveMa(value);
+ } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) {
+ mCpuBuilder.setClusterPower(index, value);
+ } else if ("gps.on".equals(name)) {
+ mGpsBuilder.setOnMa(value);
+ } else if ("modem.controller.sleep".equals(name)) {
+ mModemBuilder.setSleepMa(value);
+ } else if ("modem.controller.idle".equals(name)) {
+ mModemBuilder.setIdleMa(value);
+ } else if ("modem.controller.rx".equals(name)) {
+ mModemBuilder.setRxMa(value);
+ } else if ("radio.scanning".equals(name)) {
+ mModemBuilder.setScanningMa(value);
+ } else if ("screen.on".equals(name)) {
+ mScreen.onMa = value;
+ } else if ("screen.full".equals(name)) {
+ mScreen.fullMa = value;
+ } else if ("video".equals(name)) {
+ mVideo.onMa = value;
+ } else if ("wifi.controller.idle".equals(name)) {
+ mWifi.idleMa = value;
+ } else if ("wifi.controller.rx".equals(name)) {
+ mWifi.rxMa = value;
+ } else if ("wifi.controller.tx".equals(name)) {
+ mWifi.txMa = value;
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+
+ /**
+ * Handles an array tag in the power_profile.xml.
+ */
+ public void onArray(Locator locator, String name, float[] value) throws SAXParseException {
+ Integer index;
+ try {
+ if ("cpu.clusters.cores".equals(name)) {
+ mCpuBuilder.setCoreCount(Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) {
+ mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value));
+ } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) {
+ mCpuBuilder.setCorePower(index, value);
+ } else if ("gps.signalqualitybased".equals(name)) {
+ mGpsBuilder.setSignalMa(value);
+ } else if ("modem.controller.tx".equals(name)) {
+ mModemBuilder.setTxMa(value);
+ } else {
+ // TODO: Uncomment this when we have all of the items parsed.
+ // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+ // locator, ex);
+ }
+ } catch (ParseException ex) {
+ throw new SAXParseException(ex.getMessage(), locator, ex);
+ }
+ }
+ }
+
+ /**
+ * SAX XML handler that can parse the android resource files.
+ * In our case, all elements are floats.
+ */
+ abstract static class AndroidResourceHandler extends DefaultHandler {
+ /**
+ * The set of names already processed. Map of name to line number.
+ */
+ private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>();
+
+ /**
+ * Where in the document we are parsing.
+ */
+ private Locator mLocator;
+
+ /**
+ * Which element we are currently parsing.
+ */
+ private ElementState mState = ElementState.BEGIN;
+
+ /**
+ * Saved name from item and array elements.
+ */
+ private String mName;
+
+ /**
+ * The text that is currently being captured, or null if {@link #startCapturingText()}
+ * has not been called.
+ */
+ private StringBuilder mText;
+
+ /**
+ * The array values that have been parsed so for for this array. Null if we are
+ * not inside an array tag.
+ */
+ private ArrayList<Float> mArray;
+
+ /**
+ * Called when an item tag is encountered.
+ */
+ public abstract void onItem(Locator locator, String name, float value)
+ throws SAXParseException;
+
+ /**
+ * Called when an array is encountered.
+ */
+ public abstract void onArray(Locator locator, String name, float[] value)
+ throws SAXParseException;
+
+ /**
+ * If we have a Locator set, return the line number, otherwise return 0.
+ */
+ public int getLineNumber() {
+ return mLocator != null ? mLocator.getLineNumber() : 0;
+ }
+
+ /**
+ * Handle setting the parse location object.
+ */
+ public void setDocumentLocator(Locator locator) {
+ mLocator = locator;
+ }
+
+ /**
+ * Handle beginning of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void startElement(String ns, String ln, String element,
+ Attributes attr) throws SAXException {
+ switch (mState) {
+ case BEGIN:
+ // Outer element, we don't care the tag name.
+ mState = ElementState.TOP;
+ return;
+ case TOP:
+ if ("item".equals(element)) {
+ mState = ElementState.ITEM;
+ saveNameAttribute(attr);
+ startCapturingText();
+ return;
+ } else if ("array".equals(element)) {
+ mState = ElementState.ARRAY;
+ mArray = new ArrayList<Float>();
+ saveNameAttribute(attr);
+ return;
+ }
+ break;
+ case ARRAY:
+ if ("value".equals(element)) {
+ mState = ElementState.VALUE;
+ startCapturingText();
+ return;
+ }
+ break;
+ }
+ throw new SAXParseException("unexpected element: '" + element + "'", mLocator);
+ }
+
+ /**
+ * Handle end of an element.
+ *
+ * @param ns Namespace uri
+ * @param ln Local name (inside namespace)
+ * @param element Tag name
+ */
+ @Override
+ public void endElement(String ns, String ln, String element) throws SAXException {
+ switch (mState) {
+ case ITEM: {
+ float value = parseFloat(finishCapturingText());
+ mState = ElementState.TOP;
+ onItem(mLocator, mName, value);
+ break;
+ }
+ case ARRAY: {
+ final int N = mArray.size();
+ float[] values = new float[N];
+ for (int i=0; i<N; i++) {
+ values[i] = mArray.get(i);
+ }
+ mArray = null;
+ mState = ElementState.TOP;
+ onArray(mLocator, mName, values);
+ break;
+ }
+ case VALUE: {
+ mArray.add(parseFloat(finishCapturingText()));
+ mState = ElementState.ARRAY;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Interstitial text received.
+ *
+ * @throws SAXException if there shouldn't be non-whitespace text here
+ */
+ @Override
+ public void characters(char text[], int start, int length) throws SAXException {
+ if (mText == null && length > 0 && !isWhitespace(text, start, length)) {
+ throw new SAXParseException("unexpected text: '"
+ + firstLine(text, start, length).trim() + "'", mLocator);
+ }
+ if (mText != null) {
+ mText.append(text, start, length);
+ }
+ }
+
+ /**
+ * Begin collecting text from inside an element.
+ */
+ private void startCapturingText() {
+ if (mText != null) {
+ throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ mText = new StringBuilder();
+ }
+
+ /**
+ * Stop capturing text from inside an element.
+ *
+ * @return the captured text
+ */
+ private String finishCapturingText() {
+ if (mText == null) {
+ throw new RuntimeException("ASSERTION FAILED: Should already be capturing"
+ + " text. mState=" + mState.name()
+ + " line=" + mLocator.getLineNumber()
+ + " column=" + mLocator.getColumnNumber());
+ }
+ final String result = mText.toString().trim();
+ mText = null;
+ return result;
+ }
+
+ /**
+ * Get the "name" attribute.
+ *
+ * @throws SAXParseException if the name attribute is not present or if
+ * the name has already been seen in the file.
+ */
+ private void saveNameAttribute(Attributes attr) throws SAXParseException {
+ final String name = attr.getValue("name");
+ if (name == null) {
+ throw new SAXParseException("expected 'name' attribute", mLocator);
+ }
+ Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber());
+ if (prev != null) {
+ throw new SAXParseException("name '" + name + "' already seen on line: " + prev,
+ mLocator);
+ }
+ mName = name;
+ }
+
+ /**
+ * Gets the float value of the string.
+ *
+ * @throws SAXParseException if 'text' can't be parsed as a float.
+ */
+ private float parseFloat(String text) throws SAXParseException {
+ try {
+ return Float.parseFloat(text);
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("not a valid float value: '" + text + "'",
+ mLocator, ex);
+ }
+ }
+ }
+
+ /**
+ * Return whether the given substring is all whitespace.
+ */
+ private static boolean isWhitespace(char[] text, int start, int length) {
+ for (int i = start; i < (start + length); i++) {
+ if (!Character.isSpace(text[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the contents of text up to the first newline.
+ */
+ private static String firstLine(char[] text, int start, int length) {
+ // TODO: The line number will be wrong if we skip preceeding blank lines.
+ while (length > 0) {
+ if (Character.isSpace(text[start])) {
+ start++;
+ length--;
+ }
+ }
+ int newlen = 0;
+ for (; newlen < length; newlen++) {
+ final char c = text[newlen];
+ if (c == '\n' || c == '\r') {
+ break;
+ }
+ }
+ return new String(text, start, newlen);
+ }
+
+ /**
+ * If the pattern matches, return the first group of that as an Integer.
+ * If not return null.
+ */
+ private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text)
+ throws SAXParseException {
+ final Matcher m = pattern.matcher(text);
+ if (m.matches()) {
+ try {
+ return Integer.parseInt(m.group(1));
+ } catch (NumberFormatException ex) {
+ throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public static PowerProfile parse(InputStream stream) throws ParseException {
+ return (new Parser(stream)).parse();
+ }
+
+ private PowerProfile() {
+ }
+
+ public ComponentProfile getComponent(Component component) {
+ return mComponents.get(component);
+ }
+
+}
diff --git a/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
new file mode 100644
index 000000000000..63ff3a6b09fa
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class AudioProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
new file mode 100644
index 000000000000..8f5e7d0ae1df
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class BluetoothProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
new file mode 100644
index 000000000000..8ee22d03268c
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CameraProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
new file mode 100644
index 000000000000..0b34fc82622a
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CpuProfile extends ComponentProfile {
+ public float suspendMa;
+ public float idleMa;
+ public float activeMa;
+ public Cluster[] clusters;
+
+ public static class Cluster {
+ public int coreCount;
+ public float onMa;
+ public Frequency[] frequencies;
+ }
+
+ public static class Frequency {
+ public int speedHz;
+ public float onMa;
+ }
+
+ public static class Builder {
+ private float mSuspendMa;
+ private float mIdleMa;
+ private float mActiveMa;
+ private int[] mCoreCount;
+ private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>();
+ private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>();
+ private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>();
+
+ public Builder() {
+ }
+
+ public void setSuspendMa(float value) throws ParseException {
+ mSuspendMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setActiveMa(float value) throws ParseException {
+ mActiveMa = value;
+ }
+
+ public void setCoreCount(int[] value) throws ParseException {
+ mCoreCount = Arrays.copyOf(value, value.length);
+ }
+
+ public void setClusterPower(int cluster, float value) throws ParseException {
+ mClusterOnPower.put(cluster, value);
+ }
+
+ public void setCoreSpeeds(int cluster, int[] value) throws ParseException {
+ mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length));
+ float[] power = mCorePower.get(cluster);
+ if (power != null && value.length != power.length) {
+ throw new ParseException("length of cpu.core_speeds.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.core_power.cluster" + cluster + " (" + power.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_speeds.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public void setCorePower(int cluster, float[] value) throws ParseException {
+ mCorePower.put(cluster, Arrays.copyOf(value, value.length));
+ int[] speeds = mCoreSpeeds.get(cluster);
+ if (speeds != null && value.length != speeds.length) {
+ throw new ParseException("length of cpu.core_power.cluster" + cluster
+ + " (" + value.length + ") is different from length of"
+ + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")");
+ }
+ if (mCoreCount != null && cluster >= mCoreCount.length) {
+ throw new ParseException("cluster " + cluster
+ + " in cpu.core_power.cluster" + cluster
+ + " is larger than the number of clusters specified in cpu.clusters.cores ("
+ + mCoreCount.length + ")");
+ }
+ }
+
+ public CpuProfile build() throws ParseException {
+ final CpuProfile result = new CpuProfile();
+
+ // Validate cluster count
+
+ // All null or none null
+ // TODO
+
+ // Same size
+ // TODO
+
+ // No gaps
+ // TODO
+
+ // Fill in values
+ result.suspendMa = mSuspendMa;
+ result.idleMa = mIdleMa;
+ result.activeMa = mActiveMa;
+ if (mCoreCount != null) {
+ result.clusters = new Cluster[mCoreCount.length];
+ for (int i = 0; i < result.clusters.length; i++) {
+ final Cluster cluster = result.clusters[i] = new Cluster();
+ cluster.coreCount = mCoreCount[i];
+ cluster.onMa = mClusterOnPower.get(i);
+ int[] speeds = mCoreSpeeds.get(i);
+ float[] power = mCorePower.get(i);
+ cluster.frequencies = new Frequency[speeds.length];
+ for (int j = 0; j < speeds.length; j++) {
+ final Frequency freq = cluster.frequencies[j] = new Frequency();
+ freq.speedHz = speeds[j];
+ freq.onMa = power[j];
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
new file mode 100644
index 000000000000..c85f3ff236fd
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class FlashlightProfile extends ComponentProfile {
+ public float onMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
new file mode 100644
index 000000000000..83c06a7881ca
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class GpsProfile extends ComponentProfile {
+ public float onMa;
+ public float[] signalQualityMa;
+
+ public static class Builder {
+ private float onMa;
+ private float[] mSignalQualityMa;
+
+ public Builder() {
+ }
+
+ public void setOnMa(float value) throws ParseException {
+ onMa = value;
+ }
+
+ public void setSignalMa(float[] value) throws ParseException {
+ mSignalQualityMa = value;
+ }
+
+ public GpsProfile build() throws ParseException {
+ GpsProfile result = new GpsProfile();
+ result.onMa = onMa;
+ result.signalQualityMa = mSignalQualityMa == null
+ ? new float[0]
+ : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length);
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
new file mode 100644
index 000000000000..cda72ee205e3
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ModemProfile extends ComponentProfile {
+ public float sleepMa;
+ public float idleMa;
+ public float scanningMa;
+ public float rxMa;
+ public float[] txMa;
+
+ public float getSleepMa() {
+ return sleepMa;
+ }
+
+ public float getIdleMa() {
+ return idleMa;
+ }
+
+ public float getRxMa() {
+ return rxMa;
+ }
+
+ public float[] getTxMa() {
+ return Arrays.copyOf(txMa, txMa.length);
+ }
+
+ public float getScanningMa() {
+ return scanningMa;
+ }
+
+ public static class Builder {
+ private float mSleepMa;
+ private float mIdleMa;
+ private float mRxMa;
+ private float[] mTxMa;
+ private float mScanningMa;
+
+ public Builder() {
+ }
+
+ public void setSleepMa(float value) throws ParseException {
+ mSleepMa = value;
+ }
+
+ public void setIdleMa(float value) throws ParseException {
+ mIdleMa = value;
+ }
+
+ public void setRxMa(float value) throws ParseException {
+ mRxMa = value;
+ }
+
+ public void setTxMa(float[] value) throws ParseException {
+ mTxMa = Arrays.copyOf(value, value.length);
+ }
+
+ public void setScanningMa(float value) throws ParseException {
+ mScanningMa = value;
+ }
+
+ public ModemProfile build() throws ParseException {
+ ModemProfile result = new ModemProfile();
+ result.sleepMa = mSleepMa;
+ result.idleMa = mIdleMa;
+ result.rxMa = mRxMa;
+ result.txMa = mTxMa == null ? new float[0] : mTxMa;
+ result.scanningMa = mScanningMa;
+ return result;
+ }
+ }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
new file mode 100644
index 000000000000..e1051c69dec6
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ScreenProfile extends ComponentProfile {
+ public float onMa;
+ public float fullMa;
+ public float ambientMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
new file mode 100644
index 000000000000..515279552245
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class VideoProfile extends ComponentProfile {
+ public float onMa;
+}
+
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
new file mode 100644
index 000000000000..6f424bf0837d
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class WifiProfile extends ComponentProfile {
+ public float idleMa;
+ public float rxMa;
+ public float txMa;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
new file mode 100644
index 000000000000..9a79a2d48a59
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.powermodel.util;
+
+public class Conversion {
+
+ /**
+ * Convert the the float[] to an int[].
+ * <p>
+ * Values are rounded to the nearest integral value. Null input
+ * results in null output.
+ */
+ public static int[] toIntArray(float[] value) {
+ if (value == null) {
+ return null;
+ }
+ int[] result = new int[value.length];
+ for (int i=0; i<result.length; i++) {
+ result[i] = (int)(value[i] + 0.5f);
+ }
+ return result;
+ }
+
+ /**
+ * No public constructor.
+ */
+ private Conversion() {
+ }
+}