Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3718aaab authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Allow arrays of values for power profile data.

Parse arrays for data that has different levels with non-linear
power increase.
Track radio data awake_time from kernel and mobile/total data
transfers.
Use dummy values for default power_profile.xml. Actual values will
be in a product overlay.
parent b81645c8
Loading
Loading
Loading
Loading
+126 −9
Original line number Diff line number Diff line
@@ -30,9 +30,11 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -53,7 +55,7 @@ public final class BatteryStatsImpl extends BatteryStats {
    private static final int MAGIC = 0xBA757475; // 'BATSTATS' 

    // Current on-disk Parcel version
    private static final int VERSION = 38;
    private static final int VERSION = 39;

    private final File mFile;
    private final File mBackupFile;
@@ -149,6 +151,15 @@ public final class BatteryStatsImpl extends BatteryStats {

    long mLastWriteTime = 0; // Milliseconds

    // Mobile data transferred while on battery
    private long[] mMobileDataTx = new long[4];
    private long[] mMobileDataRx = new long[4];
    private long[] mTotalDataTx = new long[4];
    private long[] mTotalDataRx = new long[4];

    private long mRadioDataUptime;
    private long mRadioDataStart;

    /*
     * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
     */
@@ -894,6 +905,39 @@ public final class BatteryStatsImpl extends BatteryStats {
        return kwlt;
    }

    private void doDataPlug(long[] dataTransfer, long currentBytes) {
        dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
        dataTransfer[STATS_UNPLUGGED] = -1;
    }

    private void doDataUnplug(long[] dataTransfer, long currentBytes) {
        dataTransfer[STATS_UNPLUGGED] = currentBytes;
    }

    private long getCurrentRadioDataUptimeMs() {
        try {
            File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
            if (!awakeTimeFile.exists()) return 0;
            BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
            String line = br.readLine();
            br.close();
            return Long.parseLong(line);
        } catch (NumberFormatException nfe) {
            // Nothing
        } catch (IOException ioe) {
            // Nothing
        }
        return 0;
    }

    public long getRadioDataUptimeMs() {
        if (mRadioDataStart == -1) {
            return mRadioDataUptime;
        } else {
            return getCurrentRadioDataUptimeMs() - mRadioDataStart;
        }
    }

    public void doUnplug(long batteryUptime, long batteryRealtime) {
        for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
            Uid u = mUidStats.valueAt(iu);
@@ -905,6 +949,14 @@ public final class BatteryStatsImpl extends BatteryStats {
        for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
            mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
        }
        // Track total mobile data
        doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes());
        doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes());
        doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes());
        doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes());
        // Track radio awake time
        mRadioDataStart = getCurrentRadioDataUptimeMs();
        mRadioDataUptime = 0;
    }

    public void doPlug(long batteryUptime, long batteryRealtime) {
@@ -922,6 +974,13 @@ public final class BatteryStatsImpl extends BatteryStats {
        for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
            mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
        }
        doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes());
        doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes());
        doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes());
        doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes());
        // Track radio awake time
        mRadioDataUptime = getRadioDataUptimeMs();
        mRadioDataStart = -1;
    }

    public void noteStartGps(int uid) {
@@ -1039,6 +1098,7 @@ public final class BatteryStatsImpl extends BatteryStats {
                    break;
            }
        }
        if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
        if (mPhoneDataConnectionType != bin) {
            if (mPhoneDataConnectionType >= 0) {
                mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
@@ -2702,6 +2762,43 @@ public final class BatteryStatsImpl extends BatteryStats {
        return getBatteryRealtimeLocked(curTime);
    }

    private long getTcpBytes(long current, long[] dataBytes, int which) {
        if (which == STATS_LAST) {
            return dataBytes[STATS_LAST];
        } else {
            if (which == STATS_UNPLUGGED) {
                if (dataBytes[STATS_UNPLUGGED] < 0) {
                    return dataBytes[STATS_LAST];
                } else {
                    return current - dataBytes[STATS_UNPLUGGED];
                }
            } else if (which == STATS_TOTAL) {
                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
            }
            return current - dataBytes[STATS_CURRENT];
        }
    }

    /** Only STATS_UNPLUGGED works properly */
    public long getMobileTcpBytesSent(int which) {
        return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which);
    }

    /** Only STATS_UNPLUGGED works properly */
    public long getMobileTcpBytesReceived(int which) {
        return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which);
    }

    /** Only STATS_UNPLUGGED works properly */
    public long getTotalTcpBytesSent(int which) {
        return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which);
    }

    /** Only STATS_UNPLUGGED works properly */
    public long getTotalTcpBytesReceived(int which) {
        return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which);
    }

    @Override
    public int getDischargeStartLevel() {
        synchronized(this) {
@@ -3227,6 +3324,18 @@ public final class BatteryStatsImpl extends BatteryStats {
        mDischargeCurrentLevel = in.readInt();
        mLastWriteTime = in.readLong();

        mMobileDataRx[STATS_LAST] = in.readLong();
        mMobileDataRx[STATS_UNPLUGGED] = -1;
        mMobileDataTx[STATS_LAST] = in.readLong();
        mMobileDataTx[STATS_UNPLUGGED] = -1;
        mTotalDataRx[STATS_LAST] = in.readLong();
        mTotalDataRx[STATS_UNPLUGGED] = -1;
        mTotalDataTx[STATS_LAST] = in.readLong();
        mTotalDataTx[STATS_UNPLUGGED] = -1;

        mRadioDataUptime = in.readLong();
        mRadioDataStart = -1;

        mKernelWakelockStats.clear();
        int NKW = in.readInt();
        for (int ikw = 0; ikw < NKW; ikw++) {
@@ -3301,6 +3410,14 @@ public final class BatteryStatsImpl extends BatteryStats {
        out.writeInt(mDischargeCurrentLevel);
        out.writeLong(mLastWriteTime);

        out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED));
        out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED));
        out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED));
        out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));

        // Write radio uptime for data
        out.writeLong(getRadioDataUptimeMs());

        out.writeInt(mKernelWakelockStats.size());
        for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
            SamplingTimer kwlt = ent.getValue();
