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