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

Commit 1f89bd6e authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Store aggregated power stats in PowerStatsStore

Bug: 297274264
Test: atest PowerStatsTests

Change-Id: I8b6af08d8ade14503fe7e5d3258ee22f7051ce71
parent 9c97e08d
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
@@ -190,6 +190,27 @@ public final class LongArrayMultiStateCounter implements Parcelable {
        native_setState(mNativeObject, state, timestampMs);
    }

    /**
     * Sets the new values for the given state.
     */
    public void setValues(int state, long[] values) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + (mStateCount - 1) + "]");
        }
        if (values.length != mLength) {
            throw new IllegalArgumentException(
                    "Invalid array length: " + values.length + ", expected: " + mLength);
        }
        LongArrayContainer container = sTmpArrayContainer.getAndSet(null);
        if (container == null || container.mLength != values.length) {
            container = new LongArrayContainer(values.length);
        }
        container.setValues(values);
        native_setValues(mNativeObject, state, container.mNativeObject);
        sTmpArrayContainer.set(container);
    }

    /**
     * Sets the new values.  The delta between the previously set values and these values
     * is distributed among the state according to the time the object spent in those states
@@ -316,6 +337,10 @@ public final class LongArrayMultiStateCounter implements Parcelable {
    @CriticalNative
    private static native void native_setState(long nativeObject, int state, long timestampMs);

    @CriticalNative
    private static native void native_setValues(long nativeObject, int state,
            long longArrayContainerNativeObject);

    @CriticalNative
    private static native void native_updateValues(long nativeObject,
            long longArrayContainerNativeObject, long timestampMs);
+137 −2
Original line number Diff line number Diff line
@@ -16,9 +16,17 @@

package com.android.internal.os;

import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;

@@ -29,15 +37,21 @@ import java.util.Arrays;
 * values;
 */
public class MultiStateStats {
    private static final String TAG = "MultiStateStats";

    private static final String XML_TAG_STATS = "stats";

    /**
     * A set of states, e.g. on-battery, screen-on, procstate.  The state values are integers
     * from 0 to States.mLabels.length
     */
    public static class States {
        final String mName;
        final boolean mTracked;
        final String[] mLabels;

        public States(boolean tracked, String... labels) {
        public States(String name, boolean tracked, String... labels) {
            mName = name;
            this.mTracked = tracked;
            this.mLabels = labels;
        }
@@ -155,11 +169,28 @@ public class MultiStateStats {
                   >>> mStateBitFieldShifts[stateIndex];
        }

        private int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
        int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
            return (baseCompositeState & ~mStateBitFieldMasks[stateIndex])
                    | (value << mStateBitFieldShifts[stateIndex]);
        }

        int setStateInComposite(int compositeState, String stateName, String stateLabel) {
            for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
                States stateConfig = mStates[stateIndex];
                if (stateConfig.mName.equals(stateName)) {
                    for (int state = 0; state < stateConfig.mLabels.length; state++) {
                        if (stateConfig.mLabels[state].equals(stateLabel)) {
                            return setStateInComposite(compositeState, stateIndex, state);
                        }
                    }
                    Slog.e(TAG, "Unexpected label '" + stateLabel + "' for state: " + stateName);
                    return -1;
                }
            }
            Slog.e(TAG, "Unsupported state: " + stateName);
            return -1;
        }

        /**
         * Allocates a new stats container using this Factory's configuration.
         */
@@ -195,6 +226,10 @@ public class MultiStateStats {
            }
            return serialState;
        }

        int getSerialState(int compositeState) {
            return mCompositeToSerialState[compositeState];
        }
    }

    private final Factory mFactory;
