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 #include "RecurrentTimer.h"
18
19 #include <utils/Log.h>
20 #include <utils/Looper.h>
21 #include <utils/SystemClock.h>
22
23 #include <inttypes.h>
24 #include <math.h>
25
26 namespace android {
27 namespace hardware {
28 namespace automotive {
29 namespace vehicle {
30
31 namespace {
32
33 using ::android::base::ScopedLockAssertion;
34
35 constexpr int INVALID_ID = -1;
36
37 } // namespace
38
RecurrentTimer()39 RecurrentTimer::RecurrentTimer() {
40 mHandler = sp<RecurrentMessageHandler>::make(this);
41 mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
42 mThread = std::thread([this] {
43 Looper::setForThread(mLooper);
44
45 while (!mStopRequested) {
46 mLooper->pollOnce(/*timeoutMillis=*/-1);
47 }
48 });
49 }
50
~RecurrentTimer()51 RecurrentTimer::~RecurrentTimer() {
52 mStopRequested = true;
53 mLooper->removeMessages(mHandler);
54 mLooper->wake();
55 if (mThread.joinable()) {
56 mThread.join();
57 }
58 }
59
getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback)60 int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
61 const auto& it = mIdByCallback.find(callback);
62 if (it != mIdByCallback.end()) {
63 return it->second;
64 }
65 return INVALID_ID;
66 }
67
registerTimerCallback(int64_t intervalInNanos,std::shared_ptr<RecurrentTimer::Callback> callback)68 void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
69 std::shared_ptr<RecurrentTimer::Callback> callback) {
70 {
71 std::scoped_lock<std::mutex> lockGuard(mLock);
72
73 int callbackId = getCallbackIdLocked(callback);
74
75 if (callbackId == INVALID_ID) {
76 callbackId = mCallbackId++;
77 mIdByCallback.insert({callback, callbackId});
78 } else {
79 ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
80 " ns, new: %" PRId64 " ns",
81 mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
82 mLooper->removeMessages(mHandler, callbackId);
83 }
84
85 // Aligns the nextTime to multiply of interval.
86 int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
87
88 std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
89 info->callback = callback;
90 info->intervalInNanos = intervalInNanos;
91 info->nextTimeInNanos = nextTimeInNanos;
92 mCallbackInfoById.insert({callbackId, std::move(info)});
93
94 mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
95 }
96 }
97
unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback)98 void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
99 {
100 std::scoped_lock<std::mutex> lockGuard(mLock);
101
102 int callbackId = getCallbackIdLocked(callback);
103
104 if (callbackId == INVALID_ID) {
105 ALOGE("No event found to unregister");
106 return;
107 }
108
109 mLooper->removeMessages(mHandler, callbackId);
110 mCallbackInfoById.erase(callbackId);
111 mIdByCallback.erase(callback);
112 }
113 }
114
handleMessage(const Message & message)115 void RecurrentTimer::handleMessage(const Message& message) {
116 std::shared_ptr<RecurrentTimer::Callback> callback;
117 {
118 std::scoped_lock<std::mutex> lockGuard(mLock);
119
120 int callbackId = message.what;
121
122 auto it = mCallbackInfoById.find(callbackId);
123 if (it == mCallbackInfoById.end()) {
124 ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
125 return;
126 }
127
128 CallbackInfo* callbackInfo = it->second.get();
129 callback = callbackInfo->callback;
130 int64_t nowNanos = uptimeNanos();
131 // intervalCount is the number of interval we have to advance until we pass now.
132 size_t intervalCount =
133 (nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
134 callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
135
136 mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
137 }
138
139 (*callback)();
140 }
141
handleMessage(const Message & message)142 void RecurrentMessageHandler::handleMessage(const Message& message) {
143 mTimer->handleMessage(message);
144 }
145
146 } // namespace vehicle
147 } // namespace automotive
148 } // namespace hardware
149 } // namespace android
150