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