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

Commit d42820ca authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Add battery history file operation trace events

Bug: 381938229
Test: PowerStatsTests#BatteryHistoryTraceTest
Flag: EXEMPT bugfix
Change-Id: I6d0aabcef2ff8a5685c247e836bcfbf9ebcef873
parent 8e6240de
Loading
Loading
Loading
Loading
+81 −50
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.os;
import static android.os.BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
import static android.os.BatteryStats.HistoryItem.EVENT_FLAG_START;
import static android.os.BatteryStats.HistoryItem.EVENT_STATE_CHANGE;
import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER;

import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -215,6 +216,7 @@ public class BatteryStatsHistory {
    private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
    private byte mLastHistoryStepLevel = 0;
    private boolean mMutable = true;
    private int mIteratorCookie;
    private final BatteryStatsHistory mWritableHistory;

    private static class BatteryHistoryFile implements Comparable<BatteryHistoryFile> {
@@ -289,6 +291,7 @@ public class BatteryStatsHistory {
        }

        void load() {
            Trace.asyncTraceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.load", 0);
            mDirectory.mkdirs();
            if (!mDirectory.exists()) {
                Slog.wtf(TAG, "HistoryDir does not exist:" + mDirectory.getPath());
@@ -325,8 +328,11 @@ public class BatteryStatsHistory {
                        }
                    } finally {
                        unlock();
                        Trace.asyncTraceEnd(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.load", 0);
                    }
                });
            } else {
                Trace.asyncTraceEnd(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.load", 0);
            }
        }

@@ -418,6 +424,7 @@ public class BatteryStatsHistory {
        }

        void writeToParcel(Parcel out, boolean useBlobs) {
            Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.writeToParcel");
            lock();
            try {
                final long start = SystemClock.uptimeMillis();
@@ -443,6 +450,7 @@ public class BatteryStatsHistory {
                }
            } finally {
                unlock();
                Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
            }
        }

@@ -482,6 +490,8 @@ public class BatteryStatsHistory {
        }

        private void cleanup() {
            Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.cleanup");
            try {
                if (mDirectory == null) {
                    return;
                }
@@ -511,6 +521,9 @@ public class BatteryStatsHistory {
                } finally {
                    unlock();
                }
            } finally {
                Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
            }
        }
    }

@@ -710,13 +723,18 @@ public class BatteryStatsHistory {
     * in the system directory, so it is not safe while actively writing history.
     */
    public BatteryStatsHistory copy() {
        Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.copy");
        try {
            synchronized (this) {
                // Make a copy of battery history to avoid concurrent modification.
                Parcel historyBufferCopy = Parcel.obtain();
                historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());

            return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null, null,
                    null, mEventLogger, this);
                return new BatteryStatsHistory(historyBufferCopy, mSystemDir, 0, 0, null, null,
                        null, null, mEventLogger, this);
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
        }
    }

@@ -826,7 +844,7 @@ public class BatteryStatsHistory {
     */
    @NonNull
    public BatteryStatsHistoryIterator iterate(long startTimeMs, long endTimeMs) {
        if (mMutable) {
        if (mMutable || mIteratorCookie != 0) {
            return copy().iterate(startTimeMs, endTimeMs);
        }

@@ -837,7 +855,12 @@ public class BatteryStatsHistory {
        mCurrentParcel = null;
        mCurrentParcelEnd = 0;
        mParcelIndex = 0;
        return new BatteryStatsHistoryIterator(this, startTimeMs, endTimeMs);
        BatteryStatsHistoryIterator iterator = new BatteryStatsHistoryIterator(
                this, startTimeMs, endTimeMs);
        mIteratorCookie = System.identityHashCode(iterator);
        Trace.asyncTraceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.iterate",
                mIteratorCookie);
        return iterator;
    }

    /**
@@ -848,6 +871,9 @@ public class BatteryStatsHistory {
        if (mHistoryDir != null) {
            mHistoryDir.unlock();
        }
        Trace.asyncTraceEnd(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.iterate",
                mIteratorCookie);
        mIteratorCookie = 0;
    }

    /**
@@ -949,6 +975,8 @@ public class BatteryStatsHistory {
     * @return true if success, false otherwise.
     */
    public boolean readFileToParcel(Parcel out, AtomicFile file) {
        Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.read");
        try {
            byte[] raw = null;
            try {
                final long start = SystemClock.uptimeMillis();
@@ -971,6 +999,9 @@ public class BatteryStatsHistory {
            // skip monotonic size field
            out.readLong();
            return true;
        } finally {
            Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
        }
    }

    /**
+3 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ android_test {
        "servicestests-utils",
        "platform-test-annotations",
        "flag-junit",
        "apct-perftests-utils",
    ],

    libs: [
@@ -64,10 +65,12 @@ android_ravenwood_test {
        "ravenwood-junit",
        "truth",
        "androidx.annotation_annotation",
        "androidx.test.ext.junit",
        "androidx.test.rules",
        "androidx.test.uiautomator_uiautomator",
        "modules-utils-binary-xml",
        "flag-junit",
        "apct-perftests-utils",
    ],
    srcs: [
        "src/com/android/server/power/stats/*.java",
+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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 static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.google.common.truth.Truth.assertThat;

import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.ParcelFileDescriptor;
import android.perftests.utils.TraceMarkParser;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.uiautomator.UiDevice;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;

@RunWith(AndroidJUnit4.class)
@LargeTest
@android.platform.test.annotations.DisabledOnRavenwood(reason = "Atrace event test")
public class BatteryStatsHistoryTraceTest {
    private static final String ATRACE_START = "atrace --async_start -b 1024 -c ss";
    private static final String ATRACE_STOP = "atrace --async_stop";
    private static final String ATRACE_DUMP = "atrace --async_dump";

    @Before
    public void before() throws Exception {
        runShellCommand(ATRACE_START);
    }

    @After
    public void after() throws Exception {
        runShellCommand(ATRACE_STOP);
    }

    @Test
    public void dumpsys() throws Exception {
        runShellCommand("dumpsys batterystats --history");

        Set<String> slices = readAtraceSlices();
        assertThat(slices).contains("BatteryStatsHistory.copy");
        assertThat(slices).contains("BatteryStatsHistory.iterate");
    }

    @Test
    public void getBatteryUsageStats() throws Exception {
        BatteryStatsManager batteryStatsManager =
                getInstrumentation().getTargetContext().getSystemService(BatteryStatsManager.class);
        BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
                .includeBatteryHistory().build();
        BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(query);
        assertThat(batteryUsageStats).isNotNull();

        Set<String> slices = readAtraceSlices();
        assertThat(slices).contains("BatteryStatsHistory.copy");
        assertThat(slices).contains("BatteryStatsHistory.iterate");
        assertThat(slices).contains("BatteryStatsHistory.writeToParcel");
    }

    private String runShellCommand(String cmd) throws Exception {
        return UiDevice.getInstance(getInstrumentation()).executeShellCommand(cmd);
    }

    private Set<String> readAtraceSlices() throws Exception {
        Set<String> keys = new HashSet<>();

        TraceMarkParser parser = new TraceMarkParser(
                line -> line.name.startsWith("BatteryStatsHistory."));
        ParcelFileDescriptor pfd =
                getInstrumentation().getUiAutomation().executeShellCommand(ATRACE_DUMP);
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd)))) {
            String line;
            while ((line = reader.readLine()) != null) {
                parser.visit(line);
            }
        }
        parser.forAllSlices((key, slices) -> keys.add(key));
        return keys;
    }
}