Loading core/java/android/os/PowerMonitor.java +5 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ 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 */ public final class PowerMonitor implements Parcelable { Loading Loading @@ -92,6 +96,7 @@ public final class PowerMonitor implements Parcelable { return 0; } @NonNull public static final Creator<PowerMonitor> CREATOR = new Creator<>() { @Override public PowerMonitor createFromParcel(@NonNull Parcel in) { Loading core/java/android/os/PowerMonitorReadings.java +7 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import java.util.Arrays; Loading Loading @@ -43,8 +44,8 @@ public final class PowerMonitorReadings { * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index * @hide */ public PowerMonitorReadings(PowerMonitor[] powerMonitors, long[] energyUws, long[] timestampsMs) { public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, @NonNull long[] energyUws, @NonNull long[] timestampsMs) { mPowerMonitors = powerMonitors; mEnergyUws = energyUws; mTimestampsMs = timestampsMs; Loading @@ -55,7 +56,7 @@ public final class PowerMonitorReadings { * Does not persist across reboots. * Represents total energy: both on-battery and plugged-in. */ public long getConsumedEnergyUws(PowerMonitor powerMonitor) { public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mEnergyUws[offset]; Loading @@ -64,9 +65,10 @@ public final class PowerMonitorReadings { } /** * Elapsed realtime when the snapshot was taken. * Elapsed realtime, in milliseconds, when the snapshot was taken. */ public long getTimestampMs(PowerMonitor powerMonitor) { @ElapsedRealtimeLong public long getTimestamp(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mTimestampsMs[offset]; Loading core/java/android/os/health/SystemHealthManager.java +96 −58 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ 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; import android.os.PowerMonitorReadings; Loading @@ -36,8 +38,8 @@ import com.android.internal.app.IBatteryStats; import java.util.Arrays; import java.util.Comparator; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.List; import java.util.function.Consumer; /** * Provides access to data about how various system resources are used by applications. Loading @@ -62,7 +64,8 @@ public class SystemHealthManager { private final IBatteryStats mBatteryStats; @Nullable private final IPowerStatsService mPowerStats; private PowerMonitor[] mPowerMonitorsInfo; private List<PowerMonitor> mPowerMonitorsInfo; private final Object mPowerMonitorsLock = new Object(); /** * Construct a new SystemHealthManager object. Loading Loading @@ -161,107 +164,142 @@ public class SystemHealthManager { * @hide */ @NonNull public PowerMonitor[] getSupportedPowerMonitors() { synchronized (this) { public List<PowerMonitor> getSupportedPowerMonitors() { synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { return mPowerMonitorsInfo; } CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>(); getSupportedPowerMonitors(future); try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } ConditionVariable lock = new ConditionVariable(); // Populate mPowerMonitorsInfo by side-effect getSupportedPowerMonitors(null, unused -> lock.open()); lock.block(); synchronized (mPowerMonitorsLock) { return mPowerMonitorsInfo; } } /** * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()} * 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 onResult callback for the result * * @hide */ public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) { synchronized (this) { public void getSupportedPowerMonitors(@Nullable Handler handler, @NonNull Consumer<List<PowerMonitor>> onResult) { final List<PowerMonitor> result; synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { future.complete(mPowerMonitorsInfo); return; result = mPowerMonitorsInfo; } else if (mPowerStats == null) { mPowerMonitorsInfo = List.of(); result = mPowerMonitorsInfo; } else { result = null; } } if (result != null) { if (handler != null) { handler.post(() -> onResult.accept(result)); } else { onResult.accept(result); } try { if (mPowerStats == null) { mPowerMonitorsInfo = new PowerMonitor[0]; future.complete(mPowerMonitorsInfo); return; } mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { try { mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { synchronized (this) { mPowerMonitorsInfo = resultData.getParcelableArray( PowerMonitor[] array = resultData.getParcelableArray( IPowerStatsService.KEY_MONITORS, PowerMonitor.class); List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of(); synchronized (mPowerMonitorsLock) { mPowerMonitorsInfo = result; } future.complete(mPowerMonitorsInfo); onResult.accept(result); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Retrieves the accumulated power consumption reported by the specified power monitors. * * @param powerMonitors power monitors to be returned. * * @hide */ @NonNull public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) { CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>(); getPowerMonitorReadings(powerMonitors, future); try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); 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); /** * Asynchronously retrieves the accumulated power consumption reported by the specified power * monitors. * * @param powerMonitors power monitors to be retrieved. * @param handler optional Handler to deliver the callbacks. If not supplied, the callback * may be invoked on an arbitrary thread. * @param onSuccess callback for the result * @param onError callback invoked in case of an error * * @hide */ public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, @NonNull CompletableFuture<PowerMonitorReadings> future) { public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess, @NonNull Consumer<RuntimeException> onError) { if (mPowerStats == null) { future.completeExceptionally( new IllegalArgumentException("Unsupported power monitor")); onError.accept(new IllegalArgumentException("Unsupported power monitor")); return; } Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR); int[] indices = new int[powerMonitors.length]; for (int i = 0; i < powerMonitors.length; i++) { indices[i] = powerMonitors[i].index; PowerMonitor[] powerMonitorsArray = powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]); Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR); int[] indices = new int[powerMonitors.size()]; for (int i = 0; i < powerMonitors.size(); i++) { indices[i] = powerMonitorsArray[i].index; } try { mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == IPowerStatsService.RESULT_SUCCESS) { future.complete(new PowerMonitorReadings(powerMonitors, onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray, resultData.getLongArray(IPowerStatsService.KEY_ENERGY), resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS))); } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { future.completeExceptionally( new IllegalArgumentException("Unsupported power monitor")); onError.accept(new IllegalArgumentException("Unsupported power monitor")); } else { future.completeExceptionally( new IllegalStateException( onError.accept(new IllegalStateException( "Unrecognized result code " + resultCode)); } } Loading core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java +64 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ 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; Loading @@ -29,13 +30,16 @@ 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); PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors(); List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors(); assertThat(powerMonitorInfo).isNotNull(); if (powerMonitorInfo.length == 0) { if (powerMonitorInfo.isEmpty()) { // This device does not support PowerStats HAL return; } Loading @@ -50,20 +54,73 @@ public class SystemHealthManagerTest { } } List<PowerMonitor> pmis = new ArrayList<>(); List<PowerMonitor> selectedMonitors = new ArrayList<>(); if (consumerMonitor != null) { pmis.add(consumerMonitor); selectedMonitors.add(consumerMonitor); } if (measurementMonitor != null) { pmis.add(measurementMonitor); selectedMonitors.add(measurementMonitor); } PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]); PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors); for (PowerMonitor monitor : selectedMonitors) { assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0); assertThat(readings.getTimestampMs(monitor)).isGreaterThan(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
core/java/android/os/PowerMonitor.java +5 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,10 @@ 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 */ public final class PowerMonitor implements Parcelable { Loading Loading @@ -92,6 +96,7 @@ public final class PowerMonitor implements Parcelable { return 0; } @NonNull public static final Creator<PowerMonitor> CREATOR = new Creator<>() { @Override public PowerMonitor createFromParcel(@NonNull Parcel in) { Loading
core/java/android/os/PowerMonitorReadings.java +7 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.os; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import java.util.Arrays; Loading Loading @@ -43,8 +44,8 @@ public final class PowerMonitorReadings { * @param powerMonitors array of power monitor (ODPM) rails, sorted by PowerMonitor.index * @hide */ public PowerMonitorReadings(PowerMonitor[] powerMonitors, long[] energyUws, long[] timestampsMs) { public PowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, @NonNull long[] energyUws, @NonNull long[] timestampsMs) { mPowerMonitors = powerMonitors; mEnergyUws = energyUws; mTimestampsMs = timestampsMs; Loading @@ -55,7 +56,7 @@ public final class PowerMonitorReadings { * Does not persist across reboots. * Represents total energy: both on-battery and plugged-in. */ public long getConsumedEnergyUws(PowerMonitor powerMonitor) { public long getConsumedEnergyUws(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mEnergyUws[offset]; Loading @@ -64,9 +65,10 @@ public final class PowerMonitorReadings { } /** * Elapsed realtime when the snapshot was taken. * Elapsed realtime, in milliseconds, when the snapshot was taken. */ public long getTimestampMs(PowerMonitor powerMonitor) { @ElapsedRealtimeLong public long getTimestamp(@NonNull PowerMonitor powerMonitor) { int offset = Arrays.binarySearch(mPowerMonitors, powerMonitor, POWER_MONITOR_COMPARATOR); if (offset >= 0) { return mTimestampsMs[offset]; Loading
core/java/android/os/health/SystemHealthManager.java +96 −58 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ 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; import android.os.PowerMonitorReadings; Loading @@ -36,8 +38,8 @@ import com.android.internal.app.IBatteryStats; import java.util.Arrays; import java.util.Comparator; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.List; import java.util.function.Consumer; /** * Provides access to data about how various system resources are used by applications. Loading @@ -62,7 +64,8 @@ public class SystemHealthManager { private final IBatteryStats mBatteryStats; @Nullable private final IPowerStatsService mPowerStats; private PowerMonitor[] mPowerMonitorsInfo; private List<PowerMonitor> mPowerMonitorsInfo; private final Object mPowerMonitorsLock = new Object(); /** * Construct a new SystemHealthManager object. Loading Loading @@ -161,107 +164,142 @@ public class SystemHealthManager { * @hide */ @NonNull public PowerMonitor[] getSupportedPowerMonitors() { synchronized (this) { public List<PowerMonitor> getSupportedPowerMonitors() { synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { return mPowerMonitorsInfo; } CompletableFuture<PowerMonitor[]> future = new CompletableFuture<>(); getSupportedPowerMonitors(future); try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } ConditionVariable lock = new ConditionVariable(); // Populate mPowerMonitorsInfo by side-effect getSupportedPowerMonitors(null, unused -> lock.open()); lock.block(); synchronized (mPowerMonitorsLock) { return mPowerMonitorsInfo; } } /** * Retrieves a list of supported power monitors, see {@link #getSupportedPowerMonitors()} * 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 onResult callback for the result * * @hide */ public void getSupportedPowerMonitors(@NonNull CompletableFuture<PowerMonitor[]> future) { synchronized (this) { public void getSupportedPowerMonitors(@Nullable Handler handler, @NonNull Consumer<List<PowerMonitor>> onResult) { final List<PowerMonitor> result; synchronized (mPowerMonitorsLock) { if (mPowerMonitorsInfo != null) { future.complete(mPowerMonitorsInfo); return; result = mPowerMonitorsInfo; } else if (mPowerStats == null) { mPowerMonitorsInfo = List.of(); result = mPowerMonitorsInfo; } else { result = null; } } if (result != null) { if (handler != null) { handler.post(() -> onResult.accept(result)); } else { onResult.accept(result); } try { if (mPowerStats == null) { mPowerMonitorsInfo = new PowerMonitor[0]; future.complete(mPowerMonitorsInfo); return; } mPowerStats.getSupportedPowerMonitors(new ResultReceiver(null) { try { mPowerStats.getSupportedPowerMonitors(new ResultReceiver(handler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { synchronized (this) { mPowerMonitorsInfo = resultData.getParcelableArray( PowerMonitor[] array = resultData.getParcelableArray( IPowerStatsService.KEY_MONITORS, PowerMonitor.class); List<PowerMonitor> result = array != null ? Arrays.asList(array) : List.of(); synchronized (mPowerMonitorsLock) { mPowerMonitorsInfo = result; } future.complete(mPowerMonitorsInfo); onResult.accept(result); } }); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } } /** * Retrieves the accumulated power consumption reported by the specified power monitors. * * @param powerMonitors power monitors to be returned. * * @hide */ @NonNull public PowerMonitorReadings getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors) { CompletableFuture<PowerMonitorReadings> future = new CompletableFuture<>(); getPowerMonitorReadings(powerMonitors, future); try { return future.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); 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); /** * Asynchronously retrieves the accumulated power consumption reported by the specified power * monitors. * * @param powerMonitors power monitors to be retrieved. * @param handler optional Handler to deliver the callbacks. If not supplied, the callback * may be invoked on an arbitrary thread. * @param onSuccess callback for the result * @param onError callback invoked in case of an error * * @hide */ public void getPowerMonitorReadings(@NonNull PowerMonitor[] powerMonitors, @NonNull CompletableFuture<PowerMonitorReadings> future) { public void getPowerMonitorReadings(@NonNull List<PowerMonitor> powerMonitors, @Nullable Handler handler, @NonNull Consumer<PowerMonitorReadings> onSuccess, @NonNull Consumer<RuntimeException> onError) { if (mPowerStats == null) { future.completeExceptionally( new IllegalArgumentException("Unsupported power monitor")); onError.accept(new IllegalArgumentException("Unsupported power monitor")); return; } Arrays.sort(powerMonitors, POWER_MONITOR_COMPARATOR); int[] indices = new int[powerMonitors.length]; for (int i = 0; i < powerMonitors.length; i++) { indices[i] = powerMonitors[i].index; PowerMonitor[] powerMonitorsArray = powerMonitors.toArray(new PowerMonitor[powerMonitors.size()]); Arrays.sort(powerMonitorsArray, POWER_MONITOR_COMPARATOR); int[] indices = new int[powerMonitors.size()]; for (int i = 0; i < powerMonitors.size(); i++) { indices[i] = powerMonitorsArray[i].index; } try { mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(null) { mPowerStats.getPowerMonitorReadings(indices, new ResultReceiver(handler) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == IPowerStatsService.RESULT_SUCCESS) { future.complete(new PowerMonitorReadings(powerMonitors, onSuccess.accept(new PowerMonitorReadings(powerMonitorsArray, resultData.getLongArray(IPowerStatsService.KEY_ENERGY), resultData.getLongArray(IPowerStatsService.KEY_TIMESTAMPS))); } else if (resultCode == IPowerStatsService.RESULT_UNSUPPORTED_POWER_MONITOR) { future.completeExceptionally( new IllegalArgumentException("Unsupported power monitor")); onError.accept(new IllegalArgumentException("Unsupported power monitor")); } else { future.completeExceptionally( new IllegalStateException( onError.accept(new IllegalStateException( "Unrecognized result code " + resultCode)); } } Loading
core/tests/coretests/src/android/os/health/SystemHealthManagerTest.java +64 −7 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ 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; Loading @@ -29,13 +30,16 @@ 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); PowerMonitor[] powerMonitorInfo = shm.getSupportedPowerMonitors(); List<PowerMonitor> powerMonitorInfo = shm.getSupportedPowerMonitors(); assertThat(powerMonitorInfo).isNotNull(); if (powerMonitorInfo.length == 0) { if (powerMonitorInfo.isEmpty()) { // This device does not support PowerStats HAL return; } Loading @@ -50,20 +54,73 @@ public class SystemHealthManagerTest { } } List<PowerMonitor> pmis = new ArrayList<>(); List<PowerMonitor> selectedMonitors = new ArrayList<>(); if (consumerMonitor != null) { pmis.add(consumerMonitor); selectedMonitors.add(consumerMonitor); } if (measurementMonitor != null) { pmis.add(measurementMonitor); selectedMonitors.add(measurementMonitor); } PowerMonitor[] selectedMonitors = pmis.toArray(new PowerMonitor[0]); PowerMonitorReadings readings = shm.getPowerMonitorReadings(selectedMonitors); for (PowerMonitor monitor : selectedMonitors) { assertThat(readings.getConsumedEnergyUws(monitor)).isAtLeast(0); assertThat(readings.getTimestampMs(monitor)).isGreaterThan(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); } } }