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

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

Initial commit of PowerStatsService

Bug: 164465661
Bug: 164466995
Bug: 167280723
Test: Verified PowerStatsService is functional on targets that
have ODPM data.

Change-Id: Ic555b380c566ea26bc2214374f142c5448ea2ee7
parent 969e1943
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -3384,6 +3384,7 @@ public abstract class Context {
    /** @hide */
    @StringDef(suffix = { "_SERVICE" }, value = {
            POWER_SERVICE,
            //@hide: POWER_STATS_SERVICE,
            WINDOW_SERVICE,
            LAYOUT_INFLATER_SERVICE,
            ACCOUNT_SERVICE,
@@ -3724,6 +3725,16 @@ public abstract class Context {
     */
    public static final String POWER_SERVICE = "power";

    /**
     * Use with {@link #getSystemService(String)} to retrieve a
     * {@link android.os.PowerStatsService} for accessing power stats
     * service.
     *
     * @see #getSystemService(String)
     * @hide
     */
    public static final String POWER_STATS_SERVICE = "power_stats";

    /**
     * Use with {@link #getSystemService(String)} to retrieve a
     * {@link android.os.RecoverySystem} for accessing the recovery system
+6 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import "frameworks/base/core/proto/android/server/fingerprint.proto";
import "frameworks/base/core/proto/android/server/jobscheduler.proto";
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
import "frameworks/base/core/proto/android/server/powerstatsservice.proto";
import "frameworks/base/core/proto/android/server/rolemanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
@@ -510,6 +511,11 @@ message IncidentProto {
        (section).args = "sensorservice --proto"
    ];

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

    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
    optional android.util.TextDumpProto textdump_wifi = 4000 [
        (section).type = SECTION_TEXT_DUMPSYS,
+67 −0
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.
 */

syntax = "proto2";

package com.android.server.powerstats;

option java_multiple_files = true;

message IncidentReportProto {
    /** Section number matches that in incident.proto */
    optional PowerStatsServiceProto incident_report = 3054;
}

message PowerStatsServiceProto {
    repeated RailInfoProto rail_info = 1;
    repeated EnergyDataProto energy_data = 2;
}

/**
 * Rail information:
 * Reports information related to the rails being monitored.
 */
message RailInfoProto {
    /** Index corresponding to the rail */
    optional int32 index = 1;

    /** Name of the rail (opaque to the framework) */
    optional string rail_name = 2;

    /** Name of the subsystem to which this rail belongs (opaque to the framework) */
    optional string subsys_name = 3;

    /** Hardware sampling rate */
    optional int32 sampling_rate = 4;
}

/**
 * Rail level energy measurements:
 * Reports accumulated energy since boot on each rail.
 */
message EnergyDataProto {
    /**
     * Index corresponding to the rail. This index matches
     * the index returned in RailInfo
     */
    optional int32 index = 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;
}
+65 −0
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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.util.Log;

/**
 * BatteryTrigger instantiates a BroadcastReceiver that listens for changes
 * to the battery.  When the battery level drops by 1% a message is sent to
 * the PowerStatsLogger to log the rail energy data to on-device storage.
 */
public final class BatteryTrigger extends PowerStatsLogTrigger {
    private static final String TAG = BatteryTrigger.class.getSimpleName();
    private static final boolean DEBUG = false;

    private int mBatteryLevel = 0;

    private final BroadcastReceiver mBatteryLevelReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            switch (intent.getAction()) {
                case Intent.ACTION_BATTERY_CHANGED:
                    int newBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);

                    if (newBatteryLevel < mBatteryLevel) {
                        if (DEBUG) Log.d(TAG, "Battery level dropped.  Log rail data");
                        logPowerStatsData();
                    }

                    mBatteryLevel = newBatteryLevel;
                    break;
            }
        }
    };

    public BatteryTrigger(Context context, PowerStatsLogger powerStatsLogger,
            boolean triggerEnabled) {
        super(context, powerStatsLogger);

        if (triggerEnabled) {
            IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
            Intent batteryStatus = mContext.registerReceiver(mBatteryLevelReceiver, filter);
            mBatteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
        }
    }
}
+286 −0
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);
    }
}
Loading