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

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

Making PowerMonitor API public

Bug: 295027807
Test: atest CtsOsTestCases:android.os.health.cts.PowerMonitorsTest
Change-Id: I74c0c1c0bd3ac745dd1dbc760f576f9788462804
parent f2a089d3
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -33087,6 +33087,22 @@ package android.os {
    method public void onStateChanged(boolean);
  }
  @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitor implements android.os.Parcelable {
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public int describeContents();
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @NonNull public String getName();
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public int getType();
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void writeToParcel(@NonNull android.os.Parcel, int);
    field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") @NonNull public static final android.os.Parcelable.Creator<android.os.PowerMonitor> CREATOR;
    field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int POWER_MONITOR_TYPE_CONSUMER = 0; // 0x0
    field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int POWER_MONITOR_TYPE_MEASUREMENT = 1; // 0x1
  }
  @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public final class PowerMonitorReadings {
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getConsumedEnergy(@NonNull android.os.PowerMonitor);
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public long getTimestamp(@NonNull android.os.PowerMonitor);
    field @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public static final int ENERGY_UNAVAILABLE = -1; // 0xffffffff
  }
  public class Process {
    ctor public Process();
    method public static final long getElapsedCpuTime();
@@ -33649,6 +33665,8 @@ package android.os.health {
  }
  public class SystemHealthManager {
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable android.os.Handler, @NonNull java.util.function.Consumer<android.os.PowerMonitorReadings>, @NonNull java.util.function.Consumer<java.lang.RuntimeException>);
    method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable android.os.Handler, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
    method public android.os.health.HealthStats takeMyUidSnapshot();
    method public android.os.health.HealthStats takeUidSnapshot(int);
    method public android.os.health.HealthStats[] takeUidSnapshots(int[]);
+46 −15
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os;

import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;

@@ -23,12 +24,19 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * A PowerMonitor represents either a Channel aka ODPM rail (on-device power monitor) or an
 * EnergyConsumer, as defined in
 * <a href="https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats">android.hardware.power.stats</a>
 *
 * @hide
 * A PowerMonitor represents either an ODPM rail (on-device power rail monitor) or a modeled
 * energy consumer.
 * <p/>
 * ODPM rail names are device-specific. No assumptions should be made about the names and
 * exact purpose of ODPM rails across different device models. A rail name may be something
 * like "S2S_VDD_G3D"; specific knowledge of the device hardware is required to interpret
 * the corresponding power monitor data.
 * <p/>
 * Energy consumer have more human-readable names, e.g. "GPU", "MODEM" etc. However, developers
 * must be extra cautious about using energy consumers across different device models,
 * as their exact implementations are also hardware dependent and are customized by OEMs.
 */
@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public final class PowerMonitor implements Parcelable {

    /**
@@ -36,9 +44,9 @@ public final class PowerMonitor implements Parcelable {
     * power rail measurement, or modeled in some fashion.  For example, an energy consumer may
     * represent a combination of multiple rails or a portion of a rail shared between subsystems,
     * e.g. WiFi and Bluetooth are often handled by the same chip, powered by a shared rail.
     * Some consumer names are standardized (see android.hardware.power.stats.EnergyConsumerType),
     * others are not.
     * Some consumer names are standardized, others are not.
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public static final int POWER_MONITOR_TYPE_CONSUMER = 0;

    /**
@@ -46,6 +54,7 @@ public final class PowerMonitor implements Parcelable {
     * no assumptions can be made about the source of those measurements across different devices,
     * even if they have the same name.
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public static final int POWER_MONITOR_TYPE_MEASUREMENT = 1;

    /** @hide */
@@ -64,38 +73,60 @@ public final class PowerMonitor implements Parcelable {
     * @hide
     */
    public final int index;

    @PowerMonitorType
    public final int type;
    private final int mType;
    @NonNull
    public final String name;
    private final String mName;

    /**
     * @hide
     */
    public PowerMonitor(int index, int type, @NonNull String name) {
        this.index = index;
        this.type = type;
        this.name = name;
        this.mType = type;
        this.mName = name;
    }

    /**
     * Returns the type of the power monitor.
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @PowerMonitorType
    public int getType() {
        return mType;
    }

    /**
     * Returns the name of the power monitor, either a power rail or an energy consumer.
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @NonNull
    public String getName() {
        return mName;
    }

    private PowerMonitor(Parcel in) {
        index = in.readInt();
        type = in.readInt();
        name = in.readString();
        mType = in.readInt();
        mName = in.readString8();
    }

    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(index);
        dest.writeInt(type);
        dest.writeString(name);
        dest.writeInt(mType);
        dest.writeString8(mName);
    }

    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @Override
    public int describeContents() {
        return 0;
    }

    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @NonNull
    public static final Creator<PowerMonitor> CREATOR = new Creator<>() {
        @Override
+8 −5
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.os;

import android.annotation.ElapsedRealtimeLong;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;

import java.util.Arrays;
@@ -24,10 +25,10 @@ import java.util.Comparator;

/**
 * A collection of energy measurements from Power Monitors.
 *
 * @hide
 */
@FlaggedApi("com.android.server.power.optimization.power_monitor_api")
public final class PowerMonitorReadings {
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public static final int ENERGY_UNAVAILABLE = -1;

    @NonNull
@@ -41,7 +42,7 @@ public final class PowerMonitorReadings {
            Comparator.comparingInt(pm -> pm.index);

    /**
     * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index
     * @param powerMonitors array of power monitor, sorted by PowerMonitor.index
     * @hide
     */
    public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors,
@@ -56,7 +57,8 @@ public final class PowerMonitorReadings {
     * Does not persist across reboots.
     * Represents total energy: both on-battery and plugged-in.
     */
    public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) {
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public long getConsumedEnergy(@NonNull PowerMonitor powerMonitor) {
        int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
        if (offset >= 0) {
            return mEnergyUws[offset];
@@ -67,6 +69,7 @@ public final class PowerMonitorReadings {
    /**
     * Elapsed realtime, in milliseconds, when the snapshot was taken.
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    @ElapsedRealtimeLong
    public long getTimestamp(@NonNull PowerMonitor powerMonitor) {
        int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR);
@@ -84,7 +87,7 @@ public final class PowerMonitorReadings {
            if (i != 0) {
                sb.append(", ");
            }
            sb.append(mPowerMonitors[i].name)
            sb.append(mPowerMonitors[i].getName())
                    .append(" = ").append(mEnergyUws[i])
                    .append(" (").append(mTimestampsMs[i]).append(')');
        }
+8 −62
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.os.health;

import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -24,7 +25,6 @@ import android.content.Context;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.IPowerStatsService;
import android.os.PowerMonitor;
@@ -157,39 +157,15 @@ public class SystemHealthManager {
    }

    /**
     * Returns a list of supported power monitors, which include raw ODPM rails and
     * modeled energy consumers.  If ODPM is unsupported by PowerStats HAL, this method returns
     * an empty array.
     *
     * @hide
     */
    @NonNull
    public List<PowerMonitor> getSupportedPowerMonitors() {
        synchronized (mPowerMonitorsLock) {
            if (mPowerMonitorsInfo != null) {
                return mPowerMonitorsInfo;
            }
        }
        ConditionVariable lock = new ConditionVariable();
        // Populate mPowerMonitorsInfo by side-effect
        getSupportedPowerMonitors(null, unused -> lock.open());
        lock.block();

        synchronized (mPowerMonitorsLock) {
            return mPowerMonitorsInfo;
        }
    }

    /**
     * Asynchronously retrieves a list of supported power monitors, see
     * {@link #getSupportedPowerMonitors()}
     * Asynchronously retrieves a list of supported  {@link PowerMonitor}'s, which include raw ODPM
     * (on-device power rail monitor) rails and modeled energy consumers.  If ODPM is unsupported
     * on this device this method delivers an empty list.
     *
     * @param handler  optional Handler to deliver the callback. If not supplied, the callback
     *                 may be invoked on an arbitrary thread.
     * @param onResult callback for the result
     *
     * @hide
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public void getSupportedPowerMonitors(@Nullable Handler handler,
            @NonNull Consumer<List<PowerMonitor>> onResult) {
        final List<PowerMonitor> result;
@@ -229,35 +205,6 @@ public class SystemHealthManager {
        }
    }

    /**
     * Retrieves the accumulated power consumption reported by the specified power monitors.
     *
     * @param powerMonitors power monitors to be returned.
     *
     * @hide
     */
    @NonNull
    public PowerMonitorReadings getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors) {
        PowerMonitorReadings[] outReadings = new PowerMonitorReadings[1];
        RuntimeException[] outException = new RuntimeException[1];
        ConditionVariable lock = new ConditionVariable();
        getPowerMonitorReadings(powerMonitors, null,
                pms -> {
                    outReadings[0] = pms;
                    lock.open();
                },
                error -> {
                    outException[0] = error;
                    lock.open();
                }
        );
        lock.block();
        if (outException[0] != null) {
            throw outException[0];
        }
        return outReadings[0];
    }

    private static final Comparator<PowerMonitor> POWER_MONITOR_COMPARATOR =
            Comparator.comparingInt(pm -> pm.index);

@@ -270,9 +217,8 @@ public class SystemHealthManager {
     *                      may be invoked on an arbitrary thread.
     * @param onSuccess     callback for the result
     * @param onError       callback invoked in case of an error
     *
     * @hide
     */
    @FlaggedApi("com.android.server.power.optimization.power_monitor_api")
    public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors,
            @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess,
            @NonNull Consumer<RuntimeException> onError) {
+0 −126
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 android.os.health;

import static androidx.test.InstrumentationRegistry.getContext;

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

import android.os.ConditionVariable;
import android.os.PowerMonitor;
import android.os.PowerMonitorReadings;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class SystemHealthManagerTest {
    private List<PowerMonitor> mPowerMonitorInfo;
    private PowerMonitorReadings mReadings;
    private RuntimeException mException;

    @Test
    public void getPowerMonitors() {
        SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
        List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors();
        assertThat(powerMonitorInfo).isNotNull();
        if (powerMonitorInfo.isEmpty()) {
            // This device does not support PowerStats HAL
            return;
        }

        PowerMonitor consumerMonitor = null;
        PowerMonitor measurementMonitor = null;
        for (PowerMonitor pmi : powerMonitorInfo) {
            if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
                measurementMonitor = pmi;
            } else {
                consumerMonitor = pmi;
            }
        }

        List<PowerMonitor> selectedMonitors = new ArrayList<>();
        if (consumerMonitor != null) {
            selectedMonitors.add(consumerMonitor);
        }
        if (measurementMonitor != null) {
            selectedMonitors.add(measurementMonitor);
        }

        PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors);

        for (PowerMonitor monitor : selectedMonitors) {
            assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0);
            assertThat(readings.getTimestamp(monitor)).isGreaterThan(0);
        }
    }

    @Test
    public void getPowerMonitorsAsync() {
        SystemHealthManager shm = getContext().getSystemService(SystemHealthManager.class);
        ConditionVariable done = new ConditionVariable();
        shm.getSupportedPowerMonitors(null, pms -> {
            mPowerMonitorInfo = pms;
            done.open();
        });
        done.block();
        assertThat(mPowerMonitorInfo).isNotNull();
        if (mPowerMonitorInfo.isEmpty()) {
            // This device does not support PowerStats HAL
            return;
        }

        PowerMonitor consumerMonitor = null;
        PowerMonitor measurementMonitor = null;
        for (PowerMonitor pmi : mPowerMonitorInfo) {
            if (pmi.type == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) {
                measurementMonitor = pmi;
            } else {
                consumerMonitor = pmi;
            }
        }

        List<PowerMonitor> selectedMonitors = new ArrayList<>();
        if (consumerMonitor != null) {
            selectedMonitors.add(consumerMonitor);
        }
        if (measurementMonitor != null) {
            selectedMonitors.add(measurementMonitor);
        }

        done.close();
        shm.getPowerMonitorReadings(selectedMonitors, null,
                readings -> {
                    mReadings = readings;
                    done.open();
                },
                exception -> {
                    mException = exception;
                    done.open();
                }
        );
        done.block();

        assertThat(mException).isNull();

        for (PowerMonitor monitor : selectedMonitors) {
            assertThat(mReadings.getConsumedEnergyUws(monitor)).isAtLeast(0);
            assertThat(mReadings.getTimestamp(monitor)).isGreaterThan(0);
        }
    }
}
Loading