@@ -253,6 +288,106 @@ public class MultiStateStats {
        return mCounter.toString();
    }

    /**
     * Stores contents in an XML doc.
     */
    public void writeXml(TypedXmlSerializer serializer) throws IOException {
        long[] tmpArray = new long[mCounter.getArrayLength()];
        writeXmlAllStates(serializer, new int[mFactory.mStates.length], 0, tmpArray);
    }

    private void writeXmlAllStates(TypedXmlSerializer serializer, int[] states, int stateIndex,
            long[] values) throws IOException {
        if (stateIndex < states.length) {
            if (!mFactory.mStates[stateIndex].mTracked) {
                writeXmlAllStates(serializer, states, stateIndex + 1, values);
                return;
            }

            for (int i = 0; i < mFactory.mStates[stateIndex].mLabels.length; i++) {
                states[stateIndex] = i;
                writeXmlAllStates(serializer, states, stateIndex + 1, values);
            }
            return;
        }

        mCounter.getCounts(values, mFactory.getSerialState(states));
        boolean nonZero = false;
        for (long value : values) {
            if (value != 0) {
                nonZero = true;
                break;
            }
        }
        if (!nonZero) {
            return;
        }

        serializer.startTag(null, XML_TAG_STATS);

        for (int i = 0; i < states.length; i++) {
            if (mFactory.mStates[i].mTracked && states[i] != 0) {
                serializer.attribute(null, mFactory.mStates[i].mName,
                        mFactory.mStates[i].mLabels[states[i]]);
            }
        }
        for (int i = 0; i < values.length; i++) {
            if (values[i] != 0) {
                serializer.attributeLong(null, "_" + i, values[i]);
            }
        }
        serializer.endTag(null, XML_TAG_STATS);
    }

    /**
     * Populates the object with contents in an XML doc. The parser is expected to be
     * positioned on the opening tag of the corresponding element.
     */
    public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
            IOException {
        String outerTag = parser.getName();
        long[] tmpArray = new long[mCounter.getArrayLength()];
        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT
               && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
            if (eventType == XmlPullParser.START_TAG) {
                if (parser.getName().equals(XML_TAG_STATS)) {
                    Arrays.fill(tmpArray, 0);
                    int compositeState = 0;
                    int attributeCount = parser.getAttributeCount();
                    for (int i = 0; i < attributeCount; i++) {
                        String attributeName = parser.getAttributeName(i);
                        if (attributeName.startsWith("_")) {
                            int index;
                            try {
                                index = Integer.parseInt(attributeName.substring(1));
                            } catch (NumberFormatException e) {
                                throw new XmlPullParserException(
                                        "Unexpected index syntax: " + attributeName, parser, e);
                            }
                            if (index < 0 || index >= tmpArray.length) {
                                Slog.e(TAG, "State index out of bounds: " + index
                                            + " length: " + tmpArray.length);
                                return false;
                            }
                            tmpArray[index] = parser.getAttributeLong(i);
                        } else {
                            String attributeValue = parser.getAttributeValue(i);
                            compositeState = mFactory.setStateInComposite(compositeState,
                                    attributeName, attributeValue);
                            if (compositeState == -1) {
                                return false;
                            }
                        }
                    }
                    mCounter.setValues(mFactory.getSerialState(compositeState), tmpArray);
                }
            }
            eventType = parser.next();
        }
        return true;
    }

    /**
     * Prints the accumulated stats, one line of every combination of states that has data.
     */
+83 −4
Original line number Diff line number Diff line
@@ -24,9 +24,16 @@ import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;

