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 <aidl/android/hardware/power/SessionHint.h>
20 #include <android/os/IHintManager.h>
21 #include <android/os/IHintSession.h>
22 #include <android/performance_hint.h>
23 #include <binder/Binder.h>
24 #include <binder/IBinder.h>
25 #include <binder/IServiceManager.h>
26 #include <performance_hint_private.h>
27 #include <utils/SystemClock.h>
28 
29 #include <chrono>
30 #include <utility>
31 #include <vector>
32 
33 using namespace android;
34 using namespace android::os;
35 
36 using namespace std::chrono_literals;
37 
38 using AidlSessionHint = aidl::android::hardware::power::SessionHint;
39 
40 struct APerformanceHintSession;
41 
42 constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
43 
44 struct APerformanceHintManager {
45 public:
46     static APerformanceHintManager* getInstance();
47     APerformanceHintManager(sp<IHintManager> service, int64_t preferredRateNanos);
48     APerformanceHintManager() = delete;
49     ~APerformanceHintManager() = default;
50 
51     APerformanceHintSession* createSession(const int32_t* threadIds, size_t size,
52                                            int64_t initialTargetWorkDurationNanos);
53     int64_t getPreferredRateNanos() const;
54 
55 private:
56     static APerformanceHintManager* create(sp<IHintManager> iHintManager);
57 
58     sp<IHintManager> mHintManager;
59     const sp<IBinder> mToken = sp<BBinder>::make();
60     const int64_t mPreferredRateNanos;
61 };
62 
63 struct APerformanceHintSession {
64 public:
65     APerformanceHintSession(sp<IHintManager> hintManager, sp<IHintSession> session,
66                             int64_t preferredRateNanos, int64_t targetDurationNanos);
67     APerformanceHintSession() = delete;
68     ~APerformanceHintSession();
69 
70     int updateTargetWorkDuration(int64_t targetDurationNanos);
71     int reportActualWorkDuration(int64_t actualDurationNanos);
72     int sendHint(SessionHint hint);
73     int setThreads(const int32_t* threadIds, size_t size);
74     int getThreadIds(int32_t* const threadIds, size_t* size);
75 
76 private:
77     friend struct APerformanceHintManager;
78 
79     sp<IHintManager> mHintManager;
80     sp<IHintSession> mHintSession;
81     // HAL preferred update rate
82     const int64_t mPreferredRateNanos;
83     // Target duration for choosing update rate
84     int64_t mTargetDurationNanos;
85     // First target hit timestamp
86     int64_t mFirstTargetMetTimestamp;
87     // Last target hit timestamp
88     int64_t mLastTargetMetTimestamp;
89     // Last hint reported from sendHint indexed by hint value
90     std::vector<int64_t> mLastHintSentTimestamp;
91     // Cached samples
92     std::vector<int64_t> mActualDurationsNanos;
93     std::vector<int64_t> mTimestampsNanos;
94 };
95 
96 static IHintManager* gIHintManagerForTesting = nullptr;
97 static APerformanceHintManager* gHintManagerForTesting = nullptr;
98 
99 // ===================================== APerformanceHintManager implementation
APerformanceHintManager(sp<IHintManager> manager,int64_t preferredRateNanos)100 APerformanceHintManager::APerformanceHintManager(sp<IHintManager> manager,
101                                                  int64_t preferredRateNanos)
102       : mHintManager(std::move(manager)), mPreferredRateNanos(preferredRateNanos) {}
103 
getInstance()104 APerformanceHintManager* APerformanceHintManager::getInstance() {
105     if (gHintManagerForTesting) return gHintManagerForTesting;
106     if (gIHintManagerForTesting) {
107         APerformanceHintManager* manager = create(gIHintManagerForTesting);
108         gIHintManagerForTesting = nullptr;
109         return manager;
110     }
111     static APerformanceHintManager* instance = create(nullptr);
112     return instance;
113 }
114 
create(sp<IHintManager> manager)115 APerformanceHintManager* APerformanceHintManager::create(sp<IHintManager> manager) {
116     if (!manager) {
117         manager = interface_cast<IHintManager>(
118                 defaultServiceManager()->checkService(String16("performance_hint")));
119     }
120     if (manager == nullptr) {
121         ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
122         return nullptr;
123     }
124     int64_t preferredRateNanos = -1L;
125     binder::Status ret = manager->getHintSessionPreferredRate(&preferredRateNanos);
126     if (!ret.isOk()) {
127         ALOGE("%s: PerformanceHint cannot get preferred rate. %s", __FUNCTION__,
128               ret.exceptionMessage().c_str());
129         return nullptr;
130     }
131     if (preferredRateNanos <= 0) {
132         preferredRateNanos = -1L;
133     }
134     return new APerformanceHintManager(std::move(manager), preferredRateNanos);
135 }
136 
createSession(const int32_t * threadIds,size_t size,int64_t initialTargetWorkDurationNanos)137 APerformanceHintSession* APerformanceHintManager::createSession(
138         const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos) {
139     std::vector<int32_t> tids(threadIds, threadIds + size);
140     sp<IHintSession> session;
141     binder::Status ret =
142             mHintManager->createHintSession(mToken, tids, initialTargetWorkDurationNanos, &session);
143     if (!ret.isOk() || !session) {
144         return nullptr;
145     }
146     return new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
147                                        initialTargetWorkDurationNanos);
148 }
149 
getPreferredRateNanos() const150 int64_t APerformanceHintManager::getPreferredRateNanos() const {
151     return mPreferredRateNanos;
152 }
153 
154 // ===================================== APerformanceHintSession implementation
155 
APerformanceHintSession(sp<IHintManager> hintManager,sp<IHintSession> session,int64_t preferredRateNanos,int64_t targetDurationNanos)156 APerformanceHintSession::APerformanceHintSession(sp<IHintManager> hintManager,
157                                                  sp<IHintSession> session,
158                                                  int64_t preferredRateNanos,
159                                                  int64_t targetDurationNanos)
160       : mHintManager(hintManager),
161         mHintSession(std::move(session)),
162         mPreferredRateNanos(preferredRateNanos),
163         mTargetDurationNanos(targetDurationNanos),
164         mFirstTargetMetTimestamp(0),
165         mLastTargetMetTimestamp(0) {
166     const std::vector<AidlSessionHint> sessionHintRange{ndk::enum_range<AidlSessionHint>().begin(),
167                                                         ndk::enum_range<AidlSessionHint>().end()};
168 
169     mLastHintSentTimestamp = std::vector<int64_t>(sessionHintRange.size(), 0);
170 }
171 
~APerformanceHintSession()172 APerformanceHintSession::~APerformanceHintSession() {
173     binder::Status ret = mHintSession->close();
174     if (!ret.isOk()) {
175         ALOGE("%s: HintSession close failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
176     }
177 }
178 
updateTargetWorkDuration(int64_t targetDurationNanos)179 int APerformanceHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
180     if (targetDurationNanos <= 0) {
181         ALOGE("%s: targetDurationNanos must be positive", __FUNCTION__);
182         return EINVAL;
183     }
184     binder::Status ret = mHintSession->updateTargetWorkDuration(targetDurationNanos);
185     if (!ret.isOk()) {
186         ALOGE("%s: HintSession updateTargetWorkDuration failed: %s", __FUNCTION__,
187               ret.exceptionMessage().c_str());
188         return EPIPE;
189     }
190     mTargetDurationNanos = targetDurationNanos;
191     /**
192      * Most of the workload is target_duration dependent, so now clear the cached samples
193      * as they are most likely obsolete.
194      */
195     mActualDurationsNanos.clear();
196     mTimestampsNanos.clear();
197     mFirstTargetMetTimestamp = 0;
198     mLastTargetMetTimestamp = 0;
199     return 0;
200 }
201 
reportActualWorkDuration(int64_t actualDurationNanos)202 int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNanos) {
203     if (actualDurationNanos <= 0) {
204         ALOGE("%s: actualDurationNanos must be positive", __FUNCTION__);
205         return EINVAL;
206     }
207     int64_t now = elapsedRealtimeNano();
208     mActualDurationsNanos.push_back(actualDurationNanos);
209     mTimestampsNanos.push_back(now);
210 
211     if (actualDurationNanos >= mTargetDurationNanos) {
212         // Reset timestamps if we are equal or over the target.
213         mFirstTargetMetTimestamp = 0;
214     } else {
215         // Set mFirstTargetMetTimestamp for first time meeting target.
216         if (!mFirstTargetMetTimestamp || !mLastTargetMetTimestamp ||
217             (now - mLastTargetMetTimestamp > 2 * mPreferredRateNanos)) {
218             mFirstTargetMetTimestamp = now;
219         }
220         /**
221          * Rate limit the change if the update is over mPreferredRateNanos since first
222          * meeting target and less than mPreferredRateNanos since last meeting target.
223          */
224         if (now - mFirstTargetMetTimestamp > mPreferredRateNanos &&
225             now - mLastTargetMetTimestamp <= mPreferredRateNanos) {
226             return 0;
227         }
228         mLastTargetMetTimestamp = now;
229     }
230 
231     binder::Status ret =
232             mHintSession->reportActualWorkDuration(mActualDurationsNanos, mTimestampsNanos);
233     if (!ret.isOk()) {
234         ALOGE("%s: HintSession reportActualWorkDuration failed: %s", __FUNCTION__,
235               ret.exceptionMessage().c_str());
236         mFirstTargetMetTimestamp = 0;
237         mLastTargetMetTimestamp = 0;
238         return EPIPE;
239     }
240     mActualDurationsNanos.clear();
241     mTimestampsNanos.clear();
242 
243     return 0;
244 }
245 
sendHint(SessionHint hint)246 int APerformanceHintSession::sendHint(SessionHint hint) {
247     if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
248         ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
249         return EINVAL;
250     }
251     int64_t now = elapsedRealtimeNano();
252 
253     // Limit sendHint to a pre-detemined rate for safety
254     if (now < (mLastHintSentTimestamp[hint] + SEND_HINT_TIMEOUT)) {
255         return 0;
256     }
257 
258     binder::Status ret = mHintSession->sendHint(hint);
259 
260     if (!ret.isOk()) {
261         ALOGE("%s: HintSession sendHint failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
262         return EPIPE;
263     }
264     mLastHintSentTimestamp[hint] = now;
265     return 0;
266 }
267 
setThreads(const int32_t * threadIds,size_t size)268 int APerformanceHintSession::setThreads(const int32_t* threadIds, size_t size) {
269     if (size == 0) {
270         ALOGE("%s: the list of thread ids must not be empty.", __FUNCTION__);
271         return EINVAL;
272     }
273     std::vector<int32_t> tids(threadIds, threadIds + size);
274     binder::Status ret = mHintManager->setHintSessionThreads(mHintSession, tids);
275     if (!ret.isOk()) {
276         ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
277         if (ret.exceptionCode() == binder::Status::Exception::EX_SECURITY ||
278             ret.exceptionCode() == binder::Status::Exception::EX_ILLEGAL_ARGUMENT) {
279             return EINVAL;
280         }
281         return EPIPE;
282     }
283     return 0;
284 }
285 
getThreadIds(int32_t * const threadIds,size_t * size)286 int APerformanceHintSession::getThreadIds(int32_t* const threadIds, size_t* size) {
287     std::vector<int32_t> tids;
288     binder::Status ret = mHintManager->getHintSessionThreadIds(mHintSession, &tids);
289     if (!ret.isOk()) {
290         ALOGE("%s: failed: %s", __FUNCTION__, ret.exceptionMessage().c_str());
291         return EPIPE;
292     }
293 
294     // When threadIds is nullptr, this is the first call to determine the size
295     // of the thread ids list.
296     if (threadIds == nullptr) {
297         *size = tids.size();
298         return 0;
299     }
300 
301     // Second call to return the actual list of thread ids.
302     *size = tids.size();
303     for (size_t i = 0; i < *size; ++i) {
304         threadIds[i] = tids[i];
305     }
306     return 0;
307 }
308 
309 // ===================================== C API
APerformanceHint_getManager()310 APerformanceHintManager* APerformanceHint_getManager() {
311     return APerformanceHintManager::getInstance();
312 }
313 
APerformanceHint_createSession(APerformanceHintManager * manager,const int32_t * threadIds,size_t size,int64_t initialTargetWorkDurationNanos)314 APerformanceHintSession* APerformanceHint_createSession(APerformanceHintManager* manager,
315                                                         const int32_t* threadIds, size_t size,
316                                                         int64_t initialTargetWorkDurationNanos) {
317     return manager->createSession(threadIds, size, initialTargetWorkDurationNanos);
318 }
319 
APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager * manager)320 int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) {
321     return manager->getPreferredRateNanos();
322 }
323 
APerformanceHint_updateTargetWorkDuration(APerformanceHintSession * session,int64_t targetDurationNanos)324 int APerformanceHint_updateTargetWorkDuration(APerformanceHintSession* session,
325                                               int64_t targetDurationNanos) {
326     return session->updateTargetWorkDuration(targetDurationNanos);
327 }
328 
APerformanceHint_reportActualWorkDuration(APerformanceHintSession * session,int64_t actualDurationNanos)329 int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session,
330                                               int64_t actualDurationNanos) {
331     return session->reportActualWorkDuration(actualDurationNanos);
332 }
333 
APerformanceHint_closeSession(APerformanceHintSession * session)334 void APerformanceHint_closeSession(APerformanceHintSession* session) {
335     delete session;
336 }
337 
APerformanceHint_sendHint(void * session,SessionHint hint)338 int APerformanceHint_sendHint(void* session, SessionHint hint) {
339     return reinterpret_cast<APerformanceHintSession*>(session)->sendHint(hint);
340 }
341 
APerformanceHint_setThreads(APerformanceHintSession * session,const pid_t * threadIds,size_t size)342 int APerformanceHint_setThreads(APerformanceHintSession* session, const pid_t* threadIds,
343                                 size_t size) {
344     if (session == nullptr) {
345         return EINVAL;
346     }
347     return session->setThreads(threadIds, size);
348 }
349 
APerformanceHint_getThreadIds(void * aPerformanceHintSession,int32_t * const threadIds,size_t * const size)350 int APerformanceHint_getThreadIds(void* aPerformanceHintSession, int32_t* const threadIds,
351                                   size_t* const size) {
352     if (aPerformanceHintSession == nullptr) {
353         return EINVAL;
354     }
355     return static_cast<APerformanceHintSession*>(aPerformanceHintSession)
356             ->getThreadIds(threadIds, size);
357 }
358 
APerformanceHint_setIHintManagerForTesting(void * iManager)359 void APerformanceHint_setIHintManagerForTesting(void* iManager) {
360     delete gHintManagerForTesting;
361     gHintManagerForTesting = nullptr;
362     gIHintManagerForTesting = static_cast<IHintManager*>(iManager);
363 }
364