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 ¶ms)
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, ¶ms.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 ¶ms)
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, ¶ms.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 ¶ms)
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 ¶ms)
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