summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Xiang Wang <xwxw@google.com> 2024-11-13 20:45:36 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-13 20:45:36 +0000
commitedff77959efa22e21b75f613dd2f326057eaf37f (patch)
treec02cd3f4c7916729d7eccde1ed99439f1609b3fe
parentf13aa1d82636d7fbcd9b490c66add79f9d215bbf (diff)
parente74bcd1c6929ff6d1c7fb43ad416d3a4cc695b9f (diff)
Merge "Add SDK support for CPU/GPU headroom APIs" into main
-rw-r--r--core/api/current.txt20
-rw-r--r--core/java/Android.bp2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java5
-rw-r--r--core/java/android/os/CpuHeadroomParams.java91
-rw-r--r--core/java/android/os/CpuHeadroomParamsInternal.aidl31
-rw-r--r--core/java/android/os/GpuHeadroomParams.java91
-rw-r--r--core/java/android/os/GpuHeadroomParamsInternal.aidl28
-rw-r--r--core/java/android/os/IHintManager.aidl6
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/os/health/SystemHealthManager.java111
-rw-r--r--native/android/tests/performance_hint/PerformanceHintNativeTest.cpp12
-rw-r--r--services/core/java/com/android/server/power/hint/HintManagerService.java282
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java186
13 files changed, 848 insertions, 24 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index df681d43e2d1..89850a5d864a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -33344,6 +33344,14 @@ package android.os {
method public final android.os.CountDownTimer start();
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
+ ctor public CpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ 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 final class CpuUsageInfo implements android.os.Parcelable {
method public int describeContents();
method public long getActive();
@@ -33591,6 +33599,14 @@ package android.os {
method public void onProgress(long);
}
+ @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
+ ctor public GpuHeadroomParams();
+ method public int getCalculationType();
+ method public void setCalculationType(int);
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
+ field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+ }
+
public class Handler {
ctor @Deprecated public Handler();
ctor @Deprecated public Handler(@Nullable android.os.Handler.Callback);
@@ -34841,6 +34857,10 @@ 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") 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") public long getGpuHeadroomMinIntervalMillis();
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.bp b/core/java/Android.bp
index cf5ebbaa37b4..bc38294279a8 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -232,6 +232,8 @@ aidl_interface {
"android.hardware.power-aidl",
],
srcs: [
+ "android/os/CpuHeadroomParamsInternal.aidl",
+ "android/os/GpuHeadroomParamsInternal.aidl",
"android/os/IHintManager.aidl",
"android/os/IHintSession.aidl",
],
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e451116081fa..ee0c38c60ff1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -190,6 +190,7 @@ import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.IDumpstate;
import android.os.IHardwarePropertiesManager;
+import android.os.IHintManager;
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
@@ -1195,8 +1196,10 @@ public final class SystemServiceRegistry {
public SystemHealthManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder batteryStats = ServiceManager.getServiceOrThrow(BatteryStats.SERVICE_NAME);
IBinder powerStats = ServiceManager.getService(Context.POWER_STATS_SERVICE);
+ IBinder perfHint = ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE);
return new SystemHealthManager(IBatteryStats.Stub.asInterface(batteryStats),
- IPowerStatsService.Stub.asInterface(powerStats));
+ IPowerStatsService.Stub.asInterface(powerStats),
+ IHintManager.Stub.asInterface(perfHint));
}});
registerService(Context.CONTEXTHUB_SERVICE, ContextHubManager.class,
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
new file mode 100644
index 000000000000..f0d4f7d8737f
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}.
+ */
+@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
+ CPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ 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);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #CPU_HEADROOM_CALCULATION_TYPE_MIN} 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;
+ default -> CPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public CpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/CpuHeadroomParamsInternal.aidl b/core/java/android/os/CpuHeadroomParamsInternal.aidl
new file mode 100644
index 000000000000..6cc4699a809e
--- /dev/null
+++ b/core/java/android/os/CpuHeadroomParamsInternal.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.CpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#CpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable CpuHeadroomParamsInternal {
+ boolean usesDeviceHeadroom = false;
+ CpuHeadroomParams.CalculationType calculationType = CpuHeadroomParams.CalculationType.MIN;
+ CpuHeadroomParams.SelectionType selectionType = CpuHeadroomParams.SelectionType.ALL;
+}
+
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
new file mode 100644
index 000000000000..efb2a28ad2b5
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.os.health.SystemHealthManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Headroom request params used by {@link SystemHealthManager#getGpuHeadroom(GpuHeadroomParams)}.
+ */
+@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
+ GPU_HEADROOM_CALCULATION_TYPE_AVERAGE, // 1
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface GpuHeadroomCalculationType {
+ }
+
+ /**
+ * Calculates the headroom based on minimum value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
+
+ /**
+ * Calculates the headroom based on average value over a device-defined window.
+ */
+ public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
+
+ /**
+ * Sets the headroom calculation type.
+ * <p>
+ *
+ * @throws IllegalArgumentException if the type is invalid.
+ */
+ 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;
+ }
+ throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+ }
+
+ /**
+ * Gets the headroom calculation type.
+ * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} 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;
+ default -> GPU_HEADROOM_CALCULATION_TYPE_MIN;
+ };
+ return validatedType;
+ }
+
+ /**
+ * @hide
+ */
+ public GpuHeadroomParamsInternal getInternal() {
+ return mInternal;
+ }
+}
diff --git a/core/java/android/os/GpuHeadroomParamsInternal.aidl b/core/java/android/os/GpuHeadroomParamsInternal.aidl
new file mode 100644
index 000000000000..20309e7673f2
--- /dev/null
+++ b/core/java/android/os/GpuHeadroomParamsInternal.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.hardware.power.GpuHeadroomParams;
+
+/**
+ * Changes should be synced with match function of HintManagerService#GpuHeadroomCacheItem.
+ * {@hide}
+ */
+@JavaDerive(equals = true, toString = true)
+parcelable GpuHeadroomParamsInternal {
+ GpuHeadroomParams.CalculationType calculationType = GpuHeadroomParams.CalculationType.MIN;
+}
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 73cdd5682f31..33120556339f 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -17,6 +17,8 @@
package android.os;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IHintSession;
import android.hardware.power.ChannelConfig;
import android.hardware.power.SessionConfig;
@@ -50,4 +52,8 @@ interface IHintManager {
*/
@nullable ChannelConfig getSessionChannel(in IBinder token);
oneway void closeSessionChannel();
+ float[] getCpuHeadroom(in CpuHeadroomParamsInternal params);
+ long getCpuHeadroomMinIntervalMillis();
+ float getGpuHeadroom(in GpuHeadroomParamsInternal params);
+ long getGpuHeadroomMinIntervalMillis();
}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index d9db28e0b3c3..095799cce646 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -148,6 +148,13 @@ flag {
}
flag {
+ name: "cpu_gpu_headrooms"
+ namespace: "game"
+ description: "Feature flag for adding CPU/GPU headroom API"
+ bug: "346604998"
+}
+
+flag {
name: "disallow_cellular_null_ciphers_restriction"
namespace: "cellular_security"
description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index deabfed365a6..4db9bc333e2b 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -17,6 +17,7 @@
package android.os.health;
import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
@@ -25,6 +26,11 @@ import android.content.Context;
import android.os.BatteryStats;
import android.os.Build;
import android.os.Bundle;
+import android.os.CpuHeadroomParams;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParams;
+import android.os.GpuHeadroomParamsInternal;
+import android.os.IHintManager;
import android.os.IPowerStatsService;
import android.os.OutcomeReceiver;
import android.os.PowerMonitor;
@@ -68,6 +74,8 @@ public class SystemHealthManager {
private final IBatteryStats mBatteryStats;
@Nullable
private final IPowerStatsService mPowerStats;
+ @Nullable
+ private final IHintManager mHintManager;
private List<PowerMonitor> mPowerMonitorsInfo;
private final Object mPowerMonitorsLock = new Object();
private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000;
@@ -88,14 +96,111 @@ public class SystemHealthManager {
public SystemHealthManager() {
this(IBatteryStats.Stub.asInterface(ServiceManager.getService(BatteryStats.SERVICE_NAME)),
IPowerStatsService.Stub.asInterface(
- ServiceManager.getService(Context.POWER_STATS_SERVICE)));
+ ServiceManager.getService(Context.POWER_STATS_SERVICE)),
+ IHintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PERFORMANCE_HINT_SERVICE)));
}
/** {@hide} */
public SystemHealthManager(@NonNull IBatteryStats batteryStats,
- @Nullable IPowerStatsService powerStats) {
+ @Nullable IPowerStatsService powerStats, @Nullable IHintManager hintManager) {
mBatteryStats = batteryStats;
mPowerStats = powerStats;
+ mHintManager = hintManager;
+ }
+
+ /**
+ * Provides an estimate of global available CPU headroom of the calling thread.
+ * <p>
+ *
+ * @param params params to customize the CPU headroom calculation, null to use default params.
+ * @return a single value 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.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom(
+ @Nullable CpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroom(
+ params != null ? params.getInternal() : new CpuHeadroomParamsInternal())[0];
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+
+
+ /**
+ * Provides an estimate of global available GPU headroom of the device.
+ * <p>
+ *
+ * @param params params to customize the GPU 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 GPU resources can be
+ * granted.
+ * @throws UnsupportedOperationException if the API is unsupported or the request params can't
+ * be served.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom(
+ @Nullable GpuHeadroomParams params) {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroom(
+ params != null ? params.getInternal() : new GpuHeadroomParamsInternal());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getCpuHeadroom(CpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getCpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getCpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in
+ * milliseconds.
+ * <p>
+ * The {@link #getGpuHeadroom(GpuHeadroomParams)} API may return cached result if called more
+ * frequent than the interval.
+ *
+ * @throws UnsupportedOperationException if the API is unsupported.
+ */
+ @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+ public long getGpuHeadroomMinIntervalMillis() {
+ if (mHintManager == null) {
+ throw new UnsupportedOperationException();
+ }
+ try {
+ return mHintManager.getGpuHeadroomMinIntervalMillis();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -261,7 +366,7 @@ public class SystemHealthManager {
mPowerMonitorsInfo = result;
}
if (executor != null) {
- executor.execute(()-> onResult.accept(result));
+ executor.execute(() -> onResult.accept(result));
} else {
onResult.accept(result);
}
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index 9de3a6f525e6..641e8d9a9b48 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -66,6 +66,18 @@ public:
std::optional<hal::ChannelConfig>* _aidl_return),
(override));
MOCK_METHOD(ScopedAStatus, closeSessionChannel, (), (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroom,
+ (const ::aidl::android::os::CpuHeadroomParamsInternal& in_params,
+ std::vector<float>* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroom,
+ (const ::aidl::android::os::GpuHeadroomParamsInternal& in_params,
+ float* _aidl_return),
+ (override));
+ MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _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 2c0ce252df18..17459df2bc1a 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -33,11 +33,15 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.hardware.power.ChannelConfig;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.IHintManager;
@@ -90,6 +94,10 @@ public final class HintManagerService extends SystemService {
private static final int EVENT_CLEAN_UP_UID = 3;
@VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000;
+ private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000;
+ private static final int HEADROOM_INTERVAL_UNSUPPORTED = -1;
+ @VisibleForTesting static final int DEFAULT_HEADROOM_PID = -1;
@VisibleForTesting final long mHintSessionPreferredRate;
@@ -160,10 +168,76 @@ public final class HintManagerService extends SystemService {
private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
+ private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms";
private Boolean mFMQUsesIntegratedEventFlag = false;
- @VisibleForTesting final IHintManager.Stub mService = new BinderService();
+ private final Object mCpuHeadroomLock = new Object();
+
+ private static class CpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ CpuHeadroomParamsInternal mParams;
+ float[] mHeadroom;
+ long mPid;
+
+ CpuHeadroomCacheItem(long expiredTimeMillis, CpuHeadroomParamsInternal params,
+ float[] headroom, long pid) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mPid = pid;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(CpuHeadroomParamsInternal params, long pid) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params) && pid == mPid;
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mCpuHeadroomLock")
+ private final List<CpuHeadroomCacheItem> mCpuHeadroomCache;
+ private final long mCpuHeadroomIntervalMillis;
+
+ private final Object mGpuHeadroomLock = new Object();
+
+ private static class GpuHeadroomCacheItem {
+ long mExpiredTimeMillis;
+ GpuHeadroomParamsInternal mParams;
+ float mHeadroom;
+
+ GpuHeadroomCacheItem(long expiredTimeMillis, GpuHeadroomParamsInternal params,
+ float headroom) {
+ mExpiredTimeMillis = expiredTimeMillis;
+ mParams = params;
+ mHeadroom = headroom;
+ }
+
+ private boolean match(GpuHeadroomParamsInternal params) {
+ if (mParams == null && params == null) return true;
+ if (mParams != null) {
+ return mParams.equals(params);
+ }
+ return false;
+ }
+
+ private boolean isExpired() {
+ return System.currentTimeMillis() > mExpiredTimeMillis;
+ }
+ }
+
+ @GuardedBy("mGpuHeadroomLock")
+ private final List<GpuHeadroomCacheItem> mGpuHeadroomCache;
+ private final long mGpuHeadroomIntervalMillis;
+
+ @VisibleForTesting
+ final IHintManager.Stub mService = new BinderService();
public HintManagerService(Context context) {
this(context, new Injector());
@@ -197,13 +271,72 @@ public final class HintManagerService extends SystemService {
mPowerHal = injector.createIPower();
mPowerHalVersion = 0;
mUsesFmq = false;
+ long cpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
+ long gpuHeadroomIntervalMillis = HEADROOM_INTERVAL_UNSUPPORTED;
if (mPowerHal != null) {
try {
mPowerHalVersion = mPowerHal.getInterfaceVersion();
+ if (mPowerHal.getInterfaceVersion() >= 6) {
+ if (SystemProperties.getBoolean(PROPERTY_USE_HAL_HEADROOMS, true)) {
+ cpuHeadroomIntervalMillis = checkCpuHeadroomSupport();
+ gpuHeadroomIntervalMillis = checkGpuHeadroomSupport();
+ }
+ }
} catch (RemoteException e) {
throw new IllegalStateException("Could not contact PowerHAL!", e);
}
}
+ mCpuHeadroomIntervalMillis = cpuHeadroomIntervalMillis;
+ mGpuHeadroomIntervalMillis = gpuHeadroomIntervalMillis;
+ if (mCpuHeadroomIntervalMillis > 0) {
+ mCpuHeadroomCache = new ArrayList<>(4);
+ } else {
+ mCpuHeadroomCache = null;
+ }
+ if (mGpuHeadroomIntervalMillis > 0) {
+ mGpuHeadroomCache = new ArrayList<>(2);
+ } else {
+ mGpuHeadroomCache = null;
+ }
+ }
+
+ private long checkCpuHeadroomSupport() {
+ try {
+ synchronized (mCpuHeadroomLock) {
+ final CpuHeadroomParams defaultParams = new CpuHeadroomParams();
+ defaultParams.pid = Process.myPid();
+ float[] ret = mPowerHal.getCpuHeadroom(defaultParams);
+ if (ret != null && ret.length > 0) {
+ return Math.max(
+ DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getCpuHeadroomMinIntervalMillis());
+ }
+ }
+
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getCpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getCpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
+ }
+
+ private long checkGpuHeadroomSupport() {
+ try {
+ synchronized (mGpuHeadroomLock) {
+ float ret = mPowerHal.getGpuHeadroom(new GpuHeadroomParams());
+ if (!Float.isNaN(ret)) {
+ return Math.max(
+ DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS,
+ mPowerHal.getGpuHeadroomMinIntervalMillis());
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ Slog.w(TAG, "getGpuHeadroom HAL API is not supported", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "getGpuHeadroom HAL API fails, disabling the API", e);
+ }
+ return HEADROOM_INTERVAL_UNSUPPORTED;
}
private ServiceThread createCleanUpThread() {
@@ -738,7 +871,7 @@ public final class HintManagerService extends SystemService {
mLinked = false;
}
if (mConfig != null) {
- try {
+ try {
mPowerHal.closeSessionChannel(mTgid, mUid);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to close session channel!", e);
@@ -982,13 +1115,13 @@ public final class HintManagerService extends SystemService {
}
// returns the first invalid tid or null if not found
- private Integer checkTidValid(int uid, int tgid, int [] tids, IntArray nonIsolated) {
+ private Integer checkTidValid(int uid, int tgid, int[] tids, IntArray nonIsolated) {
// Make sure all tids belongs to the same UID (including isolated UID),
// tids can belong to different application processes.
List<Integer> isolatedPids = null;
for (int i = 0; i < tids.length; i++) {
int tid = tids[i];
- final String[] procStatusKeys = new String[] {
+ final String[] procStatusKeys = new String[]{
"Uid:",
"Tgid:"
};
@@ -1058,7 +1191,7 @@ public final class HintManagerService extends SystemService {
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
@@ -1214,6 +1347,124 @@ public final class HintManagerService extends SystemService {
}
@Override
+ public float[] getCpuHeadroom(@Nullable CpuHeadroomParamsInternal params) {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ CpuHeadroomParams halParams = new CpuHeadroomParams();
+ halParams.pid = Binder.getCallingPid();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ halParams.selectionType = params.selectionType;
+ if (params.usesDeviceHeadroom) {
+ halParams.pid = DEFAULT_HEADROOM_PID;
+ }
+ }
+ synchronized (mCpuHeadroomLock) {
+ while (!mCpuHeadroomCache.isEmpty()) {
+ if (mCpuHeadroomCache.getFirst().isExpired()) {
+ mCpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mCpuHeadroomCache.size(); ++i) {
+ final CpuHeadroomCacheItem item = mCpuHeadroomCache.get(i);
+ if (item.match(params, halParams.pid)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis;
+ mCpuHeadroomCache.remove(i);
+ mCpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float[] headroom = mPowerHal.getCpuHeadroom(halParams);
+ if (headroom == null || headroom.length == 0) {
+ Slog.wtf(TAG,
+ "CPU headroom from Power HAL is invalid: " + Arrays.toString(headroom));
+ return new float[]{Float.NaN};
+ }
+ synchronized (mCpuHeadroomLock) {
+ mCpuHeadroomCache.add(new CpuHeadroomCacheItem(
+ System.currentTimeMillis() + mCpuHeadroomIntervalMillis,
+ params, headroom, halParams.pid
+ ));
+ }
+ return headroom;
+
+ } catch (RemoteException e) {
+ return new float[]{Float.NaN};
+ }
+ }
+
+ @Override
+ public float getGpuHeadroom(@Nullable GpuHeadroomParamsInternal params) {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ GpuHeadroomParams halParams = new GpuHeadroomParams();
+ if (params != null) {
+ halParams.calculationType = params.calculationType;
+ }
+ synchronized (mGpuHeadroomLock) {
+ while (!mGpuHeadroomCache.isEmpty()) {
+ if (mGpuHeadroomCache.getFirst().isExpired()) {
+ mGpuHeadroomCache.removeFirst();
+ } else {
+ break;
+ }
+ }
+ for (int i = 0; i < mGpuHeadroomCache.size(); ++i) {
+ final GpuHeadroomCacheItem item = mGpuHeadroomCache.get(i);
+ if (item.match(params)) {
+ item.mExpiredTimeMillis =
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis;
+ mGpuHeadroomCache.remove(i);
+ mGpuHeadroomCache.add(item);
+ return item.mHeadroom;
+ }
+ }
+ }
+ // return from HAL directly
+ try {
+ float headroom = mPowerHal.getGpuHeadroom(halParams);
+ if (Float.isNaN(headroom)) {
+ Slog.wtf(TAG,
+ "GPU headroom from Power HAL is NaN");
+ return Float.NaN;
+ }
+ synchronized (mGpuHeadroomLock) {
+ mGpuHeadroomCache.add(new GpuHeadroomCacheItem(
+ System.currentTimeMillis() + mGpuHeadroomIntervalMillis,
+ params, headroom
+ ));
+ }
+ return headroom;
+ } catch (RemoteException e) {
+ return Float.NaN;
+ }
+ }
+
+ @Override
+ public long getCpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mCpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mCpuHeadroomIntervalMillis;
+ }
+
+ @Override
+ public long getGpuHeadroomMinIntervalMillis() throws RemoteException {
+ if (mGpuHeadroomIntervalMillis <= 0) {
+ throw new UnsupportedOperationException();
+ }
+ return mGpuHeadroomIntervalMillis;
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1235,6 +1486,25 @@ public final class HintManagerService extends SystemService {
}
}
}
+ pw.println("CPU Headroom Interval: " + mCpuHeadroomIntervalMillis);
+ pw.println("GPU Headroom Interval: " + mGpuHeadroomIntervalMillis);
+ try {
+ CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom: " + Arrays.toString(getCpuHeadroom(params)));
+ params = new CpuHeadroomParamsInternal();
+ params.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ params.usesDeviceHeadroom = true;
+ pw.println("CPU headroom per core: " + Arrays.toString(getCpuHeadroom(params)));
+ } catch (Exception e) {
+ pw.println("CPU headroom: N/A");
+ }
+ try {
+ pw.println("GPU headroom: " + getGpuHeadroom(null));
+ } catch (Exception e) {
+ pw.println("GPU headroom: N/A");
+ }
}
private void logPerformanceHintSessionAtom(int uid, long sessionId,
@@ -1467,7 +1737,7 @@ public final class HintManagerService extends SystemService {
Slogf.w(TAG, errMsg);
throw new SecurityException(errMsg);
}
- if (resetOnForkEnabled()){
+ if (resetOnForkEnabled()) {
try {
for (int tid : tids) {
int policy = Process.getThreadScheduler(tid);
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 58489f398775..0881b4cf9bcf 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.power.hint;
import static com.android.server.power.hint.HintManagerService.CLEAN_UP_UID_DELAY_MILLIS;
+import static com.android.server.power.hint.HintManagerService.DEFAULT_HEADROOM_PID;
import static com.google.common.truth.Truth.assertThat;
@@ -51,11 +52,15 @@ import android.content.pm.PackageManager;
import android.hardware.common.fmq.MQDescriptor;
import android.hardware.power.ChannelConfig;
import android.hardware.power.ChannelMessage;
+import android.hardware.power.CpuHeadroomParams;
+import android.hardware.power.GpuHeadroomParams;
import android.hardware.power.IPower;
import android.hardware.power.SessionConfig;
import android.hardware.power.SessionTag;
import android.hardware.power.WorkDuration;
import android.os.Binder;
+import android.os.CpuHeadroomParamsInternal;
+import android.os.GpuHeadroomParamsInternal;
import android.os.IBinder;
import android.os.IHintSession;
import android.os.PerformanceHintManager;
@@ -128,11 +133,11 @@ public class HintManagerServiceTest {
private static final long[] TIMESTAMPS_ZERO = new long[] {};
private static final long[] TIMESTAMPS_TWO = new long[] {1L, 2L};
private static final WorkDuration[] WORK_DURATIONS_FIVE = new WorkDuration[] {
- makeWorkDuration(1L, 11L, 1L, 8L, 4L),
- makeWorkDuration(2L, 13L, 2L, 8L, 6L),
- makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
- makeWorkDuration(2L, 13L, 2L, 0L, 6L),
- makeWorkDuration(2L, 13L, 2L, 8L, 0L),
+ makeWorkDuration(1L, 11L, 1L, 8L, 4L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 6L),
+ makeWorkDuration(3L, 333333333L, 3L, 8L, 333333333L),
+ makeWorkDuration(2L, 13L, 2L, 0L, 6L),
+ makeWorkDuration(2L, 13L, 2L, 8L, 0L),
};
private static final String TEST_APP_NAME = "com.android.test.app";
@@ -187,17 +192,17 @@ public class HintManagerServiceTest {
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_A), eq(DEFAULT_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[0],
- SESSION_IDS[0]));
+ SESSION_IDS[0]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_B), eq(DOUBLED_TARGET_DURATION), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[1],
- SESSION_IDS[1]));
+ SESSION_IDS[1]));
when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
eq(SESSION_TIDS_C), eq(0L), anyInt(),
any(SessionConfig.class))).thenAnswer(fakeCreateWithConfig(SESSION_PTRS[2],
- SESSION_IDS[2]));
+ SESSION_IDS[2]));
- when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(6);
when(mIPowerMock.getSessionChannel(anyInt(), anyInt())).thenReturn(mConfig);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock);
@@ -217,8 +222,8 @@ public class HintManagerServiceTest {
when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_C),
eq(0L))).thenReturn(SESSION_PTRS[2]);
when(mNativeWrapperMock.halCreateHintSessionWithConfig(anyInt(), anyInt(),
- any(int[].class), anyLong(), anyInt(),
- any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
+ any(int[].class), anyLong(), anyInt(),
+ any(SessionConfig.class))).thenThrow(new UnsupportedOperationException());
}
static class NativeWrapperFake extends NativeWrapper {
@@ -337,7 +342,7 @@ public class HintManagerServiceTest {
SESSION_TIDS_C, 0L, SessionTag.OTHER, new SessionConfig());
assertNotNull(c);
verify(mNativeWrapperMock, times(3)).halCreateHintSession(anyInt(), anyInt(),
- any(int[].class), anyLong());
+ any(int[].class), anyLong());
}
@Test
@@ -487,7 +492,7 @@ public class HintManagerServiceTest {
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
a.sendHint(PerformanceHintManager.Session.CPU_LOAD_RESET);
verify(mNativeWrapperMock, times(1)).halSendHint(anyLong(),
@@ -514,7 +519,7 @@ public class HintManagerServiceTest {
AppHintSession a = (AppHintSession) service.getBinderServiceInstance()
.createHintSessionWithConfig(token, SESSION_TIDS_A, DEFAULT_TARGET_DURATION,
- SessionTag.OTHER, new SessionConfig());
+ SessionTag.OTHER, new SessionConfig());
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -1096,4 +1101,157 @@ public class HintManagerServiceTest {
verify(mIPowerMock, times(1)).getSessionChannel(eq(TGID), eq(UID));
assertTrue(service.hasChannel(TGID, UID));
}
+
+ @Test
+ public void testHeadroomPowerHalNotSupported() throws Exception {
+ when(mIPowerMock.getInterfaceVersion()).thenReturn(5);
+ HintManagerService service = createService();
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroom(null);
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ });
+ assertThrows(UnsupportedOperationException.class, () -> {
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ });
+ }
+
+ @Test
+ public void testCpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getCpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal();
+ CpuHeadroomParams halParams1 = new CpuHeadroomParams();
+ halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN;
+ halParams1.selectionType = CpuHeadroomParams.SelectionType.ALL;
+ halParams1.pid = Process.myPid();
+
+ CpuHeadroomParamsInternal params2 = new CpuHeadroomParamsInternal();
+ params2.usesDeviceHeadroom = true;
+ params2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ params2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ CpuHeadroomParams halParams2 = new CpuHeadroomParams();
+ halParams2.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
+ halParams2.selectionType = CpuHeadroomParams.SelectionType.PER_CORE;
+ halParams2.pid = DEFAULT_HEADROOM_PID;
+
+ float[] headroom1 = new float[] {0.1f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float[] headroom2 = new float[] {0.1f, 0.5f};
+ when(mIPowerMock.getCpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getCpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getCpuHeadroomMinIntervalMillis();
+ service.getBinderServiceInstance().getCpuHeadroom(params1);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ service.getBinderServiceInstance().getCpuHeadroom(params2);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getCpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertArrayEquals(headroom1, service.getBinderServiceInstance().getCpuHeadroom(params1),
+ 0.01f);
+ assertArrayEquals(headroom2, service.getBinderServiceInstance().getCpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams2));
+ }
+
+ @Test
+ public void testGpuHeadroomCache() throws Exception {
+ when(mIPowerMock.getGpuHeadroomMinIntervalMillis()).thenReturn(2000L);
+ GpuHeadroomParamsInternal params1 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams1 = new GpuHeadroomParams();
+ halParams1.calculationType = GpuHeadroomParams.CalculationType.MIN;
+
+ GpuHeadroomParamsInternal params2 = new GpuHeadroomParamsInternal();
+ GpuHeadroomParams halParams2 = new GpuHeadroomParams();
+ params2.calculationType =
+ halParams2.calculationType = GpuHeadroomParams.CalculationType.AVERAGE;
+
+ float headroom1 = 0.1f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams1))).thenReturn(headroom1);
+ float headroom2 = 0.2f;
+ when(mIPowerMock.getGpuHeadroom(eq(halParams2))).thenReturn(headroom2);
+ HintManagerService service = createService();
+ clearInvocations(mIPowerMock);
+
+ service.getBinderServiceInstance().getGpuHeadroomMinIntervalMillis();
+ verify(mIPowerMock, times(0)).getGpuHeadroomMinIntervalMillis();
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+
+ // verify cache is working
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1 more second it should be served with cache still
+ Thread.sleep(1000);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 1.5 more second it should be served with cache still as timer reset
+ Thread.sleep(1500);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(0)).getGpuHeadroom(any());
+
+ // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval
+ Thread.sleep(2100);
+ clearInvocations(mIPowerMock);
+ assertEquals(headroom1, service.getBinderServiceInstance().getGpuHeadroom(params1),
+ 0.01f);
+ assertEquals(headroom2, service.getBinderServiceInstance().getGpuHeadroom(params2),
+ 0.01f);
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams1));
+ verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+ }
}