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

Commit 609abbbd authored by Mat Bevilacqua's avatar Mat Bevilacqua Committed by Android (Google) Code Review
Browse files

Merge "Initial commit of PowerStatsService"

parents cf71d0c1 81601212
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