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