diff options
| author | 2024-11-13 20:45:36 +0000 | |
|---|---|---|
| committer | 2024-11-13 20:45:36 +0000 | |
| commit | edff77959efa22e21b75f613dd2f326057eaf37f (patch) | |
| tree | c02cd3f4c7916729d7eccde1ed99439f1609b3fe | |
| parent | f13aa1d82636d7fbcd9b490c66add79f9d215bbf (diff) | |
| parent | e74bcd1c6929ff6d1c7fb43ad416d3a4cc695b9f (diff) | |
Merge "Add SDK support for CPU/GPU headroom APIs" into main
| -rw-r--r-- | core/api/current.txt | 20 | ||||
| -rw-r--r-- | core/java/Android.bp | 2 | ||||
| -rw-r--r-- | core/java/android/app/SystemServiceRegistry.java | 5 | ||||
| -rw-r--r-- | core/java/android/os/CpuHeadroomParams.java | 91 | ||||
| -rw-r--r-- | core/java/android/os/CpuHeadroomParamsInternal.aidl | 31 | ||||
| -rw-r--r-- | core/java/android/os/GpuHeadroomParams.java | 91 | ||||
| -rw-r--r-- | core/java/android/os/GpuHeadroomParamsInternal.aidl | 28 | ||||
| -rw-r--r-- | core/java/android/os/IHintManager.aidl | 6 | ||||
| -rw-r--r-- | core/java/android/os/flags.aconfig | 7 | ||||
| -rw-r--r-- | core/java/android/os/health/SystemHealthManager.java | 111 | ||||
| -rw-r--r-- | native/android/tests/performance_hint/PerformanceHintNativeTest.cpp | 12 | ||||
| -rw-r--r-- | services/core/java/com/android/server/power/hint/HintManagerService.java | 282 | ||||
| -rw-r--r-- | services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java | 186 |
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)); + } } |