@@ -61,6 +68,13 @@ public final class PowerStats {
     * to adjust the algorithm in accordance with the stats available on the device.
     */
    public static class Descriptor {
        public static final String XML_TAG_DESCRIPTOR = "descriptor";
        private static final String XML_ATTR_ID = "id";
        private static final String XML_ATTR_NAME = "name";
        private static final String XML_ATTR_STATS_ARRAY_LENGTH = "stats-array-length";
        private static final String XML_ATTR_UID_STATS_ARRAY_LENGTH = "uid-stats-array-length";
        private static final String XML_TAG_EXTRAS = "extras";

        /**
         * {@link BatteryConsumer.PowerComponent} (e.g. CPU, WIFI etc) that this snapshot relates
         * to; or a custom power component ID (if the value
@@ -126,7 +140,7 @@ public final class PowerStats {
            int firstWord = parcel.readInt();
            int version = (firstWord & PARCEL_FORMAT_VERSION_MASK) >>> PARCEL_FORMAT_VERSION_SHIFT;
            if (version != PARCEL_FORMAT_VERSION) {
                Log.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has "
                Slog.w(TAG, "Cannot read PowerStats from Parcel - the parcel format version has "
                           + "changed from " + version + " to " + PARCEL_FORMAT_VERSION);
                return null;
            }
@@ -155,6 +169,71 @@ public final class PowerStats {
                    that.extras);  // Since the Parcel is now unparceled, do a deep comparison
        }

        /**
         * Stores contents in an XML doc.
         */
        public void writeXml(TypedXmlSerializer serializer) throws IOException {
            serializer.startTag(null, XML_TAG_DESCRIPTOR);
            serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
            serializer.attribute(null, XML_ATTR_NAME, name);
            serializer.attributeInt(null, XML_ATTR_STATS_ARRAY_LENGTH, statsArrayLength);
            serializer.attributeInt(null, XML_ATTR_UID_STATS_ARRAY_LENGTH, uidStatsArrayLength);
            try {
                serializer.startTag(null, XML_TAG_EXTRAS);
                extras.saveToXml(serializer);
                serializer.endTag(null, XML_TAG_EXTRAS);
            } catch (XmlPullParserException e) {
                throw new IOException(e);
            }
            serializer.endTag(null, XML_TAG_DESCRIPTOR);
        }

        /**
         * Creates a Descriptor by parsing an XML doc.  The parser is expected to be positioned
         * on or before the opening "descriptor" tag.
         */
        public static Descriptor createFromXml(TypedXmlPullParser parser)
                throws XmlPullParserException, IOException {
            int powerComponentId = -1;
            String name = null;
            int statsArrayLength = 0;
            int uidStatsArrayLength = 0;
            PersistableBundle extras = null;
            int eventType = parser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT
                   && !(eventType == XmlPullParser.END_TAG
                        && parser.getName().equals(XML_TAG_DESCRIPTOR))) {
                if (eventType == XmlPullParser.START_TAG) {
                    switch (parser.getName()) {
                        case XML_TAG_DESCRIPTOR:
                            powerComponentId = parser.getAttributeInt(null, XML_ATTR_ID);
                            name = parser.getAttributeValue(null, XML_ATTR_NAME);
                            statsArrayLength = parser.getAttributeInt(null,
                                    XML_ATTR_STATS_ARRAY_LENGTH);
                            uidStatsArrayLength = parser.getAttributeInt(null,
                                    XML_ATTR_UID_STATS_ARRAY_LENGTH);
                            break;
                        case XML_TAG_EXTRAS:
                            extras = PersistableBundle.restoreFromXml(parser);
                            break;
                    }
                }
                eventType = parser.next();
            }
            if (powerComponentId == -1) {
                return null;
            } else if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
                return new Descriptor(powerComponentId, name, statsArrayLength, uidStatsArrayLength,
                        extras);
            } else if (powerComponentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
                return new Descriptor(powerComponentId, statsArrayLength, uidStatsArrayLength,
                        extras);
            } else {
                Slog.e(TAG, "Unrecognized power component: " + powerComponentId);
                return null;
            }
        }

        @Override
        public int hashCode() {
            return Objects.hash(powerComponentId);
@@ -259,7 +338,7 @@ public final class PowerStats {

            Descriptor descriptor = registry.get(powerComponentId);
            if (descriptor == null) {
                Log.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId);
                Slog.e(TAG, "Unsupported PowerStats for power component ID: " + powerComponentId);
                return null;
            }
            PowerStats stats = new PowerStats(descriptor);
@@ -274,7 +353,7 @@ public final class PowerStats {
                stats.uidStats.put(uid, uidStats);
            }
            if (parcel.dataPosition() != endPos) {
                Log.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length
                Slog.e(TAG, "Corrupted PowerStats parcel. Expected length: " + length
                           + ", actual length: " + (parcel.dataPosition() - startPos));
                return null;
            }
+11 −0
Original line number Diff line number Diff line
@@ -55,6 +55,15 @@ static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
    counter->setState(state, timestamp);
}

static void native_setValues(jlong nativePtr, jint state, jlong longArrayContainerNativePtr) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    std::vector<uint64_t> *vector =
            reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);

    counter->setValue(state, *vector);
}

static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr,
                                jlong timestamp) {
    battery::LongArrayMultiStateCounter *counter =
@@ -210,6 +219,8 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
        // @CriticalNative
        {"native_setState", "(JIJ)V", (void *)native_setState},
        // @CriticalNative
        {"native_setValues", "(JIJ)V", (void *)native_setValues},
        // @CriticalNative
        {"native_updateValues", "(JJJ)V", (void *)native_updateValues},
        // @CriticalNative
        {"native_incrementValues", "(JJJ)V", (void *)native_incrementValues},
+9 −0
Original line number Diff line number Diff line
@@ -31,4 +31,13 @@
    is a relatively expensive operation, this throttle period may need to be adjusted for low-power
    devices-->
    <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>

    <!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
    stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
    <integer name="config_powerStatsAggregationPeriod">14400000</integer>

    <!-- PowerStats aggregation span duration in milliseconds. This is the length of battery
    history time for every aggregated power stats span that is stored stored in PowerStatsStore.
    It should not be larger than config_powerStatsAggregationPeriod (but it can be the same) -->
    <integer name="config_aggregatedPowerStatsSpanDuration">3600000</integer>
</resources>
Loading