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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemService; 23 import android.annotation.TestApi; 24 import android.content.Context; 25 26 import com.android.internal.util.Preconditions; 27 28 import java.io.Closeable; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.lang.ref.Reference; 32 33 34 /** The PerformanceHintManager allows apps to send performance hint to system. */ 35 @SystemService(Context.PERFORMANCE_HINT_SERVICE) 36 public final class PerformanceHintManager { 37 private final long mNativeManagerPtr; 38 39 /** @hide */ create()40 public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException { 41 long nativeManagerPtr = nativeAcquireManager(); 42 if (nativeManagerPtr == 0) { 43 throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE); 44 } 45 return new PerformanceHintManager(nativeManagerPtr); 46 } 47 PerformanceHintManager(long nativeManagerPtr)48 private PerformanceHintManager(long nativeManagerPtr) { 49 mNativeManagerPtr = nativeManagerPtr; 50 } 51 52 /** 53 * Creates a {@link Session} for the given set of threads and sets their initial target work 54 * duration. 55 * 56 * @param tids The list of threads to be associated with this session. They must be part of 57 * this process' thread group. 58 * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new 59 * session. 60 * @return the new session if it is supported on this device, null if hint session is not 61 * supported on this device. 62 */ 63 @Nullable createHintSession(@onNull int[] tids, long initialTargetWorkDurationNanos)64 public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) { 65 Preconditions.checkNotNull(tids, "tids cannot be null"); 66 Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos, 67 "the hint target duration should be positive."); 68 long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids, 69 initialTargetWorkDurationNanos); 70 if (nativeSessionPtr == 0) return null; 71 return new Session(nativeSessionPtr); 72 } 73 74 /** 75 * Get preferred update rate information for this device. 76 * 77 * @return the preferred update rate supported by device software. 78 */ getPreferredUpdateRateNanos()79 public long getPreferredUpdateRateNanos() { 80 return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr); 81 } 82 83 /** 84 * A Session represents a group of threads with an inter-related workload such that hints for 85 * their performance should be considered as a unit. The threads in a given session should be 86 * long-life and not created or destroyed dynamically. 87 * 88 * <p>Each session is expected to have a periodic workload with a target duration for each 89 * cycle. The cycle duration is likely greater than the target work duration to allow other 90 * parts of the pipeline to run within the available budget. For example, a renderer thread may 91 * work at 60hz in order to produce frames at the display's frame but have a target work 92 * duration of only 6ms.</p> 93 * 94 * <p>Any call in this class will change its internal data, so you must do your own thread 95 * safety to protect from racing.</p> 96 * 97 * <p>Note that the target work duration can be {@link #updateTargetWorkDuration(long) updated} 98 * if workloads change.</p> 99 * 100 * <p>After each cycle of work, the client is expected to 101 * {@link #reportActualWorkDuration(long) report} the actual time taken to complete.</p> 102 * 103 * <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p> 104 */ 105 public static class Session implements Closeable { 106 private long mNativeSessionPtr; 107 108 /** @hide */ Session(long nativeSessionPtr)109 public Session(long nativeSessionPtr) { 110 mNativeSessionPtr = nativeSessionPtr; 111 } 112 113 /** 114 * This hint indicates a sudden increase in CPU workload intensity. It means 115 * that this hint session needs extra CPU resources immediately to meet the 116 * target duration for the current work cycle. 117 * 118 * @hide 119 */ 120 @TestApi 121 public static final int CPU_LOAD_UP = 0; 122 /** 123 * This hint indicates a decrease in CPU workload intensity. It means that 124 * this hint session can reduce CPU resources and still meet the target duration. 125 * 126 * @hide 127 */ 128 @TestApi 129 public static final int CPU_LOAD_DOWN = 1; 130 /** 131 * This hint indicates an upcoming CPU workload that is completely changed and 132 * unknown. It means that the hint session should reset CPU resources to a known 133 * baseline to prepare for an arbitrary load, and must wake up if inactive. 134 * 135 * @hide 136 */ 137 @TestApi 138 public static final int CPU_LOAD_RESET = 2; 139 /** 140 * This hint indicates that the most recent CPU workload is resuming after a 141 * period of inactivity. It means that the hint session should allocate similar 142 * CPU resources to what was used previously, and must wake up if inactive. 143 * 144 * @hide 145 */ 146 @TestApi 147 public static final int CPU_LOAD_RESUME = 3; 148 149 /** @hide */ 150 @Retention(RetentionPolicy.SOURCE) 151 @IntDef(prefix = {"CPU_LOAD_"}, value = { 152 CPU_LOAD_UP, 153 CPU_LOAD_DOWN, 154 CPU_LOAD_RESET, 155 CPU_LOAD_RESUME 156 }) 157 public @interface Hint {} 158 159 /** @hide */ 160 @Override finalize()161 protected void finalize() throws Throwable { 162 try { 163 close(); 164 } finally { 165 super.finalize(); 166 } 167 } 168 169 /** 170 * Updates this session's target duration for each cycle of work. 171 * 172 * @param targetDurationNanos the new desired duration in nanoseconds 173 */ updateTargetWorkDuration(long targetDurationNanos)174 public void updateTargetWorkDuration(long targetDurationNanos) { 175 Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration" 176 + " should be positive."); 177 nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos); 178 } 179 180 /** 181 * Reports the actual duration for the last cycle of work. 182 * 183 * <p>The system will attempt to adjust the core placement of the threads within the thread 184 * group and/or the frequency of the core on which they are run to bring the actual duration 185 * close to the target duration.</p> 186 * 187 * @param actualDurationNanos how long the thread group took to complete its last task in 188 * nanoseconds 189 */ reportActualWorkDuration(long actualDurationNanos)190 public void reportActualWorkDuration(long actualDurationNanos) { 191 Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should" 192 + " be positive."); 193 nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos); 194 } 195 196 /** 197 * Ends the current hint session. 198 * 199 * <p>Once called, you should not call anything else on this object.</p> 200 */ close()201 public void close() { 202 if (mNativeSessionPtr != 0) { 203 nativeCloseSession(mNativeSessionPtr); 204 mNativeSessionPtr = 0; 205 } 206 } 207 208 /** 209 * Sends performance hints to inform the hint session of changes in the workload. 210 * 211 * @param hint The hint to send to the session. 212 * 213 * @hide 214 */ 215 @TestApi sendHint(@int int hint)216 public void sendHint(@Hint int hint) { 217 Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least" 218 + " zero."); 219 try { 220 nativeSendHint(mNativeSessionPtr, hint); 221 } finally { 222 Reference.reachabilityFence(this); 223 } 224 } 225 226 /** 227 * Set a list of threads to the performance hint session. This operation will replace 228 * the current list of threads with the given list of threads. 229 * Note that this is not an oneway method. 230 * 231 * @param tids The list of threads to be associated with this session. They must be 232 * part of this app's thread group. 233 * 234 * @throws IllegalStateException if the hint session is not in the foreground. 235 * @throws IllegalArgumentException if the thread id list is empty. 236 * @throws SecurityException if any thread id doesn't belong to the application. 237 */ setThreads(@onNull int[] tids)238 public void setThreads(@NonNull int[] tids) { 239 if (mNativeSessionPtr == 0) { 240 return; 241 } 242 if (tids.length == 0) { 243 throw new IllegalArgumentException("Thread id list can't be empty."); 244 } 245 nativeSetThreads(mNativeSessionPtr, tids); 246 } 247 248 /** 249 * Returns the list of thread ids. 250 * 251 * @hide 252 */ 253 @TestApi getThreadIds()254 public @Nullable int[] getThreadIds() { 255 return nativeGetThreadIds(mNativeSessionPtr); 256 } 257 } 258 nativeAcquireManager()259 private static native long nativeAcquireManager(); nativeGetPreferredUpdateRateNanos(long nativeManagerPtr)260 private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr); nativeCreateSession(long nativeManagerPtr, int[] tids, long initialTargetWorkDurationNanos)261 private static native long nativeCreateSession(long nativeManagerPtr, 262 int[] tids, long initialTargetWorkDurationNanos); nativeGetThreadIds(long nativeSessionPtr)263 private static native int[] nativeGetThreadIds(long nativeSessionPtr); nativeUpdateTargetWorkDuration(long nativeSessionPtr, long targetDurationNanos)264 private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr, 265 long targetDurationNanos); nativeReportActualWorkDuration(long nativeSessionPtr, long actualDurationNanos)266 private static native void nativeReportActualWorkDuration(long nativeSessionPtr, 267 long actualDurationNanos); nativeCloseSession(long nativeSessionPtr)268 private static native void nativeCloseSession(long nativeSessionPtr); nativeSendHint(long nativeSessionPtr, int hint)269 private static native void nativeSendHint(long nativeSessionPtr, int hint); nativeSetThreads(long nativeSessionPtr, int[] tids)270 private static native void nativeSetThreads(long nativeSessionPtr, int[] tids); 271 } 272