1 /*
2 * Copyright (c) 2022 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 <uv.h>
17
18 #include "client_helper.h"
19 #include "node_api.h"
20 #include "update_helper.h"
21 #include "update_session.h"
22 #include "session_manager.h"
23
24 using namespace std;
25
26 namespace OHOS {
27 namespace UpdateEngine {
28 struct NotifyInput {
29 NotifyInput() = default;
NotifyInputOHOS::UpdateEngine::NotifyInput30 NotifyInput(SessionManager *sesssionManager, const string &itype, UpdateResult *iresult)
31 : sesssionMgr(sesssionManager), type(itype), result(iresult) {}
32 SessionManager *sesssionMgr = nullptr;
33 string type;
34 UpdateResult *result = nullptr;
35 };
36
SessionManager(napi_env env,napi_ref thisReference)37 SessionManager::SessionManager(napi_env env, napi_ref thisReference) : env_(env), thisReference_(thisReference) {}
38
~SessionManager()39 SessionManager::~SessionManager()
40 {
41 sessions_.clear();
42 if (thisReference_ != nullptr) {
43 napi_delete_reference(env_, thisReference_);
44 }
45 }
46
AddSession(std::shared_ptr<IUpdateSession> session)47 void SessionManager::AddSession(std::shared_ptr<IUpdateSession> session)
48 {
49 PARAM_CHECK(session != nullptr, return, "Invalid param");
50 #ifndef UPDATER_API_TEST
51 std::lock_guard<std::mutex> guard(sessionMutex_);
52 #endif
53 sessions_.insert(make_pair(session->GetSessionId(), session));
54 }
55
RemoveSession(uint32_t sessionId)56 void SessionManager::RemoveSession(uint32_t sessionId)
57 {
58 CLIENT_LOGI("RemoveSession sess");
59 #ifndef UPDATER_API_TEST
60 std::lock_guard<std::mutex> guard(sessionMutex_);
61 #endif
62 IUpdateSession *sess = nullptr;
63 auto iter = sessions_.find(sessionId);
64 if (iter != sessions_.end()) {
65 sess = iter->second.get();
66 sessions_.erase(iter);
67 }
68 }
69
Clear()70 void SessionManager::Clear()
71 {
72 sessions_.clear();
73 if (thisReference_ != nullptr) {
74 napi_delete_reference(env_, thisReference_);
75 }
76 }
77
GetFirstSessionId(uint32_t & sessionId)78 bool SessionManager::GetFirstSessionId(uint32_t &sessionId)
79 {
80 #ifndef UPDATER_API_TEST
81 std::lock_guard<std::mutex> guard(sessionMutex_);
82 #endif
83 {
84 if (sessions_.empty()) {
85 return false;
86 }
87 sessionId = sessions_.begin()->second->GetSessionId();
88 return true;
89 }
90 }
91
GetNextSessionId(uint32_t & sessionId)92 bool SessionManager::GetNextSessionId(uint32_t &sessionId)
93 {
94 #ifndef UPDATER_API_TEST
95 std::lock_guard<std::mutex> guard(sessionMutex_);
96 #endif
97 {
98 auto iter = sessions_.find(sessionId);
99 if (iter == sessions_.end()) {
100 return false;
101 }
102 iter++;
103 if (iter == sessions_.end()) {
104 return false;
105 }
106 sessionId = iter->second->GetSessionId();
107 }
108 return true;
109 }
110
ProcessUnsubscribe(const std::string & eventType,size_t argc,napi_value arg)111 int32_t SessionManager::ProcessUnsubscribe(const std::string &eventType, size_t argc, napi_value arg)
112 {
113 napi_handle_scope scope;
114 napi_status status = napi_open_handle_scope(env_, &scope);
115 PARAM_CHECK(status == napi_ok, return -1, "Error open handle");
116
117 uint32_t nextSessId = 0;
118 bool hasNext = GetFirstSessionId(nextSessId);
119 while (hasNext) {
120 uint32_t currSessId = nextSessId;
121 auto iter = sessions_.find(currSessId);
122 if (iter == sessions_.end()) {
123 break;
124 }
125 hasNext = GetNextSessionId(nextSessId);
126
127 UpdateListener *listener = static_cast<UpdateListener *>(iter->second.get());
128 if (listener->GetType() != SessionType::SESSION_SUBSCRIBE ||
129 eventType.compare(listener->GetEventType()) != 0) {
130 continue;
131 }
132 CLIENT_LOGI("ProcessUnsubscribe remove session");
133 if (argc == 1) {
134 listener->RemoveHandlerRef(env_);
135 RemoveSession(currSessId);
136 } else if (listener->CheckEqual(env_, arg, eventType)) {
137 listener->RemoveHandlerRef(env_);
138 RemoveSession(currSessId);
139 break;
140 }
141 }
142 napi_close_handle_scope(env_, scope);
143 return 0;
144 }
145
Unsubscribe(const EventClassifyInfo & eventClassifyInfo,napi_value handle)146 void SessionManager::Unsubscribe(const EventClassifyInfo &eventClassifyInfo, napi_value handle)
147 {
148 std::lock_guard<std::mutex> guard(sessionMutex_);
149 for (auto iter = sessions_.begin(); iter != sessions_.end();) {
150 if (iter->second == nullptr) {
151 iter = sessions_.erase(iter); // erase nullptr
152 continue;
153 }
154
155 if (iter->second->GetType() != SessionType::SESSION_SUBSCRIBE) {
156 ++iter;
157 continue;
158 }
159
160 auto listener = static_cast<UpdateListener *>(iter->second.get());
161 if (handle == nullptr && listener->IsSubscribeEvent(eventClassifyInfo)) {
162 CLIENT_LOGI("Unsubscribe, remove session %{public}d without handle", listener->GetSessionId());
163 iter = sessions_.erase(iter);
164 continue;
165 }
166
167 if (listener->IsSameListener(env_, eventClassifyInfo, handle)) {
168 CLIENT_LOGI("Unsubscribe, remove session %{public}d", listener->GetSessionId());
169 iter = sessions_.erase(iter);
170 continue;
171 }
172
173 ++iter;
174 }
175 }
176
FindSessionByHandle(napi_env env,const std::string & eventType,napi_value arg)177 IUpdateSession *SessionManager::FindSessionByHandle(napi_env env, const std::string &eventType, napi_value arg)
178 {
179 uint32_t nextSessId = 0;
180 bool hasNext = GetFirstSessionId(nextSessId);
181 while (hasNext) {
182 uint32_t currSessId = nextSessId;
183 auto iter = sessions_.find(currSessId);
184 if (iter == sessions_.end()) {
185 break;
186 }
187 hasNext = GetNextSessionId(nextSessId);
188
189 UpdateListener *listener = static_cast<UpdateListener *>(iter->second.get());
190 if (listener->GetType() != SessionType::SESSION_SUBSCRIBE) {
191 continue;
192 }
193 if ((eventType.compare(listener->GetEventType()) == 0) && listener->CheckEqual(env_, arg, eventType)) {
194 return listener;
195 }
196 }
197 return nullptr;
198 }
199
FindSessionByHandle(napi_env env,const EventClassifyInfo & eventClassifyInfo,napi_value arg)200 IUpdateSession *SessionManager::FindSessionByHandle(napi_env env, const EventClassifyInfo &eventClassifyInfo,
201 napi_value arg)
202 {
203 std::lock_guard<std::mutex> guard(sessionMutex_);
204 for (auto &iter : sessions_) {
205 if (iter.second == nullptr) {
206 continue;
207 }
208
209 if (iter.second->GetType() != SessionType::SESSION_SUBSCRIBE) {
210 continue;
211 }
212
213 auto listener = static_cast<UpdateListener *>(iter.second.get());
214 if (listener->IsSameListener(env, eventClassifyInfo, arg)) {
215 return listener;
216 }
217 }
218 return nullptr;
219 }
220
PublishToJS(const std::string & type,const UpdateResult & result)221 void SessionManager::PublishToJS(const std::string &type, const UpdateResult &result)
222 {
223 CLIENT_LOGI("PublishToJS %{public}s", type.c_str());
224 napi_handle_scope scope;
225 napi_status status = napi_open_handle_scope(env_, &scope);
226 PARAM_CHECK_NAPI_CALL(env_, status == napi_ok, return, "Error open_handle_scope");
227 napi_value thisVar = nullptr;
228 status = napi_get_reference_value(env_, thisReference_, &thisVar);
229 PARAM_CHECK_NAPI_CALL(env_, status == napi_ok, napi_close_handle_scope(env_, scope); return,
230 "Error get_reference");
231
232 uint32_t nextSessId = 0;
233 bool hasNext = GetFirstSessionId(nextSessId);
234 while (hasNext) {
235 uint32_t currSessId = nextSessId;
236 auto iter = sessions_.find(currSessId);
237 if (iter == sessions_.end()) {
238 break;
239 }
240 hasNext = GetNextSessionId(nextSessId);
241 if (iter->second == nullptr) {
242 CLIENT_LOGE("PublishToJS error, updateSession is null, %{public}d", iter->first);
243 continue;
244 }
245 IUpdateSession *updateSession = (iter->second).get();
246 CLIENT_LOGI("PublishToJS GetType %{public}d", updateSession->GetType());
247 if (updateSession->GetType() == SessionType::SESSION_SUBSCRIBE) {
248 UpdateListener *listener = static_cast<UpdateListener *>(updateSession);
249 if (type.compare(listener->GetEventType()) != 0) {
250 continue;
251 }
252
253 listener->NotifyJS(env_, thisVar, result);
254 if (listener->IsOnce()) {
255 listener->RemoveHandlerRef(env_);
256 RemoveSession(currSessId);
257 }
258 } else if (updateSession->IsAsyncCompleteWork()) {
259 updateSession->NotifyJS(env_, thisVar, result);
260 RemoveSession(currSessId);
261 } else {
262 CLIENT_LOGI("PublishToJS GetType unknown type");
263 }
264 }
265 napi_close_handle_scope(env_, scope);
266 }
267
PublishToJS(const EventClassifyInfo & eventClassifyInfo,const EventInfo & eventInfo)268 void SessionManager::PublishToJS(const EventClassifyInfo &eventClassifyInfo, const EventInfo &eventInfo)
269 {
270 napi_handle_scope scope;
271 napi_status status = napi_open_handle_scope(env_, &scope);
272 PARAM_CHECK_NAPI_CALL(env_, status == napi_ok, return, "Error open_handle_scope");
273 napi_value thisVar = nullptr;
274 status = napi_get_reference_value(env_, thisReference_, &thisVar);
275 PARAM_CHECK_NAPI_CALL(env_, status == napi_ok, napi_close_handle_scope(env_, scope); return,
276 "Error get_reference");
277
278 std::lock_guard<std::mutex> guard(sessionMutex_);
279 for (auto &iter : sessions_) {
280 if (iter.second == nullptr) {
281 continue;
282 }
283
284 if (iter.second->GetType() != SessionType::SESSION_SUBSCRIBE) {
285 continue;
286 }
287
288 UpdateListener *listener = static_cast<UpdateListener *>(iter.second.get());
289 if (!listener->IsSubscribeEvent(eventClassifyInfo)) {
290 continue;
291 }
292
293 listener->NotifyJS(env_, thisVar, eventInfo);
294 }
295 napi_close_handle_scope(env_, scope);
296 }
297
Emit(const std::string & type,const UpdateResult & result)298 void SessionManager::Emit(const std::string &type, const UpdateResult &result)
299 {
300 auto freeUpdateResult = [](UpdateResult *lres) {
301 if (lres != nullptr) {
302 lres->Release();
303 }
304 delete lres;
305 };
306 CLIENT_LOGI("SessionManager::Emit %{public}s", type.c_str());
307
308 UpdateResult *res = new (std::nothrow) UpdateResult();
309 if (res == nullptr) {
310 return;
311 }
312 *res = result;
313 uv_loop_s *loop = nullptr;
314 napi_get_uv_event_loop(env_, &loop);
315 PARAM_CHECK(loop != nullptr, freeUpdateResult(res); return, "get event loop failed.");
316
317 uv_work_t *work = new (std::nothrow) uv_work_t;
318 PARAM_CHECK(work != nullptr, freeUpdateResult(res); return, "alloc work failed.");
319
320 work->data = (void *)new (std::nothrow) NotifyInput(this, type, res);
321 PARAM_CHECK(work->data != nullptr, freeUpdateResult(res); delete work; return, "alloc work data failed.");
322
323 uv_queue_work(
324 loop,
325 work,
326 [](uv_work_t *work) {}, // run in C++ thread
327 [](uv_work_t *work, int status) {
328 NotifyInput *input = (NotifyInput *)work->data;
329 input->sesssionMgr->PublishToJS(input->type, *input->result);
330 input->result->Release();
331 delete input->result;
332 delete input;
333 delete work;
334 });
335 }
336
Emit(const EventClassifyInfo & eventClassifyInfo,const EventInfo & eventInfo)337 void SessionManager::Emit(const EventClassifyInfo &eventClassifyInfo, const EventInfo &eventInfo)
338 {
339 CLIENT_LOGI("SessionManager::Emit 0x%{public}x", CAST_INT(eventClassifyInfo.eventClassify));
340 uv_loop_s *loop = nullptr;
341 napi_get_uv_event_loop(env_, &loop);
342 PARAM_CHECK(loop != nullptr, return, "get event loop failed.");
343
344 using UvWorkData = std::tuple<SessionManager*, EventClassifyInfo, EventInfo>;
345 UvWorkData* data = new (std::nothrow) std::tuple(this, eventClassifyInfo, eventInfo);
346 PARAM_CHECK(data != nullptr, return, "alloc data failed.");
347
348 uv_work_t *work = new (std::nothrow) uv_work_t;
349 PARAM_CHECK(work != nullptr, delete data; return, "alloc work failed.");
350
351 work->data = static_cast<void *>(data);
352 uv_queue_work(
353 loop,
354 work,
355 [](uv_work_t *work) {},
356 [](uv_work_t *work, int status) {
357 UvWorkData *data = static_cast<UvWorkData*>(work->data);
358 auto &[mgr, eventClassifyInfo, eventInfo] = *data;
359 mgr->PublishToJS(eventClassifyInfo, eventInfo);
360 delete data;
361 delete work;
362 });
363 }
364 } // namespace UpdateEngine
365 } // namespace OHOS