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 "publish.h"
17 
18 #include "ans_const_define.h"
19 #include "js_native_api_types.h"
20 #include "want_agent_helper.h"
21 #include "ans_inner_errors.h"
22 
23 namespace OHOS {
24 namespace NotificationNapi {
25 using namespace AbilityRuntime::WantAgent;
26 
27 namespace {
28 constexpr int8_t PUBLISH_NOTIFICATION_MAX = 3;
29 constexpr int8_t SHOW_NOTIFICATION_MAX = 1;
30 constexpr int8_t PUBLISH_AS_BUNDLE_MAX = 4;
31 constexpr int8_t PUBLISH_AS_BUNDLEOPTION_MAX = 2;
32 }
33 
GetCallback(const napi_env & env,const napi_value & value,ParametersInfoPublish & params)34 napi_value GetCallback(const napi_env &env, const napi_value &value, ParametersInfoPublish &params)
35 {
36     ANS_LOGD("enter");
37 
38     napi_valuetype valuetype = napi_undefined;
39     NAPI_CALL(env, napi_typeof(env, value, &valuetype));
40     if (valuetype != napi_function) {
41         ANS_LOGW("Callback is not function excute promise.");
42         return Common::NapiGetNull(env);
43     }
44     napi_create_reference(env, value, 1, &params.callback);
45     ANS_LOGD("end");
46     return Common::NapiGetNull(env);
47 }
48 
ParseParameters(const napi_env & env,const napi_callback_info & info,ParametersInfoPublish & params)49 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info, ParametersInfoPublish &params)
50 {
51     ANS_LOGD("enter");
52 
53     size_t argc = PUBLISH_NOTIFICATION_MAX;
54     napi_value argv[PUBLISH_NOTIFICATION_MAX] = {nullptr};
55     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
56     if (argc < 1) {
57         ANS_LOGW("Wrong number of arguments.");
58         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
59         return nullptr;
60     }
61 
62     napi_valuetype valuetype = napi_undefined;
63     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
64     if (valuetype != napi_object) {
65         ANS_LOGW("Argument type error. Object expected.");
66         std::string msg = "Incorrect parameter types.The type of param must be object.";
67         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
68         return nullptr;
69     }
70 
71     // argv[0] : NotificationRequest
72     if (Common::GetNotificationRequest(env, argv[PARAM0], params.request) == nullptr) {
73         return nullptr;
74     }
75 
76     // argv[1] : userId / callback
77     if (argc >= PUBLISH_NOTIFICATION_MAX - 1) {
78         NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
79         if ((valuetype != napi_number) && (valuetype != napi_function)) {
80             ANS_LOGW("Wrong argument type. Function or object expected. Execute promise");
81             return Common::NapiGetNull(env);
82         }
83 
84         if (valuetype == napi_number) {
85             int32_t recvUserId = SUBSCRIBE_USER_INIT;
86             NAPI_CALL(env, napi_get_value_int32(env, argv[PARAM1], &recvUserId));
87             params.request.SetOwnerUserId(recvUserId);
88         } else {
89             napi_create_reference(env, argv[PARAM1], 1, &params.callback);
90         }
91     }
92 
93     // argv[2] : callback
94     if (argc >= PUBLISH_NOTIFICATION_MAX) {
95         if (GetCallback(env, argv[PARAM2], params) == nullptr) {
96             return nullptr;
97         }
98     }
99 
100     ANS_LOGD("end");
101     return Common::NapiGetNull(env);
102 }
103 
Publish(napi_env env,napi_callback_info info)104 napi_value Publish(napi_env env, napi_callback_info info)
105 {
106     ANS_LOGD("enter");
107 
108     ParametersInfoPublish params;
109     if (ParseParameters(env, info, params) == nullptr) {
110         return Common::NapiGetUndefined(env);
111     }
112 
113     napi_value promise = nullptr;
114     auto asynccallbackinfo = new (std::nothrow) AsyncCallbackInfoPublish {.env = env, .asyncWork = nullptr};
115     if (!asynccallbackinfo) {
116         ANS_LOGD("Asynccallbackinfo is nullptr.");
117         return Common::JSParaError(env, params.callback);
118     }
119 
120     asynccallbackinfo->request = params.request;
121     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
122 
123     ANS_LOGD("Create publish string.");
124     napi_value resourceName = nullptr;
125     napi_create_string_latin1(env, "publish", NAPI_AUTO_LENGTH, &resourceName);
126 
127     napi_create_async_work(env,
128         nullptr,
129         resourceName,
130         [](napi_env env, void *data) {
131             ANS_LOGI("Publish napi_create_async_work start");
132             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
133             if (asynccallbackinfo) {
134                 ANS_LOGI("Publish napi_create_async_work start notificationId = %{public}d, contentType = "
135                         "%{public}d",
136                     asynccallbackinfo->request.GetNotificationId(),
137                     asynccallbackinfo->request.GetContent()->GetContentType());
138 
139                 asynccallbackinfo->info.errorCode =
140                     NotificationHelper::PublishNotification(asynccallbackinfo->request);
141             }
142         },
143         [](napi_env env, napi_status status, void *data) {
144             ANS_LOGI("Publish napi_create_async_work complete start");
145             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
146             if (asynccallbackinfo) {
147                 Common::ReturnCallbackPromise(env, asynccallbackinfo->info, Common::NapiGetNull(env));
148                 if (asynccallbackinfo->info.callback != nullptr) {
149                     ANS_LOGD("Delete publish callback reference.");
150                     napi_delete_reference(env, asynccallbackinfo->info.callback);
151                 }
152                 napi_delete_async_work(env, asynccallbackinfo->asyncWork);
153                 delete asynccallbackinfo;
154                 asynccallbackinfo = nullptr;
155             }
156             ANS_LOGD("Publish work complete end.");
157         },
158         (void *)asynccallbackinfo,
159         &asynccallbackinfo->asyncWork);
160 
161     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
162 
163     if (asynccallbackinfo->info.isCallback) {
164         ANS_LOGD("publish callback is nullptr.");
165         return Common::NapiGetNull(env);
166     } else {
167         return promise;
168     }
169 }
170 
CheckProperty(const napi_env & env,const napi_value & content,const std::string & property)171 bool CheckProperty(const napi_env &env, const napi_value &content, const std::string &property)
172 {
173     ANS_LOGD("enter");
174 
175     bool hasProperty = false;
176 
177     NAPI_CALL_BASE(env, napi_has_named_property(env, content, property.data(), &hasProperty), false);
178     if (!hasProperty) {
179         ANS_LOGW("Property %{public}s expected.", property.c_str());
180     }
181     return hasProperty;
182 }
183 
GetStringProperty(const napi_env & env,const napi_value & content,const std::string & property,std::string & result)184 napi_value GetStringProperty(
185     const napi_env &env, const napi_value &content, const std::string &property, std::string &result)
186 {
187     ANS_LOGD("enter");
188 
189     if (!CheckProperty(env, content, property)) {
190         return nullptr;
191     }
192 
193     napi_valuetype valuetype = napi_undefined;
194     napi_value value = nullptr;
195     char str[STR_MAX_SIZE] = {0};
196     size_t strLen = 0;
197 
198     napi_get_named_property(env, content, property.data(), &value);
199     NAPI_CALL(env, napi_typeof(env, value, &valuetype));
200     if (valuetype != napi_string) {
201         ANS_LOGW("Wrong argument type. String expected.");
202         std::string msg = "Incorrect parameter types.The type of param must be string.";
203         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
204         return nullptr;
205     }
206     NAPI_CALL(env, napi_get_value_string_utf8(env, value, str, STR_MAX_SIZE - 1, &strLen));
207     ANS_LOGI("normal::%{public}s = %{public}s", property.c_str(), str);
208     result = str;
209     return Common::NapiGetNull(env);
210 }
211 
GetObjectProperty(const napi_env & env,const napi_value & content,const std::string & property,napi_value & result)212 napi_value GetObjectProperty(
213     const napi_env &env, const napi_value &content, const std::string &property, napi_value &result)
214 {
215     ANS_LOGD("enter");
216 
217     if (!CheckProperty(env, content, property)) {
218         return nullptr;
219     }
220 
221     napi_valuetype valuetype = napi_undefined;
222     napi_get_named_property(env, content, property.data(), &result);
223     NAPI_CALL(env, napi_typeof(env, result, &valuetype));
224     if (valuetype != napi_object) {
225         ANS_LOGW("Wrong argument type. Object expected.");
226         std::string msg = "Incorrect parameter types.The type of param must be object.";
227         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
228         return nullptr;
229     }
230     return Common::NapiGetNull(env);
231 }
232 
ParseShowOptions(const napi_env & env,const napi_callback_info & info,ParametersInfoPublish & params)233 napi_value ParseShowOptions(const napi_env &env, const napi_callback_info &info, ParametersInfoPublish &params)
234 {
235     ANS_LOGD("enter");
236 
237     size_t argc = SHOW_NOTIFICATION_MAX;
238     napi_value argv[SHOW_NOTIFICATION_MAX] = {nullptr};
239     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
240     if (argc == 0) {
241         ANS_LOGW("Wrong number of arguments.");
242         return nullptr;
243     }
244 
245     // argv[0] : ShowNotificationOptions
246     napi_valuetype valuetype = napi_undefined;
247     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
248     if (valuetype != napi_object) {
249         ANS_LOGW("Wrong argument type. Object expected.");
250         return nullptr;
251     }
252 
253     std::shared_ptr<NotificationNormalContent> normalContent = std::make_shared<NotificationNormalContent>();
254 
255     // contentTitle
256     std::string contentTitle;
257     if (GetStringProperty(env, argv[PARAM0], "contentTitle", contentTitle) != nullptr) {
258         normalContent->SetTitle(contentTitle);
259     }
260 
261     // contentText
262     std::string contentText;
263     if (GetStringProperty(env, argv[PARAM0], "contentText", contentText) != nullptr) {
264         normalContent->SetText(contentText);
265     }
266 
267     std::shared_ptr<NotificationContent> content = std::make_shared<NotificationContent>(normalContent);
268     params.request.SetContent(content);
269 
270     // clickAction
271     napi_value clickAction = nullptr;
272     if (GetObjectProperty(env, argv[PARAM0], "clickAction", clickAction) != nullptr) {
273         ANS_LOGD("create wantagent");
274         // bundleName & abilityName
275         std::shared_ptr<AAFwk::Want> want = std::make_shared<AAFwk::Want>();
276         std::string bundleName;
277         std::string abilityName;
278         if (GetStringProperty(env, clickAction, "bundleName", bundleName) == nullptr) {
279             return nullptr;
280         }
281         if (GetStringProperty(env, clickAction, "abilityName", abilityName) == nullptr) {
282             return nullptr;
283         }
284         want->SetElementName(bundleName, abilityName);
285         // uri
286         std::string uri;
287         if (GetStringProperty(env, clickAction, "uri", uri) == nullptr) {
288             return nullptr;
289         }
290         want->SetUri(uri);
291 
292         std::vector<std::shared_ptr<AAFwk::Want>> wants = {};
293         std::vector<WantAgentConstant::Flags> wantAgentFlags = {};
294         std::shared_ptr<AAFwk::WantParams> extraInfo = std::make_shared<AAFwk::WantParams>();
295         wants.emplace_back(want);
296         WantAgentInfo wantAgentInfo(-1, WantAgentConstant::OperationType::START_ABILITY, wantAgentFlags,
297             wants, extraInfo);
298         std::shared_ptr<AbilityRuntime::ApplicationContext> context = AbilityRuntime::Context::GetApplicationContext();
299 
300         std::shared_ptr<WantAgent> wantAgent = nullptr;
301         WantAgentHelper::GetWantAgent(context, wantAgentInfo, wantAgent);
302         params.request.SetWantAgent(wantAgent);
303     }
304 
305     ANS_LOGD("end");
306     return Common::NapiGetNull(env);
307 }
308 
ShowNotification(napi_env env,napi_callback_info info)309 napi_value ShowNotification(napi_env env, napi_callback_info info)
310 {
311     ANS_LOGD("enter");
312 
313     ParametersInfoPublish params;
314     if (ParseShowOptions(env, info, params) == nullptr) {
315         ANS_LOGW("parse showOptions failed");
316         return Common::NapiGetUndefined(env);
317     }
318 
319     auto asynccallbackinfo = new (std::nothrow) AsyncCallbackInfoPublish {.env = env, .asyncWork = nullptr};
320     if (!asynccallbackinfo) {
321         ANS_LOGW("failed to create asynccallbackinfo");
322         return Common::JSParaError(env, params.callback);
323     }
324 
325     asynccallbackinfo->request = params.request;
326 
327     ANS_LOGD("Create show string.");
328     napi_value resourceName = nullptr;
329     napi_create_string_latin1(env, "show", NAPI_AUTO_LENGTH, &resourceName);
330 
331     ANS_LOGI("before napi_create_async_work");
332     napi_create_async_work(env,
333         nullptr,
334         resourceName,
335         [](napi_env env, void *data) {
336             ANS_LOGD("ShowNotification work excute.");
337             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
338             if (asynccallbackinfo) {
339                 ANS_LOGI("Show napi_create_async_work start notificationId = %{public}d, contentType = "
340                         "%{public}d",
341                     asynccallbackinfo->request.GetNotificationId(),
342                     asynccallbackinfo->request.GetContent()->GetContentType());
343 
344                 asynccallbackinfo->info.errorCode =
345                     NotificationHelper::PublishNotification(asynccallbackinfo->request);
346             }
347         },
348         [](napi_env env, napi_status status, void *data) {
349             ANS_LOGD("ShowNotification work complete.");
350             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
351             if (asynccallbackinfo) {
352                 if (asynccallbackinfo->info.callback != nullptr) {
353                     napi_delete_reference(env, asynccallbackinfo->info.callback);
354                 }
355                 napi_delete_async_work(env, asynccallbackinfo->asyncWork);
356                 delete asynccallbackinfo;
357                 asynccallbackinfo = nullptr;
358             }
359             ANS_LOGI("Show napi_create_async_work complete end");
360         },
361         (void *)asynccallbackinfo,
362         &asynccallbackinfo->asyncWork);
363 
364     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
365     return nullptr;
366 }
367 
ParsePublishAsBundleParameters(const napi_env & env,const napi_callback_info & info,ParametersInfoPublish & params)368 napi_value ParsePublishAsBundleParameters(
369     const napi_env &env, const napi_callback_info &info, ParametersInfoPublish &params)
370 {
371     ANS_LOGD("enter");
372 
373     size_t argc = PUBLISH_AS_BUNDLE_MAX;
374     napi_value argv[PUBLISH_AS_BUNDLE_MAX] = {nullptr};
375     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
376     if (argc < 1) {
377         ANS_LOGW("Wrong number of arguments");
378         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
379         return nullptr;
380     }
381     // argv[0] : NotificationRequest / bundleOption
382     napi_valuetype valuetype = napi_undefined;
383     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
384     if (valuetype != napi_object) {
385         ANS_LOGW("Wrong argument type. Object expected.");
386         std::string msg = "Incorrect parameter types.The type of param must be object.";
387         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
388         return nullptr;
389     }
390 
391     if (argc > PUBLISH_AS_BUNDLEOPTION_MAX) {
392         if (Common::GetNotificationRequest(env, argv[PARAM0], params.request) == nullptr) {
393             return nullptr;
394         }
395     } else {
396         NotificationBundleOption option;
397         auto retValue = Common::GetBundleOption(env, argv[PARAM0], option);
398         if (retValue == nullptr) {
399             ANS_LOGE("GetBundleOption failed.");
400             Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
401             return nullptr;
402         }
403         params.request.SetOwnerBundleName(option.GetBundleName());
404         params.request.SetOwnerUid(option.GetUid());
405         params.request.SetIsAgentNotification(true);
406     }
407 
408     // argv[1] : bundleName / NotificationRequest
409     NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
410     if (valuetype != napi_string && valuetype != napi_number && valuetype != napi_boolean && valuetype != napi_object) {
411         ANS_LOGW("Error argument type. String number boolean object expected.");
412         std::string msg = "Incorrect parameter types.The type of param must be string or number or boolean.";
413         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
414         return nullptr;
415     }
416     if (argc > PUBLISH_AS_BUNDLEOPTION_MAX) {
417         if (valuetype == napi_string) {
418             char str[STR_MAX_SIZE] = {0};
419             size_t strLen = 0;
420             napi_get_value_string_utf8(env, argv[PARAM1], str, STR_MAX_SIZE - 1, &strLen);
421             params.request.SetOwnerBundleName(str);
422         } else if (valuetype == napi_number) {
423             int64_t number = 0;
424             NAPI_CALL(env, napi_get_value_int64(env, argv[PARAM1], &number));
425             params.request.SetOwnerBundleName(std::to_string(number));
426         } else {
427             bool result = false;
428             NAPI_CALL(env, napi_get_value_bool(env, argv[PARAM1], &result));
429             params.request.SetOwnerBundleName(std::to_string(result));
430         }
431     } else {
432         if (Common::GetNotificationRequest(env, argv[PARAM1], params.request) == nullptr) {
433             return nullptr;
434         }
435     }
436 
437     // argv[2] : userId
438     if (argc > PUBLISH_AS_BUNDLEOPTION_MAX) {
439         NAPI_CALL(env, napi_typeof(env, argv[PARAM2], &valuetype));
440         if (valuetype != napi_number) {
441             ANS_LOGW("Wrong argument type. Number expected.");
442             std::string msg = "Incorrect parameter types.The type of userId must be number.";
443             Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
444             return nullptr;
445         }
446         int32_t userId = 0;
447         napi_get_value_int32(env, argv[PARAM2], &userId);
448         params.request.SetOwnerUserId(userId);
449         params.request.SetIsAgentNotification(true);
450     }
451     // argv[3] : callback
452     if (argc >= PUBLISH_AS_BUNDLE_MAX) {
453         if (GetCallback(env, argv[PARAM3], params) == nullptr) {
454             return nullptr;
455         }
456     }
457 
458     ANS_LOGD("end");
459     return Common::NapiGetNull(env);
460 }
461 
PublishAsBundle(napi_env env,napi_callback_info info)462 napi_value PublishAsBundle(napi_env env, napi_callback_info info)
463 {
464     ANS_LOGD("enter");
465 
466     ParametersInfoPublish params;
467     if (ParsePublishAsBundleParameters(env, info, params) == nullptr) {
468         return Common::NapiGetUndefined(env);
469     }
470 
471     napi_value promise = nullptr;
472     auto asynccallbackinfo = new (std::nothrow) AsyncCallbackInfoPublish {.env = env, .asyncWork = nullptr};
473     if (!asynccallbackinfo) {
474         ANS_LOGD("Create asynccallbackinfo failed.");
475         return Common::JSParaError(env, params.callback);
476     }
477 
478     asynccallbackinfo->request = params.request;
479     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
480 
481     ANS_LOGD("Create publishasbundle string.");
482     napi_value resourceName = nullptr;
483     napi_create_string_latin1(env, "publishasbundle", NAPI_AUTO_LENGTH, &resourceName);
484 
485     napi_create_async_work(env,
486         nullptr,
487         resourceName,
488         [](napi_env env, void *data) {
489             ANS_LOGD("PublishAsBundle work excute.");
490             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
491             if (asynccallbackinfo) {
492                 ANS_LOGI("PublishAsBundle napi_create_async_work start notificationId = %{public}d, contentType = "
493                         "%{public}d",
494                     asynccallbackinfo->request.GetNotificationId(),
495                     asynccallbackinfo->request.GetContent()->GetContentType());
496 
497                 asynccallbackinfo->info.errorCode =
498                     NotificationHelper::PublishNotification(asynccallbackinfo->request);
499             }
500         },
501         [](napi_env env, napi_status status, void *data) {
502             ANS_LOGD("PublishAsBundle work complete.");
503             AsyncCallbackInfoPublish *asynccallbackinfo = static_cast<AsyncCallbackInfoPublish *>(data);
504             if (asynccallbackinfo) {
505                 Common::ReturnCallbackPromise(env, asynccallbackinfo->info, Common::NapiGetNull(env));
506                 if (asynccallbackinfo->info.callback != nullptr) {
507                     ANS_LOGD("Delete publishAsBundle callback reference.");
508                     napi_delete_reference(env, asynccallbackinfo->info.callback);
509                 }
510                 napi_delete_async_work(env, asynccallbackinfo->asyncWork);
511                 delete asynccallbackinfo;
512                 asynccallbackinfo = nullptr;
513             }
514             ANS_LOGD("PublishAsBundle complete end");
515         },
516         (void *)asynccallbackinfo,
517         &asynccallbackinfo->asyncWork);
518 
519     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
520 
521     if (asynccallbackinfo->info.isCallback) {
522         ANS_LOGD("publishAsBundle callback is nullptr.");
523         return Common::NapiGetNull(env);
524     } else {
525         return promise;
526     }
527 }
528 }  // namespace NotificationNapi
529 }  // namespace OHOS
530