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.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemService;
22 import android.content.Context;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.io.Closeable;
27 
28 /** The PerformanceHintManager allows apps to send performance hint to system. */
29 @SystemService(Context.PERFORMANCE_HINT_SERVICE)
30 public final class PerformanceHintManager {
31     private final long mNativeManagerPtr;
32 
33     /** @hide */
create()34     public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException {
35         long nativeManagerPtr = nativeAcquireManager();
36         if (nativeManagerPtr == 0) {
37             throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE);
38         }
39         return new PerformanceHintManager(nativeManagerPtr);
40     }
41 
PerformanceHintManager(long nativeManagerPtr)42     private PerformanceHintManager(long nativeManagerPtr) {
43         mNativeManagerPtr = nativeManagerPtr;
44     }
45 
46     /**
47      * Creates a {@link Session} for the given set of threads and sets their initial target work
48      * duration.
49      *
50      * @param tids The list of threads to be associated with this session. They must be part of
51      *     this process' thread group.
52      * @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new
53      *     session.
54      * @return the new session if it is supported on this device, null if hint session is not
55      *     supported on this device.
56      */
57     @Nullable
createHintSession(@onNull int[] tids, long initialTargetWorkDurationNanos)58     public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
59         Preconditions.checkNotNull(tids, "tids cannot be null");
60         Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos,
61                 "the hint target duration should be positive.");
62         long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids,
63                 initialTargetWorkDurationNanos);
64         if (nativeSessionPtr == 0) return null;
65         return new Session(nativeSessionPtr);
66     }
67 
68     /**
69      * Get preferred update rate information for this device.
70      *
71      * @return the preferred update rate supported by device software.
72      */
getPreferredUpdateRateNanos()73     public long getPreferredUpdateRateNanos() {
74         return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
75     }
76 
77     /**
78      * A Session represents a group of threads with an inter-related workload such that hints for
79      * their performance should be considered as a unit. The threads in a given session should be
80      * long-life and not created or destroyed dynamically.
81      *
82      * <p>Each session is expected to have a periodic workload with a target duration for each
83      * cycle. The cycle duration is likely greater than the target work duration to allow other
84      * parts of the pipeline to run within the available budget. For example, a renderer thread may
85      * work at 60hz in order to produce frames at the display's frame but have a target work
86      * duration of only 6ms.</p>
87      *
88      * <p>Any call in this class will change its internal data, so you must do your own thread
89      * safety to protect from racing.</p>
90      *
91      * <p>Note that the target work duration can be {@link #updateTargetWorkDuration(long) updated}
92      * if workloads change.</p>
93      *
94      * <p>After each cycle of work, the client is expected to
95      * {@link #reportActualWorkDuration(long) report} the actual time taken to complete.</p>
96      *
97      * <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
98      */
99     public static class Session implements Closeable {
100         private long mNativeSessionPtr;
101 
102         /** @hide */
Session(long nativeSessionPtr)103         public Session(long nativeSessionPtr) {
104             mNativeSessionPtr = nativeSessionPtr;
105         }
106 
107         /** @hide */
108         @Override
finalize()109         protected void finalize() throws Throwable {
110             try {
111                 close();
112             } finally {
113                 super.finalize();
114             }
115         }
116 
117         /**
118          * Updates this session's target duration for each cycle of work.
119          *
120          * @param targetDurationNanos the new desired duration in nanoseconds
121          */
updateTargetWorkDuration(long targetDurationNanos)122         public void updateTargetWorkDuration(long targetDurationNanos) {
123             Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration"
124                     + " should be positive.");
125             nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos);
126         }
127 
128         /**
129          * Reports the actual duration for the last cycle of work.
130          *
131          * <p>The system will attempt to adjust the core placement of the threads within the thread
132          * group and/or the frequency of the core on which they are run to bring the actual duration
133          * close to the target duration.</p>
134          *
135          * @param actualDurationNanos how long the thread group took to complete its last task in
136          *     nanoseconds
137          */
reportActualWorkDuration(long actualDurationNanos)138         public void reportActualWorkDuration(long actualDurationNanos) {
139             Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should"
140                     + " be positive.");
141             nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos);
142         }
143 
144         /**
145          * Ends the current hint session.
146          *
147          * <p>Once called, you should not call anything else on this object.</p>
148          */
close()149         public void close() {
150             if (mNativeSessionPtr != 0) {
151                 nativeCloseSession(mNativeSessionPtr);
152                 mNativeSessionPtr = 0;
153             }
154         }
155     }
156 
nativeAcquireManager()157     private static native long nativeAcquireManager();
nativeGetPreferredUpdateRateNanos(long nativeManagerPtr)158     private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr);
nativeCreateSession(long nativeManagerPtr, int[] tids, long initialTargetWorkDurationNanos)159     private static native long nativeCreateSession(long nativeManagerPtr,
160             int[] tids, long initialTargetWorkDurationNanos);
nativeUpdateTargetWorkDuration(long nativeSessionPtr, long targetDurationNanos)161     private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr,
162             long targetDurationNanos);
nativeReportActualWorkDuration(long nativeSessionPtr, long actualDurationNanos)163     private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
164             long actualDurationNanos);
nativeCloseSession(long nativeSessionPtr)165     private static native void nativeCloseSession(long nativeSessionPtr);
166 }
167