• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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