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

Commit 9c97e08d authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Add PowerStatsStore for aggregated PowerStats spans

Bug: 297274264
Test: atest FrameworksCoreTests:BatteryStatsTests PowerStatsTests

Change-Id: I1cfb88f0562f1b7e3dbec30045ae55137f9b66c1
parent c5372f9f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@ public final class BatteryUsageStatsQuery implements Parcelable {
         * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
         * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
         */
        // TODO(b/298459065): switch to monotonic clock
        public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
            mFromTimestamp = fromTimestamp;
            mToTimestamp = toTimestamp;
+80 −23
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ import android.telephony.ModemActivityInfo;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.StatsEvent;
@@ -119,13 +120,16 @@ import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BatteryUsageStatsStore;
import com.android.server.power.stats.PowerStatsScheduler;
import com.android.server.power.stats.PowerStatsStore;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import com.android.server.power.stats.wakeups.CpuWakeupStats;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -138,6 +142,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -155,7 +160,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
    static final String TAG = "BatteryStatsService";
    static final String TRACE_TRACK_WAKEUP_REASON = "wakeup_reason";
    static final boolean DBG = false;
    private static final boolean BATTERY_USAGE_STORE_ENABLED = true;

    private static IBatteryStats sService;

@@ -165,11 +169,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
    private final BatteryStatsImpl.BatteryStatsConfig mBatteryStatsConfig;
    final BatteryStatsImpl mStats;
    final CpuWakeupStats mCpuWakeupStats;
    private final BatteryUsageStatsStore mBatteryUsageStatsStore;
    private final PowerStatsStore mPowerStatsStore;
    private final PowerStatsScheduler mPowerStatsScheduler;
    private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
    private final Context mContext;
    private final BatteryExternalStatsWorker mWorker;
    private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
    private final AtomicFile mConfigFile;

    private volatile boolean mMonitorEnabled = true;

    private native void getRailEnergyPowerStats(RailStats railStats);
@@ -403,16 +410,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub
        mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
        mStats.startTrackingSystemServerCpuTime();

        if (BATTERY_USAGE_STORE_ENABLED) {
            mBatteryUsageStatsStore =
                    new BatteryUsageStatsStore(context, mStats, systemDir, mHandler);
        } else {
            mBatteryUsageStatsStore = null;
        }
        mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
        mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
                mBatteryUsageStatsStore);
                mPowerStatsStore);
        mPowerStatsScheduler = new PowerStatsScheduler(mPowerStatsStore, mMonotonicClock, mHandler,
                mStats, mBatteryUsageStatsProvider);
        mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map, mHandler);
        mConfigFile = new AtomicFile(new File(systemDir, "battery_usage_stats_config"));
    }

    /**
@@ -471,9 +475,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
     */
    public void onSystemReady() {
        mStats.onSystemReady();
        if (BATTERY_USAGE_STORE_ENABLED) {
            mBatteryUsageStatsStore.onSystemReady();
        }
        mPowerStatsScheduler.start();
    }

    private final class LocalService extends BatteryStatsInternal {
@@ -900,12 +902,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                    bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
                    break;
                case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
                    if (!BATTERY_USAGE_STORE_ENABLED) {
                        return StatsManager.PULL_SKIP;
                    }

                    final long sessionStart = mBatteryUsageStatsStore
                            .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
                    final long sessionStart =
                            getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
                    final long sessionEnd;
                    synchronized (mStats) {
                        sessionEnd = mStats.getStartClockTime();
@@ -918,8 +916,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                                    .aggregateSnapshots(sessionStart, sessionEnd)
                                    .build();
                    bus = getBatteryUsageStats(List.of(queryBeforeReset)).get(0);
                    mBatteryUsageStatsStore
                            .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
                    setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
                    break;
                default:
                    throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
@@ -2652,6 +2649,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
        mStats.dumpAggregatedStats(pw, /* startTime */ 0, /* endTime */0);
    }

    private void dumpPowerStatsStore(PrintWriter pw) {
        mPowerStatsStore.dump(new IndentingPrintWriter(pw, "  "));
    }

    private void dumpPowerStatsStoreTableOfContents(PrintWriter pw) {
        mPowerStatsStore.dumpTableOfContents(new IndentingPrintWriter(pw, "  "));
    }

    private void dumpMeasuredEnergyStats(PrintWriter pw) {
        // Wait for the completion of pending works if there is any
        awaitCompletion();
@@ -2797,7 +2802,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                    synchronized (mStats) {
                        mStats.resetAllStatsAndHistoryLocked(
                                BatteryStatsImpl.RESET_REASON_ADB_COMMAND);
                        mBatteryUsageStatsStore.removeAllSnapshots();
                        mPowerStatsStore.reset();
                        pw.println("Battery stats and history reset.");
                        noOutput = true;
                    }
@@ -2899,6 +2904,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                } else if ("--aggregated".equals(arg)) {
                    dumpAggregatedStats(pw);
                    return;
                } else if ("--store".equals(arg)) {
                    dumpPowerStatsStore(pw);
                    return;
                } else if ("--store-toc".equals(arg)) {
                    dumpPowerStatsStoreTableOfContents(pw);
                    return;
                } else if ("-a".equals(arg)) {
                    flags |= BatteryStats.DUMP_VERBOSE;
                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -3368,6 +3379,52 @@ public final class BatteryStatsService extends IBatteryStats.Stub
        }
    }

    private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
            "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";

    /**
     * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
     * in persistent file.
     */
    public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
        synchronized (mConfigFile) {
            Properties props = new Properties();
            try (InputStream in = mConfigFile.openRead()) {
                props.load(in);
            } catch (IOException e) {
                Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
            }
            props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
                    String.valueOf(timestamp));
            FileOutputStream out = null;
            try {
                out = mConfigFile.startWrite();
                props.store(out, "Statsd atom pull timestamps");
                mConfigFile.finishWrite(out);
            } catch (IOException e) {
                mConfigFile.failWrite(out);
                Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
            }
        }
    }

    /**
     * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
     * statsd atom pull.
     */
    public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
        synchronized (mConfigFile) {
            Properties props = new Properties();
            try (InputStream in = mConfigFile.openRead()) {
                props.load(in);
            } catch (IOException e) {
                Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
            }
            return Long.parseLong(
                    props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
        }
    }

    /**
     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
     */
