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 #define LOG_TAG "perf_hint"
18 
19 #include <utility>
20 #include <vector>
21 
22 #include <android/os/IHintManager.h>
23 #include <android/os/IHintSession.h>
24 #include <binder/Binder.h>
25 #include <binder/IBinder.h>
26 #include <binder/IServiceManager.h>
27 #include <performance_hint_private.h>
28 #include <utils/SystemClock.h>
29 
30 using namespace android;
31 using namespace android::os;
32 
33 struct APerformanceHintSession;
34 
35 struct APerformanceHintManager {
36 public:
37     static APerformanceHintManager* getInstance();
38     APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos);
39     APerformanceHintManager() = delete;
40     ~APerformanceHintManager() = default;
41 
42     APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
43                                            int64_t initialTargetWorkDurationNanos);
44     int64_t getPreferredRateNanos() const;
45 
46 private:
47     static APerformanceHintManager* create(sp<IHintManager> iHintManager);
48 
49     sp<IHintManager> mHintManager;
50     const int64_t mPreferredRateNanos;
51 };
52 
53 struct APerformanceHintSession {
54 public:
55     APerformanceHintSession(sp<IHintSession> session, int64_t preferredRateNanos,
56                             int64_t targetDurationNanos);
57     APerformanceHintSession() = delete;
58     ~APerformanceHintSession();
59 
60     int updateTargetWorkDuration(int64_t targetDurationNanos);
61     int reportActualWorkDuration(int64_t actualDurationNanos);
62 
63 private:
64     friend struct APerformanceHintManager;
65 
66     sp<IHintSession> mHintSession;
67     // HAL preferred update rate
68     const int64_t mPreferredRateNanos;
69     // Target duration for choosing update rate
70     int64_t mTargetDurationNanos;
71     // Last update timestamp
72     int64_t mLastUpdateTimestamp;
73     // Cached samples
74     std::vector<int64_t> mActualDurationsNanos;
75     std::vector<int64_t> mTimestampsNanos;
76 };
77 
78 static IHintManager* gIHintManagerForTesting = nullptr;
79 static APerformanceHintManager* gHintManagerForTesting = nullptr;
80 
81 // ===================================== APerformanceHintManager implementation
82 APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
83                                                  int64_t preferredRateNanos)
84       : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {}
85 
86 APerformanceHintManager* APerformanceHintManager::getInstance() {
87     if (gHintManagerForTesting) return gHintManagerForTesting;
88     if (gIHintManagerForTesting) {
89         APerformanceHintManager* manager = create(gIHintManagerForTesting);
90         gIHintManagerForTesting = nullptr;
91         return manager;
92     }
93     static APerformanceHintManager* instance = create(nullptr);
94     return instance;
95 }
96 
97 APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) {
98     if (!manager) {
99         manager = interface_cast<IHintManager>(
100                 defaultServiceManager()->checkService(String16("performance_hint")));
101     }
102     if (manager == nullptr) {
103         ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
104         return nullptr;
105     }
106     int64_t preferredRateNanos = -1L;
107     binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
108     if (!ret.isOk()) {
109         ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__,
110               ret.exceptionMessage().c_str());
111         return nullptr;
112     }
113     if (preferredRateNanos <= 0) {
114         preferredRateNanos = -1L;
115     }
116     return new APerformanceHintManager(std::move(manager), preferredRateNanos);
117 }
118 
119 APerformanceHintSession* APerformanceHintManager::createSession(
120         const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
121     sp<IBinder> token = sp<BBinder>::make();
122     std::vector<int32_t> tids(threadIds, threadIds + size);
123     sp<IHintSession> session;
124     binder::Status ret =
125             mHintManager->createHintSession(token, tids, initialTargetWorkDurationNanos, &session);
126     if (!ret.isOk() || !session) {
127         return nullptr;
128     }
129     return new APerformanceHintSession(std::move(session), mPreferredRateNanos,
130                                        initialTargetWorkDurationNanos);
131 }
132 
133 int64_t APerformanceHintManager::getPreferredRateNanos() const {
134     return mPreferredRateNanos;
135 }
136 
137 // ===================================== APerformanceHintSession implementation
138 
139 APerformanceHintSession::APerformanceHintSession(sp<IHintSession> session,
140                                                  int64_t preferredRateNanos,
141                                                  int64_t targetDurationNanos)
142       : mHintSession(std::move(session)),
143         mPreferredRateNanos(preferredRateNanos),
144         mTargetDurationNanos(targetDurationNanos),
145         mLastUpdateTimestamp(elapsedRealtimeNano()) {}
146 
147 APerformanceHintSession::~APerformanceHintSession() {
148     binder::Status ret = mHintSession->close();
149     if (!ret.isOk()) {
150         ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
151     }
152 }
153 
154 int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
155     if (targetDurationNanos <= 0) {
156         ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
157         return EINVAL;
158     }
159     binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
160     if (!ret.isOk()) {
161         ALOGE("%s: HintSessionn updateTargetWorkDuration failed: %s", __FUNCTION__,
162               ret.exceptionMessage().c_str());
163         return EPIPE;
164     }
165     mTargetDurationNanos = targetDurationNanos;
166     /**
167      * Most of the workload is target_duration dependent, so now clear the cached samples
168      * as they are most likely obsolete.
169      */
170     mActualDurationsNanos.clear();
171     mTimestampsNanos.clear();
172     mLastUpdateTimestamp = elapsedRealtimeNano();
173     return 0;
174 }
175 
176 int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) {
177     if (actualDurationNanos <= 0) {
178         ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
179         return EINVAL;
180     }
181     int64_t now = elapsedRealtimeNano();
182     mActualDurationsNanos.push_back(actualDurationNanos);
183     mTimestampsNanos.push_back(now);
184 
185     /**
186      * Use current sample to determine the rate limit. We can pick a shorter rate limit
187      * if any sample underperformed, however, it could be the lower level system is slow
188      * to react. So here we explicitly choose the rate limit with the latest sample.
189      */
190     int64_t rateLimit = actualDurationNanos > mTargetDurationNanos ? mPreferredRateNanos
191                                                                    : 10 * mPreferredRateNanos;
192     if (now - mLastUpdateTimestamp <= rateLimit) return 0;
193 
194     binder::Status ret =
195             mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
196     mActualDurationsNanos.clear();
197     mTimestampsNanos.clear();
198     if (!ret.isOk()) {
199         ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
200               ret.exceptionMessage().c_str());
201         return EPIPE;
202     }
203     mLastUpdateTimestamp = now;
204     return 0;
205 }
206 
207 // ===================================== C API
208 APerformanceHintManager* APerformanceHint_getManager() {
209     return APerformanceHintManager::getInstance();
210 }
211 
212 APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
213                                                         const int32_t* threadIds, size_t size,
214                                                         int64_t initialTargetWorkDurationNanos) {
215     return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
216 }
217 
218 int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
219     return manager->getPreferredRateNanos();
220 }
221 
222 int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
223                                               int64_t targetDurationNanos) {
224     return session->updateTargetWorkDuration(targetDurationNanos);
225 }
226 
227 int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
228                                               int64_t actualDurationNanos) {
229     return session->reportActualWorkDuration(actualDurationNanos);
230 }
231 
232 void APerformanceHint_closeSession(APerformanceHintSession* session) {
233     delete session;
234 }
235 
236 void APerformanceHint_setIHintManagerForTesting(void* iManager) {
237     delete gHintManagerForTesting;
238     gHintManagerForTesting = nullptr;
239     gIHintManagerForTesting = static_cast<IHintManager*>(iManager);
240 }
241