1/*
2 * Copyright (c) 2022-2023 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 "devicestatus_msdp_mock.h"
17
18#include <cerrno>
19#include <string>
20#include <unistd.h>
21
22#include <sys/epoll.h>
23#include <sys/timerfd.h>
24
25#include "devicestatus_common.h"
26#include "devicestatus_define.h"
27#include "fi_log.h"
28
29#undef LOG_TAG
30#define LOG_TAG "DeviceStatusMsdpMock"
31
32namespace OHOS {
33namespace Msdp {
34namespace DeviceStatus {
35namespace {
36constexpr int32_t TIMER_INTERVAL { 3 };
37constexpr int32_t ERR_INVALID_FD { -1 };
38DeviceStatusMsdpMock* g_msdpMock { nullptr };
39} // namespace
40
41DeviceStatusMsdpMock::DeviceStatusMsdpMock()
42{
43    enabledType_ = {
44        TYPE_STILL,
45        TYPE_RELATIVE_STILL,
46        TYPE_CAR_BLUETOOTH
47    };
48    if (dataParse_ == nullptr) {
49        dataParse_ = std::make_unique<DeviceStatusDataParse>();
50    }
51}
52
53DeviceStatusMsdpMock::~DeviceStatusMsdpMock()
54{
55    callbacks_.clear();
56    alive_ = false;
57    CloseTimer();
58    if (thread_.joinable()) {
59        thread_.join();
60        FI_HILOGI("thread_ is stop");
61    }
62}
63
64bool DeviceStatusMsdpMock::Init()
65{
66    CALL_DEBUG_ENTER;
67    InitTimer();
68    StartThread();
69    return true;
70}
71
72ErrCode DeviceStatusMsdpMock::RegisterCallback(std::shared_ptr<MsdpAlgoCallback> callback)
73{
74    std::lock_guard lock(mutex_);
75    callback_ = callback;
76    return RET_OK;
77}
78
79ErrCode DeviceStatusMsdpMock::UnregisterCallback()
80{
81    std::lock_guard lock(mutex_);
82    callback_ = nullptr;
83    return RET_OK;
84}
85
86ErrCode DeviceStatusMsdpMock::Enable(Type type)
87{
88    CALL_DEBUG_ENTER;
89    Init();
90    return RET_OK;
91}
92
93ErrCode DeviceStatusMsdpMock::Disable(Type type)
94{
95    CALL_DEBUG_ENTER;
96    alive_ = false;
97    CloseTimer();
98    if (thread_.joinable()) {
99        thread_.join();
100        FI_HILOGI("thread_ is stop");
101    }
102    return RET_OK;
103}
104
105ErrCode DeviceStatusMsdpMock::DisableCount(Type type)
106{
107    CALL_DEBUG_ENTER;
108    CHKPR(dataParse_, RET_ERR);
109    dataParse_->DisableCount(type);
110    return RET_OK;
111}
112
113ErrCode DeviceStatusMsdpMock::NotifyMsdpImpl(const Data &data)
114{
115    CALL_DEBUG_ENTER;
116    CHKPR(g_msdpMock, RET_ERR);
117    CHKPR(g_msdpMock->GetCallbackImpl(), RET_ERR);
118    FI_HILOGI("type:%{public}d, value:%{public}d", data.type, data.value);
119    g_msdpMock->GetCallbackImpl()->OnResult(data);
120    return RET_OK;
121}
122
123void DeviceStatusMsdpMock::InitTimer()
124{
125    CALL_DEBUG_ENTER;
126    epFd_ = epoll_create1(EPOLL_CLOEXEC);
127    if (epFd_ == -1) {
128        FI_HILOGE("Create epoll fd failed");
129        return;
130    }
131    timerFd_ = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
132    if (timerFd_ == ERR_INVALID_FD) {
133        FI_HILOGE("Create timer fd failed");
134        if (close(epFd_) < 0) {
135            FI_HILOGE("Close epoll fd failed, error:%{public}s, epFd_:%{public}d", strerror(errno), epFd_);
136        }
137        epFd_ = ERR_INVALID_FD;
138        return;
139    }
140    SetTimerInterval(TIMER_INTERVAL);
141    fcntl(timerFd_, F_SETFL, O_NONBLOCK);
142    auto [_, ret] = callbacks_.insert(std::make_pair(timerFd_, &DeviceStatusMsdpMock::TimerCallback));
143    if (!ret) {
144        FI_HILOGW("Insert timer fd failed");
145    }
146    if (RegisterTimerCallback(timerFd_, EVENT_TIMER_FD)) {
147        FI_HILOGE("Register timer fd failed");
148        return;
149    }
150}
151
152int32_t DeviceStatusMsdpMock::SetTimerInterval(int32_t interval)
153{
154    if (timerFd_ == ERR_INVALID_FD) {
155        FI_HILOGE("Create timer fd failed");
156        return RET_ERR;
157    }
158
159    if (interval < 0) {
160        FI_HILOGE("Illegal time interval");
161        return RET_ERR;
162    }
163    struct itimerspec itval;
164    itval.it_interval.tv_sec = interval;
165    itval.it_interval.tv_nsec = 0;
166    itval.it_value.tv_sec = interval;
167    itval.it_value.tv_nsec = 0;
168    if (timerfd_settime(timerFd_, 0, &itval, nullptr) == -1) {
169        FI_HILOGE("Set timer failed");
170        return RET_ERR;
171    }
172    return RET_OK;
173}
174
175void DeviceStatusMsdpMock::CloseTimer()
176{
177    if (timerFd_ < 0) {
178        FI_HILOGE("Invalid timerFd_");
179        return;
180    }
181    if (close(timerFd_) < 0) {
182        FI_HILOGE("Close timer fd failed, error:%{public}s, timerFd_:%{public}d", strerror(errno), timerFd_);
183    }
184    timerFd_ = -1;
185}
186
187void DeviceStatusMsdpMock::TimerCallback()
188{
189    uint64_t timers {};
190    if (read(timerFd_, &timers, sizeof(timers)) == -1) {
191        FI_HILOGE("Read timer fd failed");
192        return;
193    }
194    GetDeviceStatusData();
195}
196
197int32_t DeviceStatusMsdpMock::GetDeviceStatusData()
198{
199    for (const auto &item : enabledType_) {
200        Type type = item;
201        CHKPR(dataParse_, RET_ERR);
202        Data data;
203        dataParse_->ParseDeviceStatusData(type, data);
204        FI_HILOGD("Mock type:%{public}d, value:%{public}d", data.type, data.value);
205        NotifyMsdpImpl(data);
206    }
207    return RET_OK;
208}
209
210int32_t DeviceStatusMsdpMock::RegisterTimerCallback(int32_t fd, const EventType et)
211{
212    CALL_DEBUG_ENTER;
213    struct epoll_event ev;
214    ev.events = EPOLLIN;
215    if (et == EVENT_TIMER_FD) {
216        ev.events |= EPOLLWAKEUP;
217    }
218
219    ev.data.ptr = reinterpret_cast<void*>(this);
220    ev.data.fd = fd;
221    if (epoll_ctl(epFd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
222        FI_HILOGE("epoll_ctl failed, errno:%{public}d", errno);
223        return RET_ERR;
224    }
225
226    return RET_OK;
227}
228
229void DeviceStatusMsdpMock::StartThread()
230{
231    CALL_DEBUG_ENTER;
232    if (!alive_) {
233        alive_ = true;
234        thread_ = std::thread([this] { this->LoopingThreadEntry(); });
235    }
236}
237
238void DeviceStatusMsdpMock::LoopingThreadEntry()
239{
240    SetThreadName("os_loop_mock");
241    if (callbacks_.empty()) {
242        FI_HILOGD("callbacks_ is empty");
243        return;
244    }
245    size_t cbct = callbacks_.size();
246    struct epoll_event events[cbct];
247    while (alive_) {
248        int32_t timeout = 200;
249        int32_t nevents = epoll_wait(epFd_, events, cbct, timeout);
250        if (nevents == -1) {
251            FI_HILOGE("No events available");
252            return;
253        }
254        for (int32_t n = 0; n < nevents; ++n) {
255            if (events[n].data.ptr) {
256                DeviceStatusMsdpMock *func = const_cast<DeviceStatusMsdpMock *>(this);
257                (callbacks_.find(events[n].data.fd)->second)(func);
258            }
259        }
260    }
261}
262
263extern "C" IMsdp *Create(void)
264{
265    CALL_DEBUG_ENTER;
266    g_msdpMock = new (std::nothrow) DeviceStatusMsdpMock();
267    CHKPP(g_msdpMock);
268    return g_msdpMock;
269}
270
271extern "C" void Destroy(const IMsdp* algorithm)
272{
273    CALL_INFO_TRACE;
274    if (algorithm != nullptr) {
275        FI_HILOGD("algorithm is not nullptr");
276        delete algorithm;
277    }
278}
279} // namespace DeviceStatus
280} // namespace Msdp
281} // namespace OHOS
282