diff --git a/core/api/current.txt b/core/api/current.txt index 36cdd7fb84bb2183754e50a711cdced6a29f5934..6c0fccc871984a5d12af23790bdb0bda0de10583 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33094,6 +33094,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(); @@ -33656,6 +33672,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[]); diff --git a/core/java/android/os/PowerMonitor.java b/core/java/android/os/PowerMonitor.java index 5fb0df7febc1c0b18f68a170b5c07fce32dfb108..8c5f2cd48f61d62680dc198869696d5250049129 100644 --- a/core/java/android/os/PowerMonitor.java +++ b/core/java/android/os/PowerMonitor.java @@ -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 diff --git a/core/java/android/os/PowerMonitorReadings.java b/core/java/android/os/PowerMonitorReadings.java index e76705917c7a2b0983c7b916caa07c93a24933ca..bb677d529507e6362aad6c8baccc8a1104aaceea 100644 --- a/core/java/android/os/PowerMonitorReadings.java +++ b/core/java/android/os/PowerMonitorReadings.java @@ -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(')'); } diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index dfc43f46c57dba8ff23ddf0ee145db92f04561e4..2d53341be654f431676c682937ae32bfb6cae465 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -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. + * 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. * - * @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()} - * - * @param handler optional Handler to deliver the callback. If not supplied, the callback - * may be invoked on an arbitrary thread. + * @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) { diff --git a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java b/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java deleted file mode 100644 index e1f9523f1b49489191e4f387b7b1e77764e40f26..0000000000000000000000000000000000000000 --- a/core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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); - } - } -} diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig index d61bebc0c82c71afc9096b4b949faf16d5387695..add806febe670ad2e0760ad99913bceeb9907e1d 100644 --- a/services/core/java/com/android/server/power/stats/flags.aconfig +++ b/services/core/java/com/android/server/power/stats/flags.aconfig @@ -1,5 +1,12 @@ package: "com.android.server.power.optimization" +flag { + name: "power_monitor_api" + namespace: "power_optimization" + description: "Feature flag for ODPM API" + bug: "295027807" +} + flag { name: "streamlined_battery_stats" namespace: "power_optimization" diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 5609f6975bc47112802806836284608f560b44fa..77290fd944ebd7090bac90099e7c1fc7616d4f12 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -694,7 +694,7 @@ public class PowerStatsService extends SystemService { Log.d(TAG, String.format(Locale.ENGLISH, "Monitor=%s timestamp=%d energy=%d" + " uid=%d noise=%.1f%% returned=%d", - state.powerMonitor.name, + state.powerMonitor.getName(), state.timestampMs, state.energyUws, callingUid, @@ -728,7 +728,7 @@ public class PowerStatsService extends SystemService { } for (PowerMonitorState powerMonitorState : powerMonitorStates) { - if (powerMonitorState.powerMonitor.type + if (powerMonitorState.powerMonitor.getType() == PowerMonitor.POWER_MONITOR_TYPE_CONSUMER) { for (EnergyConsumerResult energyConsumerResult : energyConsumerResults) { if (energyConsumerResult.id == powerMonitorState.id) { @@ -754,7 +754,7 @@ public class PowerStatsService extends SystemService { } for (PowerMonitorState powerMonitorState : powerMonitorStates) { - if (powerMonitorState.powerMonitor.type + if (powerMonitorState.powerMonitor.getType() == PowerMonitor.POWER_MONITOR_TYPE_MEASUREMENT) { for (EnergyMeasurement energyMeasurement : energyMeasurements) { if (energyMeasurement.id == powerMonitorState.id) { @@ -773,7 +773,7 @@ public class PowerStatsService extends SystemService { @PowerMonitor.PowerMonitorType int type) { int count = 0; for (PowerMonitorState monitorState : powerMonitorStates) { - if (monitorState.powerMonitor.type == type) { + if (monitorState.powerMonitor.getType() == type) { count++; } } @@ -785,7 +785,7 @@ public class PowerStatsService extends SystemService { int[] ids = new int[count]; int index = 0; for (PowerMonitorState monitorState : powerMonitorStates) { - if (monitorState.powerMonitor.type == type) { + if (monitorState.powerMonitor.getType() == type) { ids[index++] = monitorState.id; } } diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 2ffe4aacda734ee782201a4dc2b8877b8cbe00d7..df46054f0f6f01db44190aa39469e610a0c09677 100644 --- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -1079,7 +1079,7 @@ public class PowerStatsServiceTest { GetSupportedPowerMonitorsResult result = new GetSupportedPowerMonitorsResult(); mService.getSupportedPowerMonitorsImpl(result); assertThat(result.powerMonitors).isNotNull(); - assertThat(Arrays.stream(result.powerMonitors).map(pm -> pm.name).toList()) + assertThat(Arrays.stream(result.powerMonitors).map(PowerMonitor::getName).toList()) .containsAtLeast( "energyconsumer0", "BLUETOOTH/1", @@ -1130,7 +1130,7 @@ public class PowerStatsServiceTest { mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult); Map<String, PowerMonitor> map = Arrays.stream(supportedPowerMonitorsResult.powerMonitors) - .collect(Collectors.toMap(pm -> pm.name, pm -> pm)); + .collect(Collectors.toMap(PowerMonitor::getName, pm -> pm)); PowerMonitor consumer1 = map.get("energyconsumer0"); PowerMonitor consumer2 = map.get("BLUETOOTH/1"); PowerMonitor measurement1 = map.get("[channelname0]:channelsubsystem0"); @@ -1196,6 +1196,6 @@ public class PowerStatsServiceTest { supportedPowerMonitorsResult = new GetSupportedPowerMonitorsResult(); mService.getSupportedPowerMonitorsImpl(supportedPowerMonitorsResult); assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors) - .map(pm -> pm.name).toList()).contains("energyconsumer0"); + .map(PowerMonitor::getName).toList()).contains("energyconsumer0"); } }