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

Commit a375ea0b authored by Mat Bevilacqua's avatar Mat Bevilacqua
Browse files

Migrate to PowerStats HAL 2.0

Bug: 169622556
Test: Tested and verified ODPM data is correctly captured in incident
reports.  Tests pass.  Service gracefully fails on devices without
PowerStats HAL 2.0 implementation.

Change-Id: I521e0a8fd26122476601d8c8450f0508549eb311
parent d681a590
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -511,9 +511,14 @@ message IncidentProto {
        (section).args = "sensorservice --proto"
    ];

    optional com.android.server.powerstats.PowerStatsServiceProto powerstats = 3054 [
    optional com.android.server.powerstats.PowerStatsServiceMeterProto powerstats_meter = 3054 [
        (section).type = SECTION_DUMPSYS,
        (section).args = "power_stats --proto"
        (section).args = "power_stats --proto meter"
    ];

    optional com.android.server.powerstats.PowerStatsServiceModelProto powerstats_model = 3055 [
        (section).type = SECTION_DUMPSYS,
        (section).args = "power_stats --proto model"
    ];

    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
+77 −22
Original line number Diff line number Diff line
@@ -20,44 +20,99 @@ package com.android.server.powerstats;

option java_multiple_files = true;

message IncidentReportProto {
/**
 * IncidentReportMeterProto is used only in the parsing tool located
 * in frameworks/base/tools which is used to parse this data out of
 * incident reports.
 */
message IncidentReportMeterProto {
    /** Section number matches that in incident.proto */
    optional PowerStatsServiceMeterProto incident_report = 3054;
}

/**
 * IncidentReportModelProto is used only in the parsing tool located
 * in frameworks/base/tools which is used to parse this data out of
 * incident reports.
 */
message IncidentReportModelProto {
    /** Section number matches that in incident.proto */
    optional PowerStatsServiceProto incident_report = 3054;
    optional PowerStatsServiceModelProto incident_report = 3055;
}

message PowerStatsServiceProto {
    repeated RailInfoProto rail_info = 1;
    repeated EnergyDataProto energy_data = 2;
/**
 * EnergyConsumer (model) data is exposed by the PowerStats HAL.  This data
 * represents modeled energy consumption estimates and is provided per
 * subsystem.  The default subsystems are defined in EnergyConsumerId.aidl.
 * Energy model estimates will be logged to incident reports in addition to
 * the raw energy meter data.
 */
message PowerStatsServiceModelProto {
    repeated EnergyConsumerIdProto energy_consumer_id = 1;
    repeated EnergyConsumerResultProto energy_consumer_result = 2;
}

/**
 * Rail information:
 * Reports information related to the rails being monitored.
 * EnergyMeasurement (meter) data is exposed by the PowerStats HAL.  This data
 * represents measurements taken directly from on-device energy meters.
 * This raw energy meter data will be logged to incident reports.
 */
message RailInfoProto {
    /** Index corresponding to the rail */
    optional int32 index = 1;
message PowerStatsServiceMeterProto {
    repeated ChannelInfoProto channel_info = 1;
    repeated EnergyMeasurementProto energy_measurement = 2;
}

    /** Name of the rail (opaque to the framework) */
    optional string rail_name = 2;
/**
 * Energy consumer ID:
 * A list of default subsystems for which energy consumption estimates
 * may be provided (hardware dependent).
 */
message EnergyConsumerIdProto {
    /** Unique index identifying the energy consumer. */
    optional int32 energy_consumer_id = 1;
}

    /** Name of the subsystem to which this rail belongs (opaque to the framework) */
    optional string subsys_name = 3;
/**
 * Energy consumer result:
 * An estimate of energy consumption since boot for the subsystem identified
 * by the unique energy_consumer_id.
 */
message EnergyConsumerResultProto {
    /** Unique index identifying the energy consumer. */
    optional int32 energy_consumer_id = 1;

    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
    optional int64 timestamp_ms = 2;

    /** Accumulated energy since device boot in microwatt-seconds (uWs) */
    optional int64 energy_uws = 3;
}

/**
 * Channel information:
 * Reports information related to the energy meter channels being monitored.
 */
message ChannelInfoProto {
    /**
     * Index corresponding to the energy meter channel. This index matches
     * the index returned in ChannelInfo.
     */
    optional int32 channel_id = 1;

    /** Hardware sampling rate */
    optional int32 sampling_rate = 4;
    /** Name of the energy meter channel */
    optional string channel_name = 2;
}

/**
 * Rail level energy measurements:
 * Reports accumulated energy since boot on each rail.
 * Energy measurements:
 * Reports accumulated energy since boot for each energy meter.
 */
message EnergyDataProto {
message EnergyMeasurementProto {
    /**
     * Index corresponding to the rail. This index matches
     * the index returned in RailInfo
     * Index corresponding to the energy meter channel. This index matches
     * the index returned in ChannelInfo.
     */
    optional int32 index = 1;
    optional int32 channel_id = 1;

    /** Time since device boot(CLOCK_BOOTTIME) in milli-seconds */
    optional int64 timestamp_ms = 2;
+1 −0
Original line number Diff line number Diff line
@@ -116,6 +116,7 @@ java_library_static {
        "android.hardware.contexthub-V1.0-java",
        "android.hardware.rebootescrow-java",
        "android.hardware.soundtrigger-V2.3-java",
        "android.hardware.power.stats-java",
        "android.hidl.manager-V1.2-java",
        "capture_state_listener-aidl-java",
        "dnsresolver_aidl_interface-java",
+0 −286
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.server.powerstats;

import android.util.Log;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
import android.util.proto.WireTypeMismatchException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * PowerStatsData is a class that performs two operations:
 * 1) Unpacks serialized protobuf byte arrays, as defined in powerstatsservice.proto,
 *    into RailInfo or EnergyData object arrays.
 *
 * 2) Packs RailInfo or EnergyData object arrays in protobuf byte arrays as
 *    defined in powerstatsservice.proto.
 *
 * Inside frameworks, proto source is generated with the genstream option
 * and therefore the getter/setter helper functions are not available.
 * The protos need to be packed/unpacked in a more manual way using
 * ProtoOutputStream/ProtoInputStream.
 */
public class PowerStatsData {
    private static final String TAG = PowerStatsData.class.getSimpleName();

    private List<Data> mDataList;

    public PowerStatsData(ProtoInputStream pis) throws IOException {
        mDataList = new ArrayList<Data>();
        unpackProto(pis);
    }

    public PowerStatsData(Data[] data) {
        mDataList = new ArrayList<Data>(Arrays.asList(data));
    }

    private void unpackProto(ProtoInputStream pis) throws IOException {
        long token;

        while (true) {
            try {
                switch (pis.nextField()) {
                    case (int) PowerStatsServiceProto.RAIL_INFO:
                        token = pis.start(PowerStatsServiceProto.RAIL_INFO);
                        mDataList.add(new RailInfo(pis));
                        pis.end(token);
                        break;

                    case (int) PowerStatsServiceProto.ENERGY_DATA:
                        token = pis.start(PowerStatsServiceProto.ENERGY_DATA);
                        mDataList.add(new EnergyData(pis));
                        pis.end(token);
                        break;

                    case ProtoInputStream.NO_MORE_FIELDS:
                        return;

                    default:
                        Log.e(TAG, "Unhandled field in proto: "
                                + ProtoUtils.currentFieldToString(pis));
                        break;
                }
            } catch (WireTypeMismatchException wtme) {
                Log.e(TAG, "Wire Type mismatch in proto: " + ProtoUtils.currentFieldToString(pis));
            }
        }
    }

    /**
     * Write this object to an output stream in protobuf format.
     *
     * @param pos ProtoOutputStream of file where data is to be written.  Data is
     *            written in protobuf format as defined by powerstatsservice.proto.
     */
    public void toProto(ProtoOutputStream pos) {
        long token;

        for (Data data : mDataList) {
            if (data instanceof RailInfo) {
                token = pos.start(PowerStatsServiceProto.RAIL_INFO);
            } else {
                token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
            }
            data.toProto(pos);
            pos.end(token);
        }
    }

    /**
     * Convert mDataList to proto format and return the serialized byte array.
     *
     * @return byte array containing a serialized protobuf of mDataList.
     */
    public byte[] getProtoBytes() {
        ProtoOutputStream pos = new ProtoOutputStream();
        long token;

        for (Data data : mDataList) {
            if (data instanceof RailInfo) {
                token = pos.start(PowerStatsServiceProto.RAIL_INFO);
            } else {
                token = pos.start(PowerStatsServiceProto.ENERGY_DATA);
            }
            data.toProto(pos);
            pos.end(token);
        }
        return pos.getBytes();
    }

    /**
     * Print this object to logcat.
     */
    public void print() {
        for (Data data : mDataList) {
            Log.d(TAG, data.toString());
        }
    }

    /**
     * RailInfo is a class that stores a description for an individual ODPM
     * rail.  It provides functionality to unpack a RailInfo object from a
     * serialized protobuf byte array, and to pack a RailInfo object into
     * a ProtoOutputStream.
     */
    public static class RailInfo extends Data {
        public String mRailName;
        public String mSubSysName;
        public long mSamplingRate;

        public RailInfo(ProtoInputStream pis) throws IOException {
            unpackProto(pis);
        }

        public RailInfo(long index, String railName, String subSysName, long samplingRate) {
            mIndex = index;
            mRailName = railName;
            mSubSysName = subSysName;
            mSamplingRate = samplingRate;
        }

        @Override
        protected void unpackProto(ProtoInputStream pis) throws IOException {
            while (true) {
                try {
                    switch (pis.nextField()) {
                        case (int) RailInfoProto.INDEX:
                            mIndex = pis.readInt(RailInfoProto.INDEX);
                            break;

                        case (int) RailInfoProto.RAIL_NAME:
                            mRailName = pis.readString(RailInfoProto.RAIL_NAME);
                            break;

                        case (int) RailInfoProto.SUBSYS_NAME:
                            mSubSysName = pis.readString(RailInfoProto.SUBSYS_NAME);
                            break;

                        case (int) RailInfoProto.SAMPLING_RATE:
                            mSamplingRate = pis.readInt(RailInfoProto.SAMPLING_RATE);
                            break;

                        case ProtoInputStream.NO_MORE_FIELDS:
                            return;

                        default:
                            Log.e(TAG, "Unhandled field in RailInfoProto: "
                                    + ProtoUtils.currentFieldToString(pis));
                            break;
                    }
                } catch (WireTypeMismatchException wtme) {
                    Log.e(TAG, "Wire Type mismatch in RailInfoProto: "
                            + ProtoUtils.currentFieldToString(pis));
                }
            }
        }

        @Override
        public void toProto(ProtoOutputStream pos) {
            pos.write(RailInfoProto.INDEX, mIndex);
            pos.write(RailInfoProto.RAIL_NAME, mRailName);
            pos.write(RailInfoProto.SUBSYS_NAME, mSubSysName);
            pos.write(RailInfoProto.SAMPLING_RATE, mSamplingRate);
        }

        @Override
        public String toString() {
            return String.format("Index = " + mIndex
                + ", RailName = " + mRailName
                + ", SubSysName = " + mSubSysName
                + ", SamplingRate = " + mSamplingRate);
        }
    }

    /**
     * EnergyData is a class that stores an energy (uWs) data reading for an
     * individual ODPM rail.  It provides functionality to unpack an EnergyData
     * object from a serialized protobuf byte array, and to pack an EnergyData
     * object into a ProtoOutputStream.
     */
    public static class EnergyData extends Data {
        public long mTimestampMs;
        public long mEnergyUWs;

        public EnergyData(ProtoInputStream pis) throws IOException {
            unpackProto(pis);
        }

        public EnergyData(long index, long timestampMs, long energyUWs) {
            mIndex = index;
            mTimestampMs = timestampMs;
            mEnergyUWs = energyUWs;
        }

        @Override
        protected void unpackProto(ProtoInputStream pis) throws IOException {
            while (true) {
                try {
                    switch (pis.nextField()) {
                        case (int) EnergyDataProto.INDEX:
                            mIndex = pis.readInt(EnergyDataProto.INDEX);
                            break;

                        case (int) EnergyDataProto.TIMESTAMP_MS:
                            mTimestampMs = pis.readLong(EnergyDataProto.TIMESTAMP_MS);
                            break;

                        case (int) EnergyDataProto.ENERGY_UWS:
                            mEnergyUWs = pis.readLong(EnergyDataProto.ENERGY_UWS);
                            break;

                        case ProtoInputStream.NO_MORE_FIELDS:
                            return;

                        default:
                            Log.e(TAG, "Unhandled field in EnergyDataProto: "
                                    + ProtoUtils.currentFieldToString(pis));
                            break;
                    }
                } catch (WireTypeMismatchException wtme) {
                    Log.e(TAG, "Wire Type mismatch in EnergyDataProto: "
                            + ProtoUtils.currentFieldToString(pis));
                }
            }
        }

        @Override
        protected void toProto(ProtoOutputStream pos) {
            pos.write(EnergyDataProto.INDEX, mIndex);
            pos.write(EnergyDataProto.TIMESTAMP_MS, mTimestampMs);
            pos.write(EnergyDataProto.ENERGY_UWS, mEnergyUWs);
        }

        @Override
        public String toString() {
            return String.format("Index = " + mIndex
                + ", Timestamp (ms) = " + mTimestampMs
                + ", Energy (uWs) = " + mEnergyUWs);
        }
    }

    private abstract static class Data {
        public long mIndex;
        protected abstract void unpackProto(ProtoInputStream pis) throws IOException;
        protected abstract void toProto(ProtoOutputStream pos);
    }
}
+14 −12
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ public class PowerStatsDataStorage {
     *             array and written to on-device storage.
     */
    public void write(byte[] data) {
        if (data.length > 0) {
            mLock.lock();

            long currentTimeMillis = System.currentTimeMillis();
@@ -212,6 +213,7 @@ public class PowerStatsDataStorage {

            mLock.unlock();
        }
    }

    /**
     * Reads all DataElements stored in on-device storage.  For each
Loading