1 /*
2  * Copyright (c) 2021-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 "local_live_view_subscribe.h"
17 #include "notification_button_option.h"
18 #include "ans_inner_errors.h"
19 #include <mutex>
20 #include <uv.h>
21 
22 namespace OHOS {
23 namespace NotificationNapi {
24 const int32_t SUBSRIBE_MAX_PARA = 2;
25 const std::string RESPONSE = "onResponse";
26 
27 struct LocalLiveViewReceiveDataWorker {
28     napi_env env = nullptr;
29     napi_ref ref = nullptr;
30     int32_t notificationId;
31     sptr<NotificationButtonOption> buttonOption;
32     LocalLiveViewSubscriberInstance *subscriber = nullptr;
33 };
34 
LocalLiveViewSubscriberInstance()35 LocalLiveViewSubscriberInstance::LocalLiveViewSubscriberInstance()
36 {}
37 
~LocalLiveViewSubscriberInstance()38 LocalLiveViewSubscriberInstance::~LocalLiveViewSubscriberInstance()
39 {
40     if (responseCallbackInfo_.ref != nullptr) {
41         napi_delete_reference(responseCallbackInfo_.env, responseCallbackInfo_.ref);
42     }
43 }
44 
OnDied()45 void LocalLiveViewSubscriberInstance::OnDied()
46 {
47     ANS_LOGD("enter");
48 }
49 
OnConnected()50 void LocalLiveViewSubscriberInstance::OnConnected()
51 {
52     ANS_LOGD("enter");
53 }
54 
OnDisconnected()55 void LocalLiveViewSubscriberInstance::OnDisconnected()
56 {
57     ANS_LOGD("enter");
58 }
59 
UvQueueWorkOnResponse(uv_work_t * work,int status)60 void UvQueueWorkOnResponse(uv_work_t *work, int status)
61 {
62     ANS_LOGI("OnResponse uv_work_t start");
63 
64     if (work == nullptr) {
65         ANS_LOGE("work is nullptr");
66         return;
67     }
68 
69     auto dataWorkerData = reinterpret_cast<LocalLiveViewReceiveDataWorker *>(work->data);
70     if (dataWorkerData == nullptr) {
71         ANS_LOGD("dataWorkerData is null.");
72         delete work;
73         work = nullptr;
74         return;
75     }
76     napi_value buttonOption = nullptr;
77     napi_value buttonName = nullptr;
78     napi_handle_scope scope;
79     napi_value notificationId = nullptr;
80     napi_open_handle_scope(dataWorkerData->env, &scope);
81     if (scope == nullptr) {
82         ANS_LOGE("Scope is null");
83         return;
84     }
85 
86     // notificationId: number
87     napi_create_int32(dataWorkerData->env, dataWorkerData->notificationId, &notificationId);
88 
89     napi_create_object(dataWorkerData->env, &buttonOption);
90     napi_create_string_utf8(dataWorkerData->env, dataWorkerData->buttonOption->GetButtonName().c_str(),
91         NAPI_AUTO_LENGTH, &buttonName);
92     napi_set_named_property(dataWorkerData->env, buttonOption, "buttonName", buttonName);
93 
94     Common::SetCallbackArg2(dataWorkerData->env, dataWorkerData->ref, notificationId, buttonOption);
95     napi_close_handle_scope(dataWorkerData->env, scope);
96 
97     delete dataWorkerData;
98     dataWorkerData = nullptr;
99     delete work;
100 }
101 
OnResponse(int32_t notificationId,sptr<NotificationButtonOption> buttonOption)102 void LocalLiveViewSubscriberInstance::OnResponse(int32_t notificationId, sptr<NotificationButtonOption> buttonOption)
103 {
104     ANS_LOGD("enter");
105 
106     if (responseCallbackInfo_.ref == nullptr) {
107         ANS_LOGI("response callback unset");
108         return;
109     }
110 
111     if (buttonOption == nullptr) {
112         ANS_LOGE("buttonOption is null");
113         return;
114     }
115 
116     ANS_LOGI("OnResponse NotificationId = %{public}d", notificationId);
117     ANS_LOGI("OnResponse buttonOption size = %{public}s", buttonOption->GetButtonName().c_str());
118 
119     uv_loop_s *loop = nullptr;
120     napi_get_uv_event_loop(responseCallbackInfo_.env, &loop);
121     if (loop == nullptr) {
122         ANS_LOGE("loop instance is nullptr");
123         return;
124     }
125 
126     LocalLiveViewReceiveDataWorker *dataWorker = new (std::nothrow) LocalLiveViewReceiveDataWorker();
127     if (dataWorker == nullptr) {
128         ANS_LOGE("DataWorker is nullptr.");
129         return;
130     }
131 
132     dataWorker->notificationId = notificationId;
133     dataWorker->buttonOption = buttonOption;
134     dataWorker->env = responseCallbackInfo_.env;
135     dataWorker->ref = responseCallbackInfo_.ref;
136 
137     uv_work_t *work = new (std::nothrow) uv_work_t;
138     if (work == nullptr) {
139         ANS_LOGE("new work failed");
140         delete dataWorker;
141         dataWorker = nullptr;
142         return;
143     }
144 
145     work->data = reinterpret_cast<void *>(dataWorker);
146 
147     int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
148         UvQueueWorkOnResponse, uv_qos_user_initiated);
149     if (ret != 0) {
150         delete dataWorker;
151         dataWorker = nullptr;
152         delete work;
153         work = nullptr;
154     }
155 }
156 
SetResponseCallbackInfo(const napi_env & env,const napi_ref & ref)157 void LocalLiveViewSubscriberInstance::SetResponseCallbackInfo(const napi_env &env, const napi_ref &ref)
158 {
159     responseCallbackInfo_.env = env;
160     responseCallbackInfo_.ref = ref;
161 }
162 
SetCallbackInfo(const napi_env & env,const std::string & type,const napi_ref & ref)163 void LocalLiveViewSubscriberInstance::SetCallbackInfo(const napi_env &env, const std::string &type, const napi_ref &ref)
164 {
165     if (type == RESPONSE) {
166         SetResponseCallbackInfo(env, ref);
167     } else {
168         ANS_LOGW("type is error");
169     }
170 }
171 
HasNotificationSubscriber(const napi_env & env,const napi_value & value,LocalLiveViewSubscriberInstancesInfo & subscriberInfo)172 bool HasNotificationSubscriber(const napi_env &env, const napi_value &value,
173     LocalLiveViewSubscriberInstancesInfo &subscriberInfo)
174 {
175     std::lock_guard<std::mutex> lock(mutex_);
176     for (auto vec : subscriberInstances_) {
177         napi_value callback = nullptr;
178         napi_get_reference_value(env, vec.ref, &callback);
179         bool isEquals = false;
180         napi_strict_equals(env, value, callback, &isEquals);
181         if (isEquals) {
182             subscriberInfo = vec;
183             return true;
184         }
185     }
186     return false;
187 }
188 
GetNotificationSubscriber(const napi_env & env,const napi_value & value,LocalLiveViewSubscriberInstancesInfo & subscriberInfo)189 napi_value GetNotificationSubscriber(
190     const napi_env &env, const napi_value &value, LocalLiveViewSubscriberInstancesInfo &subscriberInfo)
191 {
192     ANS_LOGD("enter");
193     bool hasProperty = false;
194     napi_valuetype valuetype = napi_undefined;
195     napi_ref result = nullptr;
196 
197     subscriberInfo.subscriber = new (std::nothrow) LocalLiveViewSubscriberInstance();
198     if (subscriberInfo.subscriber == nullptr) {
199         ANS_LOGE("subscriber is null");
200         return nullptr;
201     }
202 
203     napi_create_reference(env, value, 1, &subscriberInfo.ref);
204 
205     // onResponse?
206     NAPI_CALL(env, napi_has_named_property(env, value, "onResponse", &hasProperty));
207     if (hasProperty) {
208         napi_value onResponse = nullptr;
209         napi_get_named_property(env, value, "onResponse", &onResponse);
210         NAPI_CALL(env, napi_typeof(env, onResponse, &valuetype));
211         if (valuetype != napi_function) {
212             ANS_LOGE("Wrong argument type. Function expected.");
213             std::string msg = "Incorrect parameter types.The type of param must be function.";
214             Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
215             return nullptr;
216         }
217         napi_create_reference(env, onResponse, 1, &result);
218         subscriberInfo.subscriber->SetCallbackInfo(env, RESPONSE, result);
219     }
220 
221     return Common::NapiGetNull(env);
222 }
223 
AddSubscriberInstancesInfo(const napi_env & env,const LocalLiveViewSubscriberInstancesInfo & subscriberInfo)224 bool AddSubscriberInstancesInfo(const napi_env &env, const LocalLiveViewSubscriberInstancesInfo &subscriberInfo)
225 {
226     ANS_LOGD("enter");
227     if (subscriberInfo.ref == nullptr) {
228         ANS_LOGE("subscriberInfo.ref is null");
229         return false;
230     }
231     if (subscriberInfo.subscriber == nullptr) {
232         ANS_LOGE("subscriberInfo.subscriber is null");
233         return false;
234     }
235     std::lock_guard<std::mutex> lock(mutex_);
236     subscriberInstances_.emplace_back(subscriberInfo);
237 
238     return true;
239 }
240 
DelSubscriberInstancesInfo(const napi_env & env,const LocalLiveViewSubscriberInstance * subscriber)241 bool DelSubscriberInstancesInfo(const napi_env &env, const LocalLiveViewSubscriberInstance *subscriber)
242 {
243     ANS_LOGD("enter");
244     if (subscriber == nullptr) {
245         ANS_LOGE("subscriber is null");
246         return false;
247     }
248 
249     std::lock_guard<std::mutex> lock(mutex_);
250     for (auto it = subscriberInstances_.begin(); it != subscriberInstances_.end(); ++it) {
251         if ((*it).subscriber == subscriber) {
252             if ((*it).ref != nullptr) {
253                 napi_delete_reference(env, (*it).ref);
254             }
255             DelDeletingSubscriber((*it).subscriber);
256             delete (*it).subscriber;
257             (*it).subscriber = nullptr;
258             subscriberInstances_.erase(it);
259             return true;
260         }
261     }
262     return false;
263 }
ParseParameters(const napi_env & env,const napi_callback_info & info,LocalLiveViewSubscriberInstance * & subscriber,napi_ref & callback)264 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info,
265     LocalLiveViewSubscriberInstance *&subscriber, napi_ref &callback)
266 {
267     ANS_LOGD("enter");
268 
269     size_t argc = SUBSRIBE_MAX_PARA;
270     napi_value argv[SUBSRIBE_MAX_PARA] = {nullptr};
271     napi_value thisVar = nullptr;
272     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
273     if (argc < 1) {
274         ANS_LOGE("Wrong number of arguments");
275         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
276         return nullptr;
277     }
278 
279     napi_valuetype valuetype = napi_undefined;
280 
281     // argv[0]:LocalLiveViewButton
282     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
283     if (valuetype != napi_object) {
284         ANS_LOGE("Wrong argument type for arg0. LocalLiveViewButton object expected.");
285         std::string msg = "Incorrect parameter types.The type of param must be LocalLiveViewButton.";
286         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
287         return nullptr;
288     }
289 
290     LocalLiveViewSubscriberInstancesInfo subscriberInstancesInfo;
291     if (!HasNotificationSubscriber(env, argv[PARAM0], subscriberInstancesInfo)) {
292         if (GetNotificationSubscriber(env, argv[PARAM0], subscriberInstancesInfo) == nullptr) {
293             ANS_LOGE("LocalLiveViewButton parse failed");
294             Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
295             if (subscriberInstancesInfo.subscriber) {
296                 delete subscriberInstancesInfo.subscriber;
297                 subscriberInstancesInfo.subscriber = nullptr;
298             }
299             return nullptr;
300         }
301         if (!AddSubscriberInstancesInfo(env, subscriberInstancesInfo)) {
302             ANS_LOGE("AddSubscriberInstancesInfo add failed");
303             Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
304             if (subscriberInstancesInfo.subscriber) {
305                 delete subscriberInstancesInfo.subscriber;
306                 subscriberInstancesInfo.subscriber = nullptr;
307             }
308             return nullptr;
309         }
310     }
311     subscriber = subscriberInstancesInfo.subscriber;
312 
313     // argv[1]:callback
314     if (argc >= SUBSRIBE_MAX_PARA) {
315         NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
316         if (valuetype != napi_function) {
317             ANS_LOGE("Callback is not function enforce promise.");
318             return Common::NapiGetNull(env);
319         }
320         napi_create_reference(env, argv[PARAM1], 1, &callback);
321     }
322 
323     return Common::NapiGetNull(env);
324 }
325 
AddDeletingSubscriber(LocalLiveViewSubscriberInstance * subscriber)326 bool AddDeletingSubscriber(LocalLiveViewSubscriberInstance *subscriber)
327 {
328     std::lock_guard<std::mutex> lock(delMutex_);
329     auto iter = std::find(DeletingSubscriber.begin(), DeletingSubscriber.end(), subscriber);
330     if (iter != DeletingSubscriber.end()) {
331         return false;
332     }
333 
334     DeletingSubscriber.push_back(subscriber);
335     return true;
336 }
337 
DelDeletingSubscriber(LocalLiveViewSubscriberInstance * subscriber)338 void DelDeletingSubscriber(LocalLiveViewSubscriberInstance *subscriber)
339 {
340     std::lock_guard<std::mutex> lock(delMutex_);
341     auto iter = std::find(DeletingSubscriber.begin(), DeletingSubscriber.end(), subscriber);
342     if (iter != DeletingSubscriber.end()) {
343         DeletingSubscriber.erase(iter);
344     }
345 }
346 
347 }  // namespace NotificationNapi
348 }  // namespace OHOS
349