1/*
2 * Copyright (C) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "hril_event.h"
17
18#include <cerrno>
19#include <fcntl.h>
20#include <sys/select.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include "securec.h"
25
26#include "telephony_log_wrapper.h"
27
28namespace OHOS {
29namespace Telephony {
30void HRilEvent::GetNowTime(struct timeval &tv)
31{
32    struct timespec ts;
33    clock_gettime(CLOCK_MONOTONIC, &ts);
34    tv.tv_sec = ts.tv_sec;
35    tv.tv_usec = ts.tv_nsec / TIME_UNIT;
36}
37
38bool HRilEvent::GetNextTimeOut(struct timeval &tv)
39{
40    std::lock_guard<std::mutex> mutexLock(listLock_);
41    std::list<HRilEventMessage>::iterator eventIt = timerList_.begin();
42    if (eventIt == timerList_.end() || timerList_.empty()) {
43        return false;
44    }
45
46    struct timeval now;
47    GetNowTime(now);
48    TELEPHONY_LOGD("now = %{public}ds + %{public}dus", (int32_t)now.tv_sec, (int32_t)now.tv_usec);
49    TELEPHONY_LOGD(
50        "next = %{public}ds + %{public}dus", (int32_t)eventIt->timeout.tv_sec, (int32_t)eventIt->timeout.tv_usec);
51    if (timercmp(&eventIt->timeout, &now, >)) {
52        timersub(&eventIt->timeout, &now, &tv);
53    } else {
54        // timer already expired.
55        tv.tv_sec = tv.tv_usec = 0;
56    }
57    return true;
58}
59
60void HRilEvent::ProcessTimerList()
61{
62    struct timeval now;
63    std::lock_guard<std::mutex> mutexLock(listLock_);
64    std::list<HRilEventMessage>::iterator eventIt = timerList_.begin();
65    GetNowTime(now);
66
67    TELEPHONY_LOGD("finding for timers <= %{public}ds + %{public}dus", (int32_t)now.tv_sec, (int32_t)now.tv_usec);
68    while ((eventIt != timerList_.end()) && (timercmp(&now, &eventIt->timeout, >))) {
69        HRilEventMessage evMsg = {};
70        evMsg.fd = eventIt->fd;
71        evMsg.func = eventIt->func;
72        evMsg.index = eventIt->index;
73        evMsg.param = eventIt->param;
74        evMsg.timeout = eventIt->timeout;
75        pendingList_.push_back(evMsg);
76        eventIt = timerList_.erase(eventIt);
77    }
78}
79
80bool HRilEvent::HasEventMessageFromPendingList(HRilEventMessage *eventMsg)
81{
82    std::lock_guard<std::mutex> mutexLock(listLock_);
83    std::list<HRilEventMessage>::iterator eventIt = pendingList_.begin();
84    if (eventIt == pendingList_.end()) {
85        return false;
86    }
87    eventMsg->fd = eventIt->fd;
88    eventMsg->func = eventIt->func;
89    eventMsg->index = eventIt->index;
90    eventMsg->param = eventIt->param;
91    eventMsg->timeout = eventIt->timeout;
92    pendingList_.erase(eventIt);
93    return true;
94}
95
96void HRilEvent::ProcessPendingList()
97{
98    HRilEventMessage evMsg = {};
99    while (HasEventMessageFromPendingList(&evMsg)) {
100        if (evMsg.func != nullptr) {
101            evMsg.func(evMsg.fd, 0, evMsg.param);
102        }
103    }
104}
105
106void HRilEvent::EraseListenEvent(HRilEventMessage &eventMsg, int32_t index)
107{
108    listenEventTable_[index] = nullptr;
109    eventMsg.index = DEFAULT_INDEX;
110
111    FD_CLR(eventMsg.fd, &readFds_);
112
113    if (eventMsg.fd + 1 == nfds_) {
114        int32_t n = 0;
115        for (auto msg : listenEventTable_) {
116            if ((msg != nullptr) && (msg->fd > n)) {
117                n = msg->fd;
118            }
119        }
120        nfds_ = n + 1;
121        TELEPHONY_LOGD("Updated nfds = %{public}d", nfds_);
122    }
123}
124
125void HRilEvent::ProcessEvents(fd_set *rfds, int32_t number)
126{
127    if (rfds == nullptr) {
128        return;
129    }
130    std::lock_guard<std::mutex> mutexLock(listLock_);
131    auto it = listenEventTable_.begin();
132    for (; (it != listenEventTable_.end()) && (number > 0); ++it) {
133        if (*it != nullptr && FD_ISSET((*it)->fd, rfds)) {
134            pendingList_.push_back(*(*it));
135            if ((*it)->isHolding == false) {
136                EraseListenEvent(*(*it), (*it)->index);
137            }
138            number--;
139        }
140    }
141}
142
143void HRilEvent::TimerEventInit()
144{
145    std::lock_guard<std::mutex> mutexLock(listLock_);
146    FD_ZERO(&readFds_);
147    timerList_.clear();
148    pendingList_.clear();
149    listenEventTable_.clear();
150    for (int32_t i = 0; i < LISTEN_FD_EVENTS_MAX; i++) {
151        listenEventTable_.push_back(nullptr);
152    }
153}
154
155void HRilEvent::AddTimerEvent(HRilEventMessage &eventMsg, const struct timeval &tv)
156{
157    std::lock_guard<std::mutex> mutexLock(listLock_);
158    struct timeval now;
159    eventMsg.fd = IVNALID_FD; // make sure fd is invalid
160
161    GetNowTime(now);
162    timeradd(&now, &tv, &eventMsg.timeout);
163
164    std::list<HRilEventMessage>::iterator it = timerList_.begin();
165    for (; it != timerList_.end(); ++it) {
166        if (timercmp(&it->timeout, &eventMsg.timeout, >)) {
167            timerList_.insert(it, eventMsg);
168            return;
169        }
170    }
171    if (it == timerList_.end()) {
172        timerList_.push_back(eventMsg);
173    }
174}
175
176void HRilEvent::SetTimerEvent(
177    HRilEventMessage &eventMsg, int32_t fd, bool isHolding, HRilEventCallback func, std::shared_ptr<void> param)
178{
179    (void)memset_s(&eventMsg, sizeof(HRilEventMessage), 0, sizeof(HRilEventMessage));
180    eventMsg.fd = fd;
181    eventMsg.index = DEFAULT_INDEX;
182    eventMsg.func = func;
183    eventMsg.param = param;
184    eventMsg.isHolding = isHolding;
185    fcntl(fd, F_SETFL, O_NONBLOCK);
186}
187
188void HRilEvent::AddEventMessage(const HRilEventMessage &eventMsg)
189{
190    std::lock_guard<std::mutex> mutexLock(listLock_);
191    for (int32_t i = 0; i < LISTEN_FD_EVENTS_MAX; i++) {
192        if (listenEventTable_[i] == nullptr) {
193            listenEventTable_[i] = const_cast<HRilEventMessage *>(&eventMsg);
194            listenEventTable_[i]->index = i;
195            FD_SET(eventMsg.fd, &readFds_);
196            if (eventMsg.fd >= nfds_) {
197                nfds_ = eventMsg.fd + 1;
198            }
199            break;
200        }
201    }
202}
203
204void HRilEvent::RemoveEventMessage(HRilEventMessage &eventMsg)
205{
206    std::lock_guard<std::mutex> mutexLock(listLock_);
207    if (eventMsg.index < 0 || eventMsg.index >= LISTEN_FD_EVENTS_MAX) {
208        TELEPHONY_LOGE("Invalid event message! index:%{pubulic}d", eventMsg.index);
209        return;
210    }
211    EraseListenEvent(eventMsg, eventMsg.index);
212}
213
214void HRilEvent::CopyReadFds(fd_set *rfds)
215{
216    std::lock_guard<std::mutex> mutexLock(listLock_);
217    (void)memcpy_s(rfds, sizeof(fd_set), &readFds_, sizeof(fd_set));
218}
219
220void HRilEvent::EventMessageLoop()
221{
222    int32_t ret;
223    fd_set rfds;
224    struct timeval timeout;
225    struct timeval *pTimeOut;
226
227    TELEPHONY_LOGD("****** EventMessageLoop start ******");
228    while (1) {
229        CopyReadFds(&rfds);
230        if (!GetNextTimeOut(timeout)) {
231            // Enter blocking wait without setting a timer.
232            TELEPHONY_LOGD("Enter blocking wait without setting a timer.");
233            pTimeOut = nullptr;
234        } else {
235            TELEPHONY_LOGD(
236                "Setting timeout for %{public}ds + %{public}dus", (int32_t)timeout.tv_sec, (int32_t)timeout.tv_usec);
237            pTimeOut = &timeout;
238        }
239        ret = select(nfds_, &rfds, nullptr, nullptr, pTimeOut);
240        TELEPHONY_LOGD("There are %{public}d events fired, isNormalDestory: %{public}d.", ret, isNormalDestory);
241        if (isNormalDestory) {
242            return;
243        }
244        if (ret < 0) {
245            if (errno == EINTR) {
246                continue;
247            }
248            TELEPHONY_LOGE("select error (%{public}d)", errno);
249            return;
250        }
251        ProcessTimerList();
252        ProcessEvents(&rfds, ret);
253        ProcessPendingList();
254    }
255}
256
257void HRilEvent::SetNormalDestory(bool isDestory)
258{
259    isNormalDestory = isDestory;
260}
261bool HRilEvent::IsNormalDestory()
262{
263    return isNormalDestory;
264}
265
266} // namespace Telephony
267} // namespace OHOS
268