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

Commit 72f21ef4 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "Add battery history file operation trace events" into main

parents e40423d5 d42820ca
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;
    }
}