diff options
author | 2024-12-17 13:07:25 -0800 | |
---|---|---|
committer | 2025-01-03 21:57:07 -0800 | |
commit | 9801ae6160b104c8fe6a95b91ea3f7250697ff98 (patch) | |
tree | d887770e4b4ff1e69666db3ec6f6d8c821556e4e | |
parent | 98ab6d9dc032ea719ef23337f2f276d04beb32ea (diff) |
Address API review feedback
* Sync the headroom explanation between NDK and SDK
* Make the calculation window min max values public
* Change params to be immutable and add builder
* Add comment on how to use the APIs and the binder call warning
* Add new API getMaxCpuHeadroomTidsSize to query max TID count
Bug: 384056153
Bug: 346604998
Flag: android.os.cpu_gpu_headrooms
Test: atest HintManagerServiceTest
Change-Id: I8aff28a0b281e2948462924875017f9273440022
-rw-r--r-- | core/api/current.txt | 35 | ||||
-rw-r--r-- | core/java/android/os/CpuHeadroomParams.java | 225 | ||||
-rw-r--r-- | core/java/android/os/GpuHeadroomParams.java | 172 | ||||
-rw-r--r-- | core/java/android/os/IHintManager.aidl | 3 | ||||
-rw-r--r-- | core/java/android/os/health/SystemHealthManager.java | 194 | ||||
-rw-r--r-- | native/android/libandroid.map.txt | 3 | ||||
-rw-r--r-- | native/android/system_health.cpp | 137 | ||||
-rw-r--r-- | native/android/tests/performance_hint/PerformanceHintNativeTest.cpp | 3 | ||||
-rw-r--r-- | services/core/java/com/android/server/power/hint/HintManagerService.java | 104 |
9 files changed, 668 insertions, 208 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 7c56a5811abb..6957798d3c8f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33623,16 +33623,23 @@ package android.os { } @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams { - ctor public CpuHeadroomParams(); method public int getCalculationType(); - method @IntRange(from=0x32, to=0x2710) public long getCalculationWindowMillis(); - method public void setCalculationType(int); - method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int); - method public void setTids(@NonNull int...); + method public long getCalculationWindowMillis(); + method @NonNull public int[] getTids(); + method @NonNull public android.os.CpuHeadroomParams.Builder toBuilder(); field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 } + public static final class CpuHeadroomParams.Builder { + ctor public CpuHeadroomParams.Builder(); + ctor public CpuHeadroomParams.Builder(@NonNull android.os.CpuHeadroomParams); + method @NonNull public android.os.CpuHeadroomParams build(); + method @NonNull public android.os.CpuHeadroomParams.Builder setCalculationType(int); + method @NonNull public android.os.CpuHeadroomParams.Builder setCalculationWindowMillis(@IntRange(from=1) int); + method @NonNull public android.os.CpuHeadroomParams.Builder setTids(@NonNull int...); + } + public final class CpuUsageInfo implements android.os.Parcelable { method public int describeContents(); method public long getActive(); @@ -33881,13 +33888,20 @@ package android.os { } @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams { - ctor public GpuHeadroomParams(); method public int getCalculationType(); - method @IntRange(from=0x32, to=0x2710) public int getCalculationWindowMillis(); - method public void setCalculationType(int); - method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int); + method public int getCalculationWindowMillis(); field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1 field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710 + field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32 + } + + public static final class GpuHeadroomParams.Builder { + ctor public GpuHeadroomParams.Builder(); + ctor public GpuHeadroomParams.Builder(@NonNull android.os.GpuHeadroomParams); + method @NonNull public android.os.GpuHeadroomParams build(); + method @NonNull public android.os.GpuHeadroomParams.Builder setCalculationType(int); + method @NonNull public android.os.GpuHeadroomParams.Builder setCalculationWindowMillis(@IntRange(from=1) int); } public class Handler { @@ -35153,9 +35167,12 @@ package android.os.health { public class SystemHealthManager { method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getCpuHeadroom(@Nullable android.os.CpuHeadroomParams); + method @FlaggedApi("android.os.cpu_gpu_headrooms") @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getCpuHeadroomCalculationWindowRange(); method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getCpuHeadroomMinIntervalMillis(); method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getGpuHeadroom(@Nullable android.os.GpuHeadroomParams); + method @FlaggedApi("android.os.cpu_gpu_headrooms") @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getGpuHeadroomCalculationWindowRange(); method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getGpuHeadroomMinIntervalMillis(); + method @FlaggedApi("android.os.cpu_gpu_headrooms") @IntRange(from=1) public int getMaxCpuHeadroomTidsSize(); method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>); method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>); method public android.os.health.HealthStats takeMyUidSnapshot(); diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java index 072c012d6db9..e51197607520 100644 --- a/core/java/android/os/CpuHeadroomParams.java +++ b/core/java/android/os/CpuHeadroomParams.java @@ -28,15 +28,11 @@ import java.util.Arrays; /** * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}. + * + * <p>This class is immutable and one should use the {@link Builder} to build a new instance. */ @FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS) public final class CpuHeadroomParams { - final CpuHeadroomParamsInternal mInternal; - - public CpuHeadroomParams() { - mInternal = new CpuHeadroomParamsInternal(); - } - /** @hide */ @IntDef(flag = false, prefix = {"CPU_HEADROOM_CALCULATION_TYPE_"}, value = { CPU_HEADROOM_CALCULATION_TYPE_MIN, // 0 @@ -47,107 +43,188 @@ public final class CpuHeadroomParams { } /** - * Calculates the headroom based on minimum value over a device-defined window. + * The headroom calculation type bases on minimum value over a specified window. */ public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; /** - * Calculates the headroom based on average value over a device-defined window. + * The headroom calculation type bases on average value over a specified window. */ public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; - private static final int CALCULATION_WINDOW_MILLIS_MIN = 50; - private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000; - private static final int MAX_TID_COUNT = 5; + /** @hide */ + public final CpuHeadroomParamsInternal mInternal; + + private CpuHeadroomParams() { + mInternal = new CpuHeadroomParamsInternal(); + } + + public static final class Builder { + private int mCalculationType = -1; + private int mCalculationWindowMillis = -1; + private int[] mTids = null; + + public Builder() { + } + + /** + * Returns a new builder copy with the same values as the params. + */ + public Builder(@NonNull CpuHeadroomParams params) { + if (params.mInternal.calculationType >= 0) { + mCalculationType = params.mInternal.calculationType; + } + if (params.mInternal.calculationWindowMillis >= 0) { + mCalculationWindowMillis = params.mInternal.calculationWindowMillis; + } + if (params.mInternal.tids != null) { + mTids = Arrays.copyOf(params.mInternal.tids, params.mInternal.tids.length); + } + } + + /** + * Sets the headroom calculation type. + * <p> + * + * @throws IllegalArgumentException if the type is invalid. + */ + @NonNull + public Builder setCalculationType( + @CpuHeadroomCalculationType int calculationType) { + switch (calculationType) { + case CPU_HEADROOM_CALCULATION_TYPE_MIN: + case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE: { + mCalculationType = calculationType; + return this; + } + } + throw new IllegalArgumentException("Invalid calculation type: " + calculationType); + } + + /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds ranges from + * {@link SystemHealthManager#getCpuHeadroomCalculationWindowRange()}. + * The smaller the window size, the larger fluctuation in the headroom + * value should be expected. The default value can be retrieved from + * the {@link CpuHeadroomParams#getCalculationWindowMillis}. The device + * will try to use the closest feasible window size to this param. + * @throws IllegalArgumentException if the window is invalid. + */ + @NonNull + public Builder setCalculationWindowMillis(@IntRange(from = 1) int windowMillis) { + if (windowMillis <= 0) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mCalculationWindowMillis = windowMillis; + return this; + } + + /** + * Sets the thread TIDs to track. + * <p> + * The TIDs should belong to the same of the process that will make the headroom call. And + * they should not have different core affinity. + * <p> + * If not set or set to empty, the headroom will be based on the PID of the process making + * the call. + * + * @param tids non-null list of TIDs, where maximum size can be read from + * {@link SystemHealthManager#getMaxCpuHeadroomTidsSize()}. + * @throws IllegalArgumentException if the TID is not positive. + */ + @NonNull + public Builder setTids(@NonNull int... tids) { + for (int tid : tids) { + if (tid <= 0) { + throw new IllegalArgumentException("Invalid TID: " + tid); + } + } + mTids = tids; + return this; + } + + /** + * Builds the {@link CpuHeadroomParams} object. + */ + @NonNull + public CpuHeadroomParams build() { + CpuHeadroomParams params = new CpuHeadroomParams(); + if (mCalculationType >= 0) { + params.mInternal.calculationType = (byte) mCalculationType; + } + if (mCalculationWindowMillis >= 0) { + params.mInternal.calculationWindowMillis = mCalculationWindowMillis; + } + if (mTids != null) { + params.mInternal.tids = mTids; + } + return params; + } + } /** - * Sets the headroom calculation type. - * <p> - * - * @throws IllegalArgumentException if the type is invalid. + * Returns a new builder with the same values as this object. */ - public void setCalculationType(@CpuHeadroomCalculationType int calculationType) { - switch (calculationType) { - case CPU_HEADROOM_CALCULATION_TYPE_MIN: - case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE: - mInternal.calculationType = (byte) calculationType; - return; - } - throw new IllegalArgumentException("Invalid calculation type: " + calculationType); + @NonNull + public Builder toBuilder() { + return new Builder(this); } /** * Gets the headroom calculation type. - * Default to {@link #CPU_HEADROOM_CALCULATION_TYPE_MIN} if not set. + * <p> + * This will return the default value chosen by the device if not set. */ public @CpuHeadroomCalculationType int getCalculationType() { @CpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) { - case CPU_HEADROOM_CALCULATION_TYPE_MIN, CPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> - mInternal.calculationType; + case CPU_HEADROOM_CALCULATION_TYPE_MIN, + CPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> mInternal.calculationType; default -> CPU_HEADROOM_CALCULATION_TYPE_MIN; }; return validatedType; } /** - * Sets the headroom calculation window size in milliseconds. - * <p> - * - * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the - * window size, the larger fluctuation in the headroom value should be - * expected. The default value can be retrieved from the - * {@link #getCalculationWindowMillis}. The device will try to use the - * closest feasible window size to this param. - * @throws IllegalArgumentException if the window size is not in allowed range. - */ - public void setCalculationWindowMillis( - @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to = - CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { - if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN - || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) { - throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); - } - mInternal.calculationWindowMillis = windowMillis; - } - - /** * Gets the headroom calculation window size in milliseconds. * <p> - * This will return the default value chosen by the device if the params is not set. + * This will return the default value chosen by the device if not set. */ - public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to = - CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() { + public long getCalculationWindowMillis() { return mInternal.calculationWindowMillis; } /** - * Sets the thread TIDs to track. - * <p> - * The TIDs should belong to the same of the process that will the headroom call. And they - * should not have different core affinity. + * Gets the TIDs to track. * <p> - * If not set, the headroom will be based on the PID of the process making the call. - * - * @param tids non-empty list of TIDs, maximum 5. - * @throws IllegalArgumentException if the list size is not in allowed range or TID is not - * positive. + * This will return a copy of the TIDs in the params, or null if the params is not set. */ - public void setTids(@NonNull int... tids) { - if (tids.length == 0 || tids.length > MAX_TID_COUNT) { - throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length); - } - for (int tid : tids) { - if (tid <= 0) { - throw new IllegalArgumentException("Invalid TID: " + tid); - } - } - mInternal.tids = Arrays.copyOf(tids, tids.length); + @NonNull + public int[] getTids() { + return mInternal.tids == null ? null : Arrays.copyOf(mInternal.tids, mInternal.tids.length); } - /** - * @hide - */ - public CpuHeadroomParamsInternal getInternal() { - return mInternal; + @Override + public String toString() { + return "CpuHeadroomParams{" + + "calculationType=" + mInternal.calculationType + + ", calculationWindowMillis=" + mInternal.calculationWindowMillis + + ", tids=" + Arrays.toString(mInternal.tids) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CpuHeadroomParams that = (CpuHeadroomParams) o; + return mInternal.equals(that.mInternal); + } + + @Override + public int hashCode() { + return mInternal.hashCode(); } } diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java index 126ee8cce3d0..5c5e7bb5ccaf 100644 --- a/core/java/android/os/GpuHeadroomParams.java +++ b/core/java/android/os/GpuHeadroomParams.java @@ -19,6 +19,7 @@ package android.os; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.NonNull; import android.os.health.SystemHealthManager; import java.lang.annotation.Retention; @@ -26,15 +27,11 @@ import java.lang.annotation.RetentionPolicy; /** * Headroom request params used by {@link SystemHealthManager#getGpuHeadroom(GpuHeadroomParams)}. + * + * <p>This class is immutable and one should use the {@link Builder} to build a new instance. */ @FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS) public final class GpuHeadroomParams { - final GpuHeadroomParamsInternal mInternal; - - public GpuHeadroomParams() { - mInternal = new GpuHeadroomParamsInternal(); - } - /** @hide */ @IntDef(flag = false, prefix = {"GPU_HEADROOM_CALCULATION_TYPE_"}, value = { GPU_HEADROOM_CALCULATION_TYPE_MIN, // 0 @@ -45,82 +42,153 @@ public final class GpuHeadroomParams { } /** - * Calculates the headroom based on minimum value over a device-defined window. + * The headroom calculation type bases on minimum value over a specified window. */ public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; /** - * Calculates the headroom based on average value over a device-defined window. + * The headroom calculation type bases on average value over a specified window. */ public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; - private static final int CALCULATION_WINDOW_MILLIS_MIN = 50; - private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000; + /** + * The minimum size of the window to compute the headroom over. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; /** - * Sets the headroom calculation type. - * <p> - * - * @throws IllegalArgumentException if the type is invalid. + * The maximum size of the window to compute the headroom over. + */ + public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; + + /** + * @hide + */ + public final GpuHeadroomParamsInternal mInternal; + + /** + * @hide */ - public void setCalculationType(@GpuHeadroomCalculationType int calculationType) { - switch (calculationType) { - case GPU_HEADROOM_CALCULATION_TYPE_MIN: - case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE: - mInternal.calculationType = (byte) calculationType; - return; + private GpuHeadroomParams() { + mInternal = new GpuHeadroomParamsInternal(); + } + + public static final class Builder { + private int mCalculationType = -1; + private int mCalculationWindowMillis = -1; + + public Builder() { + } + + /** + * Returns a new builder with the same values as this object. + */ + public Builder(@NonNull GpuHeadroomParams params) { + if (params.mInternal.calculationType >= 0) { + mCalculationType = params.mInternal.calculationType; + } + if (params.mInternal.calculationWindowMillis >= 0) { + mCalculationWindowMillis = params.mInternal.calculationWindowMillis; + } + } + + /** + * Sets the headroom calculation type. + * <p> + * + * @throws IllegalArgumentException if the type is invalid. + */ + @NonNull + public Builder setCalculationType( + @GpuHeadroomCalculationType int calculationType) { + switch (calculationType) { + case GPU_HEADROOM_CALCULATION_TYPE_MIN: + case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE: { + mCalculationType = calculationType; + return this; + } + } + throw new IllegalArgumentException("Invalid calculation type: " + calculationType); + } + + /** + * Sets the headroom calculation window size in milliseconds. + * <p> + * + * @param windowMillis the window size in milliseconds ranges from + * {@link SystemHealthManager#getGpuHeadroomCalculationWindowRange()}. + * The smaller the window size, the larger fluctuation in the headroom + * value should be expected. The default value can be retrieved from + * the {@link GpuHeadroomParams#getCalculationWindowMillis}. The device + * will try to use the closest feasible window size to this param. + * @throws IllegalArgumentException if the window is invalid. + */ + @NonNull + public Builder setCalculationWindowMillis(@IntRange(from = 1) int windowMillis) { + if (windowMillis <= 0) { + throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); + } + mCalculationWindowMillis = windowMillis; + return this; + } + + /** + * Builds the {@link GpuHeadroomParams} object. + */ + @NonNull + public GpuHeadroomParams build() { + GpuHeadroomParams params = new GpuHeadroomParams(); + if (mCalculationType >= 0) { + params.mInternal.calculationType = (byte) mCalculationType; + } + if (mCalculationWindowMillis >= 0) { + params.mInternal.calculationWindowMillis = mCalculationWindowMillis; + } + return params; } - throw new IllegalArgumentException("Invalid calculation type: " + calculationType); } /** * Gets the headroom calculation type. - * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if the params is not set. + * <p> + * This will return the default value chosen by the device if not set. */ public @GpuHeadroomCalculationType int getCalculationType() { @GpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) { - case GPU_HEADROOM_CALCULATION_TYPE_MIN, GPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> - mInternal.calculationType; + case GPU_HEADROOM_CALCULATION_TYPE_MIN, + GPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> mInternal.calculationType; default -> GPU_HEADROOM_CALCULATION_TYPE_MIN; }; return validatedType; } /** - * Sets the headroom calculation window size in milliseconds. - * <p> - * - * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the - * window size, the larger fluctuation in the headroom value should be - * expected. The default value can be retrieved from the - * {@link #getCalculationWindowMillis}. The device will try to use the - * closest feasible window size to this param. - * @throws IllegalArgumentException if the window is invalid. - */ - public void setCalculationWindowMillis( - @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to = - CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) { - if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN - || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) { - throw new IllegalArgumentException("Invalid calculation window: " + windowMillis); - } - mInternal.calculationWindowMillis = windowMillis; - } - - /** * Gets the headroom calculation window size in milliseconds. * <p> * This will return the default value chosen by the device if not set. */ - public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to = - CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() { + public int getCalculationWindowMillis() { return mInternal.calculationWindowMillis; } - /** - * @hide - */ - public GpuHeadroomParamsInternal getInternal() { - return mInternal; + @Override + public String toString() { + return "GpuHeadroomParams{" + + "calculationType=" + mInternal.calculationType + + ", calculationWindowMillis=" + mInternal.calculationWindowMillis + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GpuHeadroomParams that = (GpuHeadroomParams) o; + return mInternal.equals(that.mInternal); + } + + @Override + public int hashCode() { + return mInternal.hashCode(); } } diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl index 56a089aff78a..ac4a1e2d8cef 100644 --- a/core/java/android/os/IHintManager.aidl +++ b/core/java/android/os/IHintManager.aidl @@ -66,6 +66,7 @@ interface IHintManager { parcelable HintManagerClientData { int powerHalVersion; int maxGraphicsPipelineThreads; + int maxCpuHeadroomThreads; long preferredRateNanos; SupportInfo supportInfo; } @@ -82,4 +83,6 @@ interface IHintManager { * passing back a bundle of support and configuration information. */ HintManagerClientData registerClient(in IHintManagerClient client); + + HintManagerClientData getClientData(); } diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java index cd79e416531a..a1e9cf25e3e1 100644 --- a/core/java/android/os/health/SystemHealthManager.java +++ b/core/java/android/os/health/SystemHealthManager.java @@ -18,6 +18,7 @@ package android.os.health; import android.annotation.FlaggedApi; import android.annotation.FloatRange; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; @@ -42,6 +43,8 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.SynchronousResultReceiver; +import android.util.Pair; +import android.util.Slog; import com.android.internal.app.IBatteryStats; import com.android.server.power.optimization.Flags; @@ -69,15 +72,41 @@ import java.util.function.Consumer; * plugged in (e.g. using {@link android.app.job.JobScheduler JobScheduler}), and * while that can affect charging rates, it is still preferable to actually draining * the battery. + * <p> + * <b>CPU/GPU Usage</b><br> + * CPU/GPU headroom APIs are designed to be best used by applications with consistent and intense + * workload such as games to query the remaining capacity headroom over a short period and perform + * optimization accordingly. Due to the nature of the fast job scheduling and frequency scaling of + * CPU and GPU, the headroom by nature will have "TOCTOU" problem which makes it less suitable for + * apps with inconsistent or low workload to take any useful action but simply monitoring. And to + * avoid oscillation it's not recommended to adjust workload too frequent (on each polling request) + * or too aggressively. As the headroom calculation is more based on reflecting past history usage + * than predicting future capacity. Take game as an example, if the API returns CPU headroom of 0 in + * one scenario (especially if it's constant across multiple calls), or some value significantly + * smaller than other scenarios, then it can reason that the recent performance result is more CPU + * bottlenecked. Then reducing the CPU workload intensity can help reserve some headroom to handle + * the load variance better, which can result in less frame drops or smooth FPS value. On the other + * hand, if the API returns large CPU headroom constantly, the app can be more confident to increase + * the workload and expect higher possibility of device meeting its performance expectation. + * App can also use thermal APIs to read the current thermal status and headroom first, then poll + * the CPU and GPU headroom if the device is (about to) getting thermal throttled. If the CPU/GPU + * headrooms provide enough significance such as one valued at 0 while the other at 100, then it can + * be used to infer that reducing CPU workload could be more efficient to cool down the device. + * There is a caveat that the power controller may scale down the frequency of the CPU and GPU due + * to thermal and other reasons, which can result in a higher than usual percentage usage of the + * capacity. */ @SystemService(Context.SYSTEM_HEALTH_SERVICE) public class SystemHealthManager { + private static final String TAG = "SystemHealthManager"; @NonNull private final IBatteryStats mBatteryStats; @Nullable private final IPowerStatsService mPowerStats; @Nullable private final IHintManager mHintManager; + @Nullable + private final IHintManager.HintManagerClientData mHintManagerClientData; private List<PowerMonitor> mPowerMonitorsInfo; private final Object mPowerMonitorsLock = new Object(); private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000; @@ -109,29 +138,72 @@ public class SystemHealthManager { mBatteryStats = batteryStats; mPowerStats = powerStats; mHintManager = hintManager; + IHintManager.HintManagerClientData data = null; + if (mHintManager != null) { + try { + data = mHintManager.getClientData(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get hint manager client data", e); + } + } + mHintManagerClientData = data; } /** - * Provides an estimate of global available CPU headroom. + * Provides an estimate of available CPU capacity headroom of the device. + * <p> + * The value can be used by the calling application to determine if the workload was CPU bound + * and then take action accordingly to ensure that the workload can be completed smoothly. It + * can also be used with the thermal status and headroom to determine if reducing the CPU bound + * workload can help reduce the device temperature to avoid thermal throttling. * <p> + * If the params are valid, each call will perform at least one synchronous binder transaction + * that can take more than 1ms. So it's not recommended to call or wait for this on critical + * threads. Some devices may implement this as an on-demand API with lazy initialization, so the + * caller should expect higher latency when making the first call (especially with non-default + * params) since app starts or after changing params, as the device may need to change its data + * collection. * - * @param params params to customize the CPU headroom calculation, null to use default params. - * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. - * A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be - * granted. + * @param params params to customize the CPU headroom calculation, or null to use default. + * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable due to + * server error or not enough user CPU workload. + * Each valid value ranges from [0, 100], where 0 indicates no more cpu resources can be + * granted * @throws UnsupportedOperationException if the API is unsupported. + * @throws IllegalArgumentException if the params are invalid. * @throws SecurityException if the TIDs of the params don't belong to the same process. * @throws IllegalStateException if the TIDs of the params don't have the same affinity setting. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom( @Nullable CpuHeadroomParams params) { - if (mHintManager == null) { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) { throw new UnsupportedOperationException(); } + if (params != null) { + if (params.mInternal.tids != null && (params.mInternal.tids.length == 0 + || params.mInternal.tids.length + > mHintManagerClientData.maxCpuHeadroomThreads)) { + throw new IllegalArgumentException( + "Invalid number of TIDs: " + params.mInternal.tids.length); + } + if (params.mInternal.calculationWindowMillis + < mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis + || params.mInternal.calculationWindowMillis + > mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis) { + throw new IllegalArgumentException( + "Invalid calculation window: " + + params.mInternal.calculationWindowMillis + ", expect range: [" + + mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis + + ", " + + mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis + + "]"); + } + } try { final CpuHeadroomResult ret = mHintManager.getCpuHeadroom( - params != null ? params.getInternal() : new CpuHeadroomParamsInternal()); + params != null ? params.mInternal : new CpuHeadroomParamsInternal()); if (ret == null || ret.getTag() != CpuHeadroomResult.globalHeadroom) { return Float.NaN; } @@ -141,27 +213,69 @@ public class SystemHealthManager { } } - + /** + * Gets the maximum number of TIDs this device supports for getting CPU headroom. + * <p> + * See {@link CpuHeadroomParams#setTids(int...)}. + * + * @return the maximum size of TIDs supported + * @throws UnsupportedOperationException if the CPU headroom API is unsupported. + */ + @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) + public @IntRange(from = 1) int getMaxCpuHeadroomTidsSize() { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) { + throw new UnsupportedOperationException(); + } + return mHintManagerClientData.maxCpuHeadroomThreads; + } /** - * Provides an estimate of global available GPU headroom of the device. + * Provides an estimate of available GPU capacity headroom of the device. + * <p> + * The value can be used by the calling application to determine if the workload was GPU bound + * and then take action accordingly to ensure that the workload can be completed smoothly. It + * can also be used with the thermal status and headroom to determine if reducing the GPU bound + * workload can help reduce the device temperature to avoid thermal throttling. * <p> + * If the params are valid, each call will perform at least one synchronous binder transaction + * that can take more than 1ms. So it's not recommended to call or wait for this on critical + * threads. Some devices may implement this as an on-demand API with lazy initialization, so the + * caller should expect higher latency when making the first call (especially with non-default + * params) since app starts or after changing params, as the device may need to change its data + * collection. * - * @param params params to customize the GPU headroom calculation, null to use default params. + * @param params params to customize the GPU headroom calculation, or null to use default. * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable. - * A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be + * Each valid value ranges from [0, 100], where 0 indicates no more cpu resources can be * granted. * @throws UnsupportedOperationException if the API is unsupported. + * @throws IllegalArgumentException if the params are invalid. */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom( @Nullable GpuHeadroomParams params) { - if (mHintManager == null) { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) { throw new UnsupportedOperationException(); } + if (params != null) { + if (params.mInternal.calculationWindowMillis + < mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis + || params.mInternal.calculationWindowMillis + > mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis) { + throw new IllegalArgumentException( + "Invalid calculation window: " + + params.mInternal.calculationWindowMillis + ", expect range: [" + + mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis + + ", " + + mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis + + "]"); + } + } try { final GpuHeadroomResult ret = mHintManager.getGpuHeadroom( - params != null ? params.getInternal() : new GpuHeadroomParamsInternal()); + params != null ? params.mInternal : new GpuHeadroomParamsInternal()); if (ret == null || ret.getTag() != GpuHeadroomResult.globalHeadroom) { return Float.NaN; } @@ -172,7 +286,51 @@ public class SystemHealthManager { } /** - * Minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in + * Gets the range of the calculation window size for CPU headroom. + * <p> + * In API version 36, the range will be a superset of [50, 10000]. + * <p> + * See {@link CpuHeadroomParams#setCalculationWindowMillis(int)}. + * + * @return the range of the calculation window size supported in milliseconds. + * @throws UnsupportedOperationException if the CPU headroom API is unsupported. + */ + @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) + @NonNull + public Pair<Integer, Integer> getCpuHeadroomCalculationWindowRange() { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) { + throw new UnsupportedOperationException(); + } + return new Pair<>( + mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis, + mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis); + } + + /** + * Gets the range of the calculation window size for GPU headroom. + * <p> + * In API version 36, the range will be a superset of [50, 10000]. + * <p> + * See {@link GpuHeadroomParams#setCalculationWindowMillis(int)}. + * + * @return the range of the calculation window size supported in milliseconds. + * @throws UnsupportedOperationException if the GPU headroom API is unsupported. + */ + @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) + @NonNull + public Pair<Integer, Integer> getGpuHeadroomCalculationWindowRange() { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) { + throw new UnsupportedOperationException(); + } + return new Pair<>( + mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis, + mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis); + } + + /** + * Gets minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in * milliseconds. * <p> * The {@link #getCpuHeadroom(CpuHeadroomParams)} API may return cached result if called more @@ -182,7 +340,8 @@ public class SystemHealthManager { */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public long getCpuHeadroomMinIntervalMillis() { - if (mHintManager == null) { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) { throw new UnsupportedOperationException(); } try { @@ -193,7 +352,7 @@ public class SystemHealthManager { } /** - * Minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in + * Gets minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in * milliseconds. * <p> * The {@link #getGpuHeadroom(GpuHeadroomParams)} API may return cached result if called more @@ -203,7 +362,8 @@ public class SystemHealthManager { */ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS) public long getGpuHeadroomMinIntervalMillis() { - if (mHintManager == null) { + if (mHintManager == null || mHintManagerClientData == null + || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) { throw new UnsupportedOperationException(); } try { diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 1ccadf90c2a9..c04f347dc9e8 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -320,6 +320,9 @@ LIBANDROID { ASystemFontIterator_open; # introduced=29 ASystemFontIterator_close; # introduced=29 ASystemFontIterator_next; # introduced=29 + ASystemHealth_getMaxCpuHeadroomTidsSize; # introduced=36 + ASystemHealth_getCpuHeadroomCalculationWindowRange; # introduced=36 + ASystemHealth_getGpuHeadroomCalculationWindowRange; # introduced=36 ASystemHealth_getCpuHeadroom; # introduced=36 ASystemHealth_getGpuHeadroom; # introduced=36 ASystemHealth_getCpuHeadroomMinIntervalMillis; # introduced=36 diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp index f3fa9f6836d5..5c07ac7bfccc 100644 --- a/native/android/system_health.cpp +++ b/native/android/system_health.cpp @@ -31,26 +31,28 @@ namespace hal = aidl::android::hardware::power; struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {}; struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {}; -const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; -const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; -const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; -const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; -const int CPU_HEADROOM_MAX_TID_COUNT = 5; - struct ASystemHealthManager { public: static ASystemHealthManager* getInstance(); - ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager); + + ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager, + IHintManager::HintManagerClientData&& clientData); ASystemHealthManager() = delete; ~ASystemHealthManager(); int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom); int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom); int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis); int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis); + int getMaxCpuHeadroomTidsSize(size_t* outSize); + int getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis); + int getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis); private: static ASystemHealthManager* create(std::shared_ptr<IHintManager> hintManager); std::shared_ptr<IHintManager> mHintManager; + IHintManager::HintManagerClientData mClientData; }; ASystemHealthManager* ASystemHealthManager::getInstance() { @@ -60,10 +62,11 @@ ASystemHealthManager* ASystemHealthManager::getInstance() { return instance; } -ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager) - : mHintManager(std::move(hintManager)) {} +ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager, + IHintManager::HintManagerClientData&& clientData) + : mHintManager(std::move(hintManager)), mClientData(clientData) {} -ASystemHealthManager::~ASystemHealthManager() {} +ASystemHealthManager::~ASystemHealthManager() = default; ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> hintManager) { if (!hintManager) { @@ -74,20 +77,37 @@ ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__); return nullptr; } - return new ASystemHealthManager(hintManager); -} - -ASystemHealthManager* ASystemHealth_acquireManager() { - return ASystemHealthManager::getInstance(); + IHintManager::HintManagerClientData clientData; + ndk::ScopedAStatus ret = hintManager->getClientData(&clientData); + if (!ret.isOk()) { + ALOGE("%s: PerformanceHint service is not initialized %s", __FUNCTION__, ret.getMessage()); + return nullptr; + } + return new ASystemHealthManager(hintManager, std::move(clientData)); } int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) { + if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; std::optional<hal::CpuHeadroomResult> res; ::ndk::ScopedAStatus ret; CpuHeadroomParamsInternal internalParams; if (!params) { ret = mHintManager->getCpuHeadroom(internalParams, &res); } else { + LOG_ALWAYS_FATAL_IF((int)params->tids.size() > mClientData.maxCpuHeadroomThreads, + "%s: tids size should not exceed %d", __FUNCTION__, + mClientData.maxCpuHeadroomThreads); + LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis < + mClientData.supportInfo.headroom + .cpuMinCalculationWindowMillis || + params->calculationWindowMillis > + mClientData.supportInfo.headroom + .cpuMaxCalculationWindowMillis, + "%s: calculationWindowMillis should be in range [%d, %d] but got %d", + __FUNCTION__, + mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis, + mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis, + params->calculationWindowMillis); ret = mHintManager->getCpuHeadroom(*params, &res); } if (!ret.isOk()) { @@ -106,12 +126,24 @@ int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float } int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) { + if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; std::optional<hal::GpuHeadroomResult> res; ::ndk::ScopedAStatus ret; GpuHeadroomParamsInternal internalParams; if (!params) { ret = mHintManager->getGpuHeadroom(internalParams, &res); } else { + LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis < + mClientData.supportInfo.headroom + .gpuMinCalculationWindowMillis || + params->calculationWindowMillis > + mClientData.supportInfo.headroom + .gpuMaxCalculationWindowMillis, + "%s: calculationWindowMillis should be in range [%d, %d] but got %d", + __FUNCTION__, + mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis, + mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis, + params->calculationWindowMillis); ret = mHintManager->getGpuHeadroom(*params, &res); } if (!ret.isOk()) { @@ -128,6 +160,7 @@ int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float } int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) { + if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; int64_t minIntervalMillis = 0; ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis); if (!ret.isOk()) { @@ -142,6 +175,7 @@ int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinInterva } int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) { + if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; int64_t minIntervalMillis = 0; ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis); if (!ret.isOk()) { @@ -155,6 +189,57 @@ int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinInterva return OK; } +int ASystemHealthManager::getMaxCpuHeadroomTidsSize(size_t* outSize) { + if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; + *outSize = mClientData.maxCpuHeadroomThreads; + return OK; +} + +int ASystemHealthManager::getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) { + if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP; + *outMinMillis = mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis; + *outMaxMillis = mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis; + return OK; +} + +int ASystemHealthManager::getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) { + if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP; + *outMinMillis = mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis; + *outMaxMillis = mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis; + return OK; +} + +int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize) { + LOG_ALWAYS_FATAL_IF(outSize == nullptr, "%s: outSize should not be null", __FUNCTION__); + auto manager = ASystemHealthManager::getInstance(); + if (manager == nullptr) return ENOTSUP; + return manager->getMaxCpuHeadroomTidsSize(outSize); +} + +int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) { + LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null", + __FUNCTION__); + LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null", + __FUNCTION__); + auto manager = ASystemHealthManager::getInstance(); + if (manager == nullptr) return ENOTSUP; + return manager->getCpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis); +} + +int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis, + int32_t* _Nonnull outMaxMillis) { + LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null", + __FUNCTION__); + LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null", + __FUNCTION__); + auto manager = ASystemHealthManager::getInstance(); + if (manager == nullptr) return ENOTSUP; + return manager->getGpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis); +} + int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params, float* _Nonnull outHeadroom) { LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__); @@ -189,19 +274,15 @@ int ASystemHealth_getGpuHeadroomMinIntervalMillis(int64_t* _Nonnull outMinInterv void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params, int windowMillis) { - LOG_ALWAYS_FATAL_IF(windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN || - windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX, - "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__, - windowMillis); + LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d", + __FUNCTION__, windowMillis); params->calculationWindowMillis = windowMillis; } void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params, int windowMillis) { - LOG_ALWAYS_FATAL_IF(windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN || - windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX, - "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__, - windowMillis); + LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d", + __FUNCTION__, windowMillis); params->calculationWindowMillis = windowMillis; } @@ -214,13 +295,11 @@ int AGpuHeadroomParams_getCalculationWindowMillis(AGpuHeadroomParams* _Nonnull p } void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids, - int tidsSize) { + size_t tidsSize) { LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__); - LOG_ALWAYS_FATAL_IF(tidsSize > CPU_HEADROOM_MAX_TID_COUNT, "%s: tids size should not exceed 5", - __FUNCTION__); params->tids.resize(tidsSize); params->tids.clear(); - for (int i = 0; i < tidsSize; ++i) { + for (int i = 0; i < (int)tidsSize; ++i) { LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d", tids[i]); params->tids[i] = tids[i]; @@ -269,10 +348,10 @@ AGpuHeadroomParams* _Nonnull AGpuHeadroomParams_create() { return new AGpuHeadroomParams(); } -void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) { +void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) { delete params; } -void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) { +void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) { delete params; } diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index e3c10f63abb4..7c18f2b5f50e 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -86,6 +86,9 @@ public: clientDataIn, ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return), (override)); + MOCK_METHOD(ScopedAStatus, getClientData, + (::aidl::android::os::IHintManager::HintManagerClientData * _aidl_return), + (override)); MOCK_METHOD(SpAIBinder, asBinder, (), (override)); MOCK_METHOD(bool, isRemote, (), (override)); }; diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index a0bc77e939d1..647a4b2f5e53 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -120,6 +120,8 @@ public final class HintManagerService extends SystemService { @VisibleForTesting final long mHintSessionPreferredRate; @VisibleForTesting static final int MAX_GRAPHICS_PIPELINE_THREADS_COUNT = 5; + private static final int DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT = 5; + private static final int DEFAULT_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS = 50; // Multi-level map storing all active AppHintSessions. // First level is keyed by the UID of the client process creating the session. @@ -205,12 +207,17 @@ public final class HintManagerService extends SystemService { "persist.hms.check_headroom_affinity"; private static final String PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS = "persist.hms.check_headroom_proc_stat_min_millis"; + private static final String PROPERTY_CPU_HEADROOM_TID_MAX_CNT = + "persist.hms.cpu_headroom_tid_max_cnt"; private Boolean mFMQUsesIntegratedEventFlag = false; private final Object mCpuHeadroomLock = new Object(); @VisibleForTesting final float mJiffyMillis; + private final boolean mCheckHeadroomTid; + private final boolean mCheckHeadroomAffinity; private final int mCheckHeadroomProcStatMinMillis; + private final int mCpuHeadroomMaxTidCnt; @GuardedBy("mCpuHeadroomLock") private long mLastCpuUserModeTimeCheckedMillis = 0; @GuardedBy("mCpuHeadroomLock") @@ -338,13 +345,23 @@ public final class HintManagerService extends SystemService { mUidToLastUserModeJiffies = new ArrayMap<>(); long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); mJiffyMillis = 1000.0f / jiffyHz; + mCheckHeadroomTid = SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true); + mCheckHeadroomAffinity = SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_AFFINITY, + true); mCheckHeadroomProcStatMinMillis = SystemProperties.getInt( - PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, 50); + PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, + DEFAULT_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS); + mCpuHeadroomMaxTidCnt = Math.min(SystemProperties.getInt( + PROPERTY_CPU_HEADROOM_TID_MAX_CNT, DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT), + mSupportInfo.headroom.cpuMaxTidCount); } else { mCpuHeadroomCache = null; mUidToLastUserModeJiffies = null; mJiffyMillis = 0.0f; + mCheckHeadroomTid = true; + mCheckHeadroomAffinity = true; mCheckHeadroomProcStatMinMillis = 0; + mCpuHeadroomMaxTidCnt = 0; } if (mSupportInfo.headroom.isGpuSupported) { mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis); @@ -1538,8 +1555,7 @@ public final class HintManagerService extends SystemService { if (params.usesDeviceHeadroom) { halParams.tids = new int[]{}; } else if (params.tids != null && params.tids.length > 0) { - if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && SystemProperties.getBoolean( - PROPERTY_CHECK_HEADROOM_TID, true)) { + if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && mCheckHeadroomTid) { final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); for (int tid : params.tids) { if (Process.getThreadGroupLeader(tid) != tgid) { @@ -1549,8 +1565,8 @@ public final class HintManagerService extends SystemService { } } } - if (cpuHeadroomAffinityCheck() && params.tids.length > 1 - && SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_AFFINITY, true)) { + if (cpuHeadroomAffinityCheck() && mCheckHeadroomAffinity + && params.tids.length > 1) { checkThreadAffinityForTids(params.tids); } halParams.tids = params.tids; @@ -1676,15 +1692,22 @@ public final class HintManagerService extends SystemService { throw new IllegalArgumentException( "Unknown CPU headroom calculation type " + (int) params.calculationType); } - if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) { + if (params.calculationWindowMillis < mSupportInfo.headroom.cpuMinCalculationWindowMillis + || params.calculationWindowMillis + > mSupportInfo.headroom.cpuMaxCalculationWindowMillis) { throw new IllegalArgumentException( - "Invalid CPU headroom calculation window, expected [50, 10000] but got " + "Invalid CPU headroom calculation window, expected [" + + mSupportInfo.headroom.cpuMinCalculationWindowMillis + + ", " + + mSupportInfo.headroom.cpuMaxCalculationWindowMillis + + "] but got " + params.calculationWindowMillis); } if (!params.usesDeviceHeadroom) { - if (params.tids != null && params.tids.length > 5) { + if (params.tids != null && params.tids.length > mCpuHeadroomMaxTidCnt) { throw new IllegalArgumentException( - "More than 5 TIDs requested: " + params.tids.length); + "More than " + mCpuHeadroomMaxTidCnt + " TIDs requested: " + + params.tids.length); } } } @@ -1745,9 +1768,13 @@ public final class HintManagerService extends SystemService { throw new IllegalArgumentException( "Unknown GPU headroom calculation type " + (int) params.calculationType); } - if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) { + if (params.calculationWindowMillis < mSupportInfo.headroom.gpuMinCalculationWindowMillis + || params.calculationWindowMillis + > mSupportInfo.headroom.gpuMaxCalculationWindowMillis) { throw new IllegalArgumentException( - "Invalid GPU headroom calculation window, expected [50, 10000] but got " + "Invalid GPU headroom calculation window, expected [" + + mSupportInfo.headroom.gpuMinCalculationWindowMillis + ", " + + mSupportInfo.headroom.gpuMaxCalculationWindowMillis + "] but got " + params.calculationWindowMillis); } } @@ -1780,9 +1807,15 @@ public final class HintManagerService extends SystemService { @Override public IHintManager.HintManagerClientData registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) { + return getClientData(); + } + + @Override + public IHintManager.HintManagerClientData getClientData() { IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData(); out.preferredRateNanos = mHintSessionPreferredRate; out.maxGraphicsPipelineThreads = getMaxGraphicsPipelineThreadsCount(); + out.maxCpuHeadroomThreads = DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT; out.powerHalVersion = mPowerHalVersion; out.supportInfo = mSupportInfo; return out; @@ -1811,23 +1844,40 @@ public final class HintManagerService extends SystemService { } } } - pw.println("CPU Headroom Interval: " + mSupportInfo.headroom.cpuMinIntervalMillis); - pw.println("GPU Headroom Interval: " + mSupportInfo.headroom.gpuMinIntervalMillis); - try { - CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal(); - params.usesDeviceHeadroom = true; - CpuHeadroomResult ret = getCpuHeadroom(params); - pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); - } catch (Exception e) { - Slog.d(TAG, "Failed to dump CPU headroom", e); - pw.println("CPU headroom: N/A"); + pw.println("CPU Headroom Supported: " + mSupportInfo.headroom.isCpuSupported); + if (mSupportInfo.headroom.isCpuSupported) { + pw.println("CPU Headroom Interval: " + mSupportInfo.headroom.cpuMinIntervalMillis); + pw.println("CPU Headroom TID Max Count: " + mCpuHeadroomMaxTidCnt); + pw.println("CPU Headroom TID Max Count From HAL: " + + mSupportInfo.headroom.cpuMaxTidCount); + pw.println("CPU Headroom Calculation Window Range: [" + + mSupportInfo.headroom.cpuMinCalculationWindowMillis + ", " + + mSupportInfo.headroom.cpuMaxCalculationWindowMillis + "]"); + try { + CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal(); + params.usesDeviceHeadroom = true; + CpuHeadroomResult ret = getCpuHeadroom(params); + pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); + } catch (Exception e) { + Slog.d(TAG, "Failed to dump CPU headroom", e); + pw.println("CPU headroom: N/A"); + } } - try { - GpuHeadroomResult ret = getGpuHeadroom(new GpuHeadroomParamsInternal()); - pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); - } catch (Exception e) { - Slog.d(TAG, "Failed to dump GPU headroom", e); - pw.println("GPU headroom: N/A"); + pw.println("GPU Headroom Supported: " + mSupportInfo.headroom.isGpuSupported); + if (mSupportInfo.headroom.isGpuSupported) { + pw.println("GPU Headroom Interval: " + mSupportInfo.headroom.gpuMinIntervalMillis); + pw.println("GPU Headroom Calculation Window Range: [" + + mSupportInfo.headroom.gpuMinCalculationWindowMillis + ", " + + mSupportInfo.headroom.gpuMaxCalculationWindowMillis + "]"); + try { + GpuHeadroomParamsInternal params = new GpuHeadroomParamsInternal(); + params.calculationWindowMillis = mDefaultGpuHeadroomCalculationWindowMillis; + GpuHeadroomResult ret = getGpuHeadroom(params); + pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom())); + } catch (Exception e) { + Slog.d(TAG, "Failed to dump GPU headroom", e); + pw.println("GPU headroom: N/A"); + } } } |