1 /*
2  * Copyright 2020 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 "carwatchdogd_ioperf_test"
18 
19 #include "LooperStub.h"
20 
21 #include <android-base/chrono_utils.h>
22 #include <utils/Looper.h>
23 
24 #include <future>
25 
26 namespace android {
27 namespace automotive {
28 namespace watchdog {
29 namespace testing {
30 
31 using android::base::Error;
32 using android::base::Result;
33 
34 // As the messages, which are to be polled immediately, are enqueued in the underlying looper
35 // handler before calling its poll method, the looper handler doesn't have to wait for any new
36 // messages.
37 const std::chrono::milliseconds kLooperPollTimeout = 0ms;
38 
39 // Maximum timeout before giving up on the underlying looper handler. This doesn't block the test
40 // as long as the underlying looper handler processes the enqueued messages quickly and updates
41 // |mShouldPoll|.
42 const std::chrono::milliseconds kStubPollCheckTimeout = 3min;
43 
pollAll(int)44 int LooperStub::pollAll(int /*timeoutMillis*/) {
45     {
46         Mutex::Autolock lock(mMutex);
47         if (!mShouldPoll) {
48             return 0;
49         }
50         mElapsedTime = mTimer;
51         while (!mCache.empty() && mCache.front().empty()) {
52             mTimer += 1s;  // Each empty entry in the cache is a second elapsed.
53             mCache.erase(mCache.begin());
54         }
55         mElapsedTime = mTimer - mElapsedTime;
56         if (mCache.empty()) {
57             mShouldPoll = false;
58             return 0;
59         }
60         // Send messages from the top of the cache and poll them immediately.
61         const auto messages = mCache.front();
62         for (const auto& m : messages) {
63             mLooper->sendMessage(mHandler, m);
64         }
65         mCache.erase(mCache.begin());
66     }
67     int result = mLooper->pollAll(kLooperPollTimeout.count());
68     Mutex::Autolock lock(mMutex);
69     mShouldPoll = false;
70     return result;
71 }
72 
sendMessage(const android::sp<MessageHandler> & handler,const Message & message)73 void LooperStub::sendMessage(const android::sp<MessageHandler>& handler, const Message& message) {
74     sendMessageAtTime(now(), handler, message);
75 }
76 
sendMessageAtTime(nsecs_t uptime,const android::sp<MessageHandler> & handler,const Message & message)77 void LooperStub::sendMessageAtTime(nsecs_t uptime, const android::sp<MessageHandler>& handler,
78                                    const Message& message) {
79     Mutex::Autolock lock(mMutex);
80     mHandler = handler;
81     nsecs_t uptimeDelay = uptime - now();
82     size_t pos = static_cast<size_t>(ns2s(uptimeDelay));
83     while (mCache.size() < pos + 1) {
84         mCache.emplace_back(LooperStub::CacheEntry());
85     }
86     mCache[pos].emplace_back(message);
87 }
88 
removeMessages(const android::sp<MessageHandler> & handler)89 void LooperStub::removeMessages(const android::sp<MessageHandler>& handler) {
90     Mutex::Autolock lock(mMutex);
91     mCache.clear();
92     mLooper->removeMessages(handler);
93 }
94 
pollCache()95 Result<void> LooperStub::pollCache() {
96     {
97         Mutex::Autolock lock(mMutex);
98         mShouldPoll = true;
99     }
100     auto checkPollCompleted = std::async([&]() {
101         bool shouldPoll = true;
102         while (shouldPoll) {
103             Mutex::Autolock lock(mMutex);
104             shouldPoll = mShouldPoll;
105         }
106     });
107     if (checkPollCompleted.wait_for(kStubPollCheckTimeout) != std::future_status::ready) {
108         mShouldPoll = false;
109         return Error() << "Poll didn't complete before " << kStubPollCheckTimeout.count()
110                        << " milliseconds";
111     }
112     return {};
113 }
114 
115 }  // namespace testing
116 }  // namespace watchdog
117 }  // namespace automotive
118 }  // namespace android
119