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