summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Xiang Wang <xwxw@google.com> 2024-12-17 13:07:25 -0800
committer Xiang Wang <xwxw@google.com> 2025-01-03 21:57:07 -0800
commit9801ae6160b104c8fe6a95b91ea3f7250697ff98 (patch)
treed887770e4b4ff1e69666db3ec6f6d8c821556e4e
parent98ab6d9dc032ea719ef23337f2f276d04beb32ea (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.txt35
-rw-r--r--core/java/android/os/CpuHeadroomParams.java225
-rw-r--r--core/java/android/os/GpuHeadroomParams.java172
-rw-r--r--core/java/android/os/IHintManager.aidl3
-rw-r--r--core/java/android/os/health/SystemHealthManager.java194
-rw-r--r--native/android/libandroid.map.txt3
-rw-r--r--native/android/system_health.cpp137
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp3
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java104
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");
+ }
}
}