+45 −13
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ public class BatteryUsageStatsProvider {
    private static final String TAG = "BatteryUsageStatsProv";
    private final Context mContext;
    private final BatteryStats mStats;
    private final BatteryUsageStatsStore mBatteryUsageStatsStore;
    private final PowerStatsStore mPowerStatsStore;
    private final PowerProfile mPowerProfile;
    private final CpuScalingPolicies mCpuScalingPolicies;
    private final Object mLock = new Object();
@@ -58,10 +58,10 @@ public class BatteryUsageStatsProvider {

    @VisibleForTesting
    public BatteryUsageStatsProvider(Context context, BatteryStats stats,
            BatteryUsageStatsStore batteryUsageStatsStore) {
            PowerStatsStore powerStatsStore) {
        mContext = context;
        mStats = stats;
        mBatteryUsageStatsStore = batteryUsageStatsStore;
        mPowerStatsStore = powerStatsStore;
        mPowerProfile = stats instanceof BatteryStatsImpl
                ? ((BatteryStatsImpl) stats).getPowerProfile()
                : new PowerProfile(context);
@@ -314,20 +314,52 @@ public class BatteryUsageStatsProvider {
        final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
                customEnergyConsumerNames, includePowerModels, includeProcessStateData,
                minConsumedPowerThreshold);
        if (mBatteryUsageStatsStore == null) {
            Log.e(TAG, "BatteryUsageStatsStore is unavailable");
        if (mPowerStatsStore == null) {
            Log.e(TAG, "PowerStatsStore is unavailable");
            return builder.build();
        }

        final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
        for (long timestamp : timestamps) {
            if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
                final BatteryUsageStats snapshot =
                        mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
                if (snapshot == null) {
        List<PowerStatsSpan.Metadata> toc = mPowerStatsStore.getTableOfContents();
        for (PowerStatsSpan.Metadata spanMetadata : toc) {
            if (!spanMetadata.getSections().contains(BatteryUsageStatsSection.TYPE)) {
                continue;
            }

            // BatteryUsageStatsQuery is expressed in terms of wall-clock time range for the
            // session end time.
            //
            // The following algorithm is correct when there is only one time frame in the span.
            // When the wall-clock time is adjusted in the middle of an stats span,
            // constraining it by wall-clock time becomes ambiguous. In this case, the algorithm
            // only covers some situations, but not others.  When using the resulting data for
            // analysis, we should always pay attention to the full set of included timeframes.
            // TODO(b/298459065): switch to monotonic clock
            long minTime = Long.MAX_VALUE;
            long maxTime = 0;
            for (PowerStatsSpan.TimeFrame timeFrame : spanMetadata.getTimeFrames()) {
                long spanEndTime = timeFrame.startTime + timeFrame.duration;
                minTime = Math.min(minTime, spanEndTime);
                maxTime = Math.max(maxTime, spanEndTime);
            }

            // Per BatteryUsageStatsQuery API, the "from" timestamp is *exclusive*,
            // while the "to" timestamp is *inclusive*.
            boolean isInRange =
                    (query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp())
                    && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
            if (!isInRange) {
                continue;
            }

            PowerStatsSpan powerStatsSpan = mPowerStatsStore.loadPowerStatsSpan(
                    spanMetadata.getId(), BatteryUsageStatsSection.TYPE);
            if (powerStatsSpan == null) {
                continue;
            }

            for (PowerStatsSpan.Section section : powerStatsSpan.getSections()) {
                BatteryUsageStats snapshot =
                        ((BatteryUsageStatsSection) section).getBatteryUsageStats();
                if (!Arrays.equals(snapshot.getCustomPowerComponentNames(),
                        customEnergyConsumerNames)) {
                    Log.w(TAG, "Ignoring older BatteryUsageStats snapshot, which has different "
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.power.stats;

import android.os.BatteryUsageStats;
import android.util.IndentingPrintWriter;

import com.android.modules.utils.TypedXmlSerializer;

import java.io.IOException;

class BatteryUsageStatsSection extends PowerStatsSpan.Section {
    public static final String TYPE = "battery-usage-stats";

    private final BatteryUsageStats mBatteryUsageStats;

    BatteryUsageStatsSection(BatteryUsageStats batteryUsageStats) {
        super(TYPE);
        mBatteryUsageStats = batteryUsageStats;
    }

    public BatteryUsageStats getBatteryUsageStats() {
        return mBatteryUsageStats;
    }

    @Override
    void write(TypedXmlSerializer serializer) throws IOException {
        mBatteryUsageStats.writeXml(serializer);
    }

    @Override
    public void dump(IndentingPrintWriter ipw) {
        mBatteryUsageStats.dump(ipw, "");
    }
}
+73 −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.power.stats;

import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Handler;

import com.android.internal.os.MonotonicClock;

/**
 * Controls the frequency at which {@link PowerStatsSpan}'s are generated and stored in
 * {@link PowerStatsStore}.
 */
public class PowerStatsScheduler {
    private final PowerStatsStore mPowerStatsStore;
    private final MonotonicClock mMonotonicClock;
    private final Handler mHandler;
    private final BatteryStatsImpl mBatteryStats;
    private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;

    public PowerStatsScheduler(PowerStatsStore powerStatsStore, MonotonicClock monotonicClock,
            Handler handler, BatteryStatsImpl batteryStats,
            BatteryUsageStatsProvider batteryUsageStatsProvider) {
        mPowerStatsStore = powerStatsStore;
        mMonotonicClock = monotonicClock;
        mHandler = handler;
        mBatteryStats = batteryStats;
        mBatteryUsageStatsProvider = batteryUsageStatsProvider;
    }

    /**
     * Kicks off the scheduling of power stats aggregation spans.
     */
    public void start() {
        mBatteryStats.setBatteryResetListener(this::storeBatteryUsageStatsOnReset);
    }

    private void storeBatteryUsageStatsOnReset(int resetReason) {
        if (resetReason == BatteryStatsImpl.RESET_REASON_CORRUPT_FILE) {
            return;
        }

        final BatteryUsageStats batteryUsageStats =
                mBatteryUsageStatsProvider.getBatteryUsageStats(
                        new BatteryUsageStatsQuery.Builder()
                                .setMaxStatsAgeMs(0)
                                .includePowerModels()
                                .includeProcessStateData()
                                .build());

        // TODO(b/188068523): BatteryUsageStats should contain monotonic time for start and end
        // When that is done, we will be able to use the BatteryUsageStats' monotonic start time
        long monotonicStartTime =
                mMonotonicClock.monotonicTime() - batteryUsageStats.getStatsDuration();
        mHandler.post(() ->
                mPowerStatsStore.storeBatteryUsageStats(monotonicStartTime, batteryUsageStats));
    }
}
Loading