1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.os; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemService; 24 import android.annotation.TestApi; 25 import android.content.Context; 26 27 import com.android.internal.util.Preconditions; 28 29 import java.io.Closeable; 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 import java.lang.ref.Reference; 33 import java.util.Objects; 34 35 36 /** The PerformanceHintManager allows apps to send performance hint to system. */ 37 @SystemService(Context.PERFORMANCE_HINT_SERVICE) 38 public final class PerformanceHintManager { 39 private final long mNativeManagerPtr; 40 41 /** @hide */ create()42 public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException { 43 long nativeManagerPtr = nativeAcquireManager(); 44 if (nativeManagerPtr == 0) { 45 throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE); 46 } 47 return new PerformanceHintManager(nativeManagerPtr); 48 } 49 PerformanceHintManager(long nativeManagerPtr)50 private PerformanceHintManager(long nativeManagerPtr) { 51 mNativeManagerPtr = nativeManagerPtr; 52 } 53 54 /** 55 * Get preferred update rate information for this device. 56 * 57 * @return the preferred update rate supported by device software 58 */ getPreferredUpdateRateNanos()59 public long getPreferredUpdateRateNanos() { 60 return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr); 61 } 62 63 /** 64 * Creates a {@link Session} for the given set of threads and sets their initial target work 65 * duration. 66 * 67 * @param tids The list of threads to be associated with this session. They must be part of 68 * this process' thread group 69 * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new 70 * session 71 * @return the new session if it is supported on this device, null if hint session is not 72 * supported on this device or the tid doesn't belong to the application 73 * @throws IllegalArgumentException if the thread id list is empty, or 74 * initialTargetWorkDurationNanos is non-positive 75 */ 76 @Nullable createHintSession(@onNull int[] tids, long initialTargetWorkDurationNanos)77 public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) { 78 Objects.requireNonNull(tids, "tids cannot be null"); 79 if (tids.length == 0) { 80 throw new IllegalArgumentException("thread id list can't be empty."); 81 } 82 Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos, 83 "the hint target duration should be positive."); 84 long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids, 85 initialTargetWorkDurationNanos); 86 if (nativeSessionPtr == 0) return null; 87 return new Session(nativeSessionPtr); 88 } 89 90 /** 91 * A Session represents a group of threads with an inter-related workload such that hints for 92 * their performance should be considered as a unit. The threads in a given session should be 93 * long-lived and not created or destroyed dynamically. 94 * 95 * The work duration API can be used with periodic workloads to dynamically adjust thread 96 * performance and keep the work on schedule while optimizing the available power budget. 97 * When using the work duration API, the starting target duration should be specified 98 * while creating the session, but can later be adjusted with 99 * {@link #updateTargetWorkDuration(long)}. While using the work duration API, the client is be 100 * expected to call {@link #reportActualWorkDuration(long)} each cycle to report the actual 101 * time taken to complete to the system. 102 * 103 * Any call in this class will change its internal data, so you must do your own thread 104 * safety to protect from racing. 105 * 106 * All timings should be in {@link SystemClock#uptimeNanos()}. 107 */ 108 public static class Session implements Closeable { 109 private long mNativeSessionPtr; 110 111 /** @hide */ Session(long nativeSessionPtr)112 public Session(long nativeSessionPtr) { 113 mNativeSessionPtr = nativeSessionPtr; 114 } 115 116 /** 117 * This hint indicates a sudden increase in CPU workload intensity. It means 118 * that this hint session needs extra CPU resources immediately to meet the 119 * target duration for the current work cycle. 120 * 121 * @hide 122 */ 123 @TestApi 124 public static final int CPU_LOAD_UP = 0; 125 /** 126 * This hint indicates a decrease in CPU workload intensity. It means that 127 * this hint session can reduce CPU resources and still meet the target duration. 128 * 129 * @hide 130 */ 131 @TestApi 132 public static final int CPU_LOAD_DOWN = 1; 133 /** 134 * This hint indicates an upcoming CPU workload that is completely changed and 135 * unknown. It means that the hint session should reset CPU resources to a known 136 * baseline to prepare for an arbitrary load, and must wake up if inactive. 137 * 138 * @hide 139 */ 140 @TestApi 141 public static final int CPU_LOAD_RESET = 2; 142 /** 143 * This hint indicates that the most recent CPU workload is resuming after a 144 * period of inactivity. It means that the hint session should allocate similar 145 * CPU resources to what was used previously, and must wake up if inactive. 146 * 147 * @hide 148 */ 149 @TestApi 150 public static final int CPU_LOAD_RESUME = 3; 151 152 /** 153 * This hint indicates an increase in GPU workload intensity. It means that 154 * this hint session needs extra GPU resources to meet the target duration. 155 * This hint must be sent before reporting the actual duration to the session. 156 * 157 * @hide 158 */ 159 @TestApi 160 @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) 161 public static final int GPU_LOAD_UP = 5; 162 163 /** 164 * This hint indicates a decrease in GPU workload intensity. It means that 165 * this hint session can reduce GPU resources and still meet the target duration. 166 * 167 * @hide 168 */ 169 @TestApi 170 @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) 171 public static final int GPU_LOAD_DOWN = 6; 172 173 /** 174 * This hint indicates an upcoming GPU workload that is completely changed and 175 * unknown. It means that the hint session should reset GPU resources to a known 176 * baseline to prepare for an arbitrary load, and must wake up if inactive. 177 * 178 * @hide 179 */ 180 @TestApi 181 @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) 182 public static final int GPU_LOAD_RESET = 7; 183 184 /** @hide */ 185 @Retention(RetentionPolicy.SOURCE) 186 @IntDef(prefix = {"CPU_LOAD_", "GPU_LOAD_"}, value = { 187 CPU_LOAD_UP, 188 CPU_LOAD_DOWN, 189 CPU_LOAD_RESET, 190 CPU_LOAD_RESUME, 191 GPU_LOAD_UP, 192 GPU_LOAD_DOWN, 193 GPU_LOAD_RESET 194 }) 195 public @interface Hint {} 196 197 /** @hide */ 198 @Override finalize()199 protected void finalize() throws Throwable { 200 try { 201 close(); 202 } finally { 203 super.finalize(); 204 } 205 } 206 207 /** 208 * Updates this session's target total duration for each cycle of work. 209 * 210 * @param targetDurationNanos the new desired duration in nanoseconds 211 */ updateTargetWorkDuration(long targetDurationNanos)212 public void updateTargetWorkDuration(long targetDurationNanos) { 213 Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration" 214 + " should be positive."); 215 nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos); 216 } 217 218 /** 219 * Reports the actual duration for the last cycle of work. 220 * 221 * The system will attempt to adjust the core placement of the threads within the thread 222 * group and/or the frequency of the core on which they are run to bring the actual duration 223 * close to the target duration. 224 * 225 * @param actualDurationNanos how long the thread group took to complete its last task in 226 * nanoseconds 227 */ reportActualWorkDuration(long actualDurationNanos)228 public void reportActualWorkDuration(long actualDurationNanos) { 229 Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should" 230 + " be positive."); 231 nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos); 232 } 233 234 /** 235 * Ends the current hint session. 236 * 237 * Once called, you should not call anything else on this object. 238 */ close()239 public void close() { 240 if (mNativeSessionPtr != 0) { 241 nativeCloseSession(mNativeSessionPtr); 242 mNativeSessionPtr = 0; 243 } 244 } 245 246 /** 247 * Sends performance hints to inform the hint session of changes in the workload. 248 * 249 * @param hint The hint to send to the session 250 * 251 * @hide 252 */ 253 @TestApi sendHint(@int int hint)254 public void sendHint(@Hint int hint) { 255 Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least" 256 + " zero."); 257 try { 258 nativeSendHint(mNativeSessionPtr, hint); 259 } finally { 260 Reference.reachabilityFence(this); 261 } 262 } 263 264 /** 265 * This tells the session that these threads can be 266 * safely scheduled to prefer power efficiency over performance. 267 * 268 * @param enabled The flag that sets whether this session uses power-efficient scheduling. 269 */ 270 @FlaggedApi(Flags.FLAG_ADPF_PREFER_POWER_EFFICIENCY) setPreferPowerEfficiency(boolean enabled)271 public void setPreferPowerEfficiency(boolean enabled) { 272 nativeSetPreferPowerEfficiency(mNativeSessionPtr, enabled); 273 } 274 275 /** 276 * Set a list of threads to the performance hint session. This operation will replace 277 * the current list of threads with the given list of threads. 278 * Note that this is not an oneway method. 279 * 280 * @param tids The list of threads to be associated with this session. They must be 281 * part of this app's thread group 282 * 283 * @throws IllegalStateException if the hint session is not in the foreground 284 * @throws IllegalArgumentException if the thread id list is empty 285 * @throws SecurityException if any thread id doesn't belong to the application 286 */ setThreads(@onNull int[] tids)287 public void setThreads(@NonNull int[] tids) { 288 if (mNativeSessionPtr == 0) { 289 return; 290 } 291 Objects.requireNonNull(tids, "tids cannot be null"); 292 if (tids.length == 0) { 293 throw new IllegalArgumentException("Thread id list can't be empty."); 294 } 295 nativeSetThreads(mNativeSessionPtr, tids); 296 } 297 298 /** 299 * Returns the list of thread ids. 300 * 301 * @hide 302 */ 303 @TestApi getThreadIds()304 public @Nullable int[] getThreadIds() { 305 return nativeGetThreadIds(mNativeSessionPtr); 306 } 307 308 /** 309 * Reports the work duration for the last cycle of work. 310 * 311 * The system will attempt to adjust the core placement of the threads within the thread 312 * group and/or the frequency of the core on which they are run to bring the actual duration 313 * close to the target duration. 314 * 315 * @param workDuration the work duration of each component. 316 * @throws IllegalArgumentException if 317 * the work period start timestamp or the total duration are less than or equal to zero, 318 * if either the actual CPU duration or actual GPU duration is less than zero, 319 * or if both the CPU and GPU durations are zero. 320 */ 321 @FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION) reportActualWorkDuration(@onNull WorkDuration workDuration)322 public void reportActualWorkDuration(@NonNull WorkDuration workDuration) { 323 if (workDuration.mWorkPeriodStartTimestampNanos <= 0) { 324 throw new IllegalArgumentException( 325 "the work period start timestamp should be greater than zero."); 326 } 327 if (workDuration.mActualTotalDurationNanos <= 0) { 328 throw new IllegalArgumentException( 329 "the actual total duration should be greater than zero."); 330 } 331 if (workDuration.mActualCpuDurationNanos < 0) { 332 throw new IllegalArgumentException( 333 "the actual CPU duration should be greater than or equal to zero."); 334 } 335 if (workDuration.mActualGpuDurationNanos < 0) { 336 throw new IllegalArgumentException( 337 "the actual GPU duration should be greater than or equal to zero."); 338 } 339 if (workDuration.mActualCpuDurationNanos + workDuration.mActualGpuDurationNanos <= 0) { 340 throw new IllegalArgumentException( 341 "either the actual CPU duration or the actual GPU duration should be greater" 342 + "than zero."); 343 } 344 nativeReportActualWorkDuration(mNativeSessionPtr, 345 workDuration.mWorkPeriodStartTimestampNanos, 346 workDuration.mActualTotalDurationNanos, 347 workDuration.mActualCpuDurationNanos, workDuration.mActualGpuDurationNanos); 348 } 349 } 350 nativeAcquireManager()351 private static native long nativeAcquireManager(); nativeGetPreferredUpdateRateNanos(long nativeManagerPtr)352 private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr); nativeCreateSession(long nativeManagerPtr, int[] tids, long initialTargetWorkDurationNanos)353 private static native long nativeCreateSession(long nativeManagerPtr, 354 int[] tids, long initialTargetWorkDurationNanos); nativeGetThreadIds(long nativeSessionPtr)355 private static native int[] nativeGetThreadIds(long nativeSessionPtr); nativeUpdateTargetWorkDuration(long nativeSessionPtr, long targetDurationNanos)356 private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr, 357 long targetDurationNanos); nativeReportActualWorkDuration(long nativeSessionPtr, long actualDurationNanos)358 private static native void nativeReportActualWorkDuration(long nativeSessionPtr, 359 long actualDurationNanos); nativeCloseSession(long nativeSessionPtr)360 private static native void nativeCloseSession(long nativeSessionPtr); nativeSendHint(long nativeSessionPtr, int hint)361 private static native void nativeSendHint(long nativeSessionPtr, int hint); nativeSetThreads(long nativeSessionPtr, int[] tids)362 private static native void nativeSetThreads(long nativeSessionPtr, int[] tids); nativeSetPreferPowerEfficiency(long nativeSessionPtr, boolean enabled)363 private static native void nativeSetPreferPowerEfficiency(long nativeSessionPtr, 364 boolean enabled); nativeReportActualWorkDuration(long nativeSessionPtr, long workPeriodStartTimestampNanos, long actualTotalDurationNanos, long actualCpuDurationNanos, long actualGpuDurationNanos)365 private static native void nativeReportActualWorkDuration(long nativeSessionPtr, 366 long workPeriodStartTimestampNanos, long actualTotalDurationNanos, 367 long actualCpuDurationNanos, long actualGpuDurationNanos); 368 } 369