+74 −21
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

/**
@@ -118,26 +119,28 @@ public class PowerProfile {
     */
    public static final String POWER_VIDEO = "dsp.video";

    static final HashMap<String, Double> sPowerMap = new HashMap<String, Double>();
    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();

    private static final String TAG_DEVICE = "device";
    private static final String TAG_ITEM = "item";
    private static final String TAG_ARRAY = "array";
    private static final String TAG_ARRAYITEM = "value";
    private static final String ATTR_NAME = "name";

    public PowerProfile(Context context, CharSequence profile) {
    public PowerProfile(Context context) {
        // Read the XML file for the given profile (normally only one per
        // device)
        if (sPowerMap.size() == 0) {
            readPowerValuesFromXml(context, profile);
            readPowerValuesFromXml(context);
        }
    }

    private void readPowerValuesFromXml(Context context, CharSequence profile) {
        // FIXME
        //int id = context.getResources().getIdentifier(profile.toString(), "xml", 
        //        "com.android.internal");
        int id = com.android.internal.R.xml.power_profile_default;
    private void readPowerValuesFromXml(Context context) {
        int id = com.android.internal.R.xml.power_profile;
        XmlResourceParser parser = context.getResources().getXml(id);
        boolean parsingArray = false;
        ArrayList<Double> array = new ArrayList<Double>();
        String arrayName = null;

        try {
            XmlUtils.beginDocument(parser, TAG_DEVICE);
@@ -146,11 +149,20 @@ public class PowerProfile {
                XmlUtils.nextElement(parser);

                String element = parser.getName();
                if (element == null || !(element.equals(TAG_ITEM))) {
                    break;
                }
                if (element == null) break;
                
                String name = parser.getAttributeValue(null, ATTR_NAME);
                if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
                    // Finish array
                    sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
                    parsingArray = false;
                }
                if (element.equals(TAG_ARRAY)) {
                    parsingArray = true;
                    array.clear();
                    arrayName = parser.getAttributeValue(null, ATTR_NAME);
                } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
                    String name = null;
                    if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
                    if (parser.next() == XmlPullParser.TEXT) {
                        String power = parser.getText();
                        double value = 0;
@@ -158,9 +170,17 @@ public class PowerProfile {
                            value = Double.valueOf(power);
                        } catch (NumberFormatException nfe) {
                        }
                        if (element.equals(TAG_ITEM)) {
                            sPowerMap.put(name, value);
                        } else if (parsingArray) {
                            array.add(value);
                        }
                    }
                }
            }
            if (parsingArray) {
                sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
            }
        } catch (XmlPullParserException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
@@ -177,7 +197,40 @@ public class PowerProfile {
     */
    public double getAveragePower(String type) {
        if (sPowerMap.containsKey(type)) {
            return sPowerMap.get(type);
            Object data = sPowerMap.get(type);
            if (data instanceof Double[]) {
                return ((Double[])data)[0];
            } else {
                return (Double) sPowerMap.get(type);
            }
        } else {
            return 0;
        }
    }
    
    /**
     * Returns the average current in mA consumed by the subsystem for the given level. 
     * @param type the subsystem type
     * @param level the level of power at which the subsystem is running. For instance, the
     *  signal strength of the cell network between 0 and 4 (if there are 4 bars max.).
     *  If there is no data for multiple levels, the level is ignored.
     * @return the average current in milliAmps.
     */
    public double getAveragePower(String type, int level) {
        if (sPowerMap.containsKey(type)) {
            Object data = sPowerMap.get(type);
            if (data instanceof double[]) {
                final double[] values = (double[]) data;
                if (values.length > level) {
                    return values[level];
                } else if (values.length < 0) {
                    return values[0];
                } else {
                    return values[values.length - 1];
                }
            } else {
                return (Double) data;
            }
        } else {
            return 0;
        }
+18 −15
Original line number Diff line number Diff line
@@ -19,19 +19,22 @@

<device name="Android">
  <item name="none">0</item>
  <item name="screen.on">30</item>
  <item name="bluetooth.active">103</item>
  <item name="bluetooth.on">5</item>
  <item name="screen.full">114</item>
  <item name="wifi.on">23</item>
  <item name="wifi.active">200</item>
  <item name="wifi.scan">200</item>
  <item name="cpu.idle">1.6</item>
  <item name="cpu.normal">100</item>
  <item name="cpu.full">140</item>
  <item name="dsp.audio">70</item>
  <item name="dsp.video">100</item>
  <item name="radio.on">3</item>
  <item name="radio.active">175</item>
  <item name="gps.on">120</item>
  <item name="screen.on">0.1</item>
  <item name="bluetooth.active">0.1</item>
  <item name="bluetooth.on">0.1</item>
  <item name="screen.full">0.1</item>
  <item name="wifi.on">0.1</item>
  <item name="wifi.active">0.1</item>
  <item name="wifi.scan">0.1</item>
  <item name="cpu.idle">0.1</item>
  <item name="cpu.normal">0.2</item>
  <item name="cpu.full">1</item>
  <item name="dsp.audio">0.1</item>
  <item name="dsp.video">0.1</item>
  <item name="radio.active">1</item>
  <item name="gps.on">1</item>
  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
      <value>1</value>
      <value>0.1</value>
  </array>
</device>