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 "enable_notification.h"
17 
18 #include <uv.h>
19 
20 #include "ability_manager_client.h"
21 
22 #include "ans_dialog_host_client.h"
23 #include "ans_inner_errors.h"
24 #include "js_ans_dialog_callback.h"
25 
26 namespace OHOS {
27 namespace NotificationNapi {
28 const int ENABLE_NOTIFICATION_MAX_PARA = 3;
29 const int ENABLE_NOTIFICATION_MIN_PARA = 2;
30 const int IS_NOTIFICATION_ENABLE_MAX_PARA = 2;
31 
ParseParameters(const napi_env & env,const napi_callback_info & info,EnableParams & params)32 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info, EnableParams &params)
33 {
34     ANS_LOGD("enter");
35 
36     size_t argc = ENABLE_NOTIFICATION_MAX_PARA;
37     napi_value argv[ENABLE_NOTIFICATION_MAX_PARA] = {nullptr};
38     napi_value thisVar = nullptr;
39     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
40     if (argc < ENABLE_NOTIFICATION_MIN_PARA) {
41         ANS_LOGW("Wrong number of arguments.");
42         Common::NapiThrow(env, ERROR_PARAM_INVALID, MANDATORY_PARAMETER_ARE_LEFT_UNSPECIFIED);
43         return nullptr;
44     }
45 
46     // argv[0]: bundle
47     napi_valuetype valuetype = napi_undefined;
48     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
49     if (valuetype != napi_object) {
50         ANS_LOGW("Parameter type error. Object expected.");
51         std::string msg = "Incorrect parameter types.The type of param must be object.";
52         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
53         return nullptr;
54     }
55     auto retValue = Common::GetBundleOption(env, argv[PARAM0], params.option);
56     if (retValue == nullptr) {
57         ANS_LOGE("GetBundleOption failed.");
58         Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
59         return nullptr;
60     }
61 
62     // argv[1]: enable
63     NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
64     if (valuetype != napi_boolean) {
65         ANS_LOGW("Wrong argument type. Bool expected.");
66         std::string msg = "Incorrect parameter types.The type of param must be boolean.";
67         Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
68         return nullptr;
69     }
70     napi_get_value_bool(env, argv[PARAM1], &params.enable);
71 
72     // argv[2]:callback
73     if (argc >= ENABLE_NOTIFICATION_MAX_PARA) {
74         NAPI_CALL(env, napi_typeof(env, argv[PARAM2], &valuetype));
75         if (valuetype != napi_function) {
76             ANS_LOGW("Callback is not function excute promise.");
77             return Common::NapiGetNull(env);
78         }
79         napi_create_reference(env, argv[PARAM2], 1, &params.callback);
80     }
81 
82     return Common::NapiGetNull(env);
83 }
84 
ParseParameters(const napi_env & env,const napi_callback_info & info,IsEnableParams & params)85 napi_value ParseParameters(const napi_env &env, const napi_callback_info &info, IsEnableParams &params)
86 {
87     ANS_LOGD("enter");
88 
89     size_t argc = IS_NOTIFICATION_ENABLE_MAX_PARA;
90     napi_value argv[IS_NOTIFICATION_ENABLE_MAX_PARA] = {nullptr};
91     napi_value thisVar = nullptr;
92     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
93 
94     if (argc == 0) {
95         return Common::NapiGetNull(env);
96     }
97 
98     // argv[0]: bundle / userId / callback
99     napi_valuetype valuetype = napi_undefined;
100     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
101     if ((valuetype != napi_object) && (valuetype != napi_number) && (valuetype != napi_function)) {
102         ANS_LOGW("Parameter type error. Function or object expected. Excute promise.");
103         return Common::NapiGetNull(env);
104     }
105     if (valuetype == napi_object) {
106         auto retValue = Common::GetBundleOption(env, argv[PARAM0], params.option);
107         if (retValue == nullptr) {
108             ANS_LOGE("GetBundleOption failed.");
109             Common::NapiThrow(env, ERROR_PARAM_INVALID, PARAMETER_VERIFICATION_FAILED);
110             return nullptr;
111         }
112         params.hasBundleOption = true;
113     } else if (valuetype == napi_number) {
114         NAPI_CALL(env, napi_get_value_int32(env, argv[PARAM0], &params.userId));
115         params.hasUserId = true;
116     } else {
117         napi_create_reference(env, argv[PARAM0], 1, &params.callback);
118     }
119 
120     // argv[1]:callback
121     if (argc >= IS_NOTIFICATION_ENABLE_MAX_PARA) {
122         NAPI_CALL(env, napi_typeof(env, argv[PARAM1], &valuetype));
123         if (valuetype != napi_function) {
124             ANS_LOGW("Callback is not function excute promise.");
125             return Common::NapiGetNull(env);
126         }
127         napi_create_reference(env, argv[PARAM1], 1, &params.callback);
128     }
129 
130     return Common::NapiGetNull(env);
131 }
132 
AsyncCompleteCallbackEnableNotification(napi_env env,napi_status status,void * data)133 void AsyncCompleteCallbackEnableNotification(napi_env env, napi_status status, void *data)
134 {
135     ANS_LOGD("enter");
136     if (!data) {
137         ANS_LOGE("Invalid async callback data");
138         return;
139     }
140     AsyncCallbackInfoEnable *asynccallbackinfo = static_cast<AsyncCallbackInfoEnable *>(data);
141     if (asynccallbackinfo) {
142         Common::ReturnCallbackPromise(env, asynccallbackinfo->info, Common::NapiGetNull(env));
143         if (asynccallbackinfo->info.callback != nullptr) {
144             ANS_LOGD("Delete EnableNotification callback reference.");
145             napi_delete_reference(env, asynccallbackinfo->info.callback);
146         }
147         napi_delete_async_work(env, asynccallbackinfo->asyncWork);
148         delete asynccallbackinfo;
149         asynccallbackinfo = nullptr;
150     }
151 }
152 
EnableNotification(napi_env env,napi_callback_info info)153 napi_value EnableNotification(napi_env env, napi_callback_info info)
154 {
155     ANS_LOGD("enter");
156 
157     EnableParams params {};
158     if (ParseParameters(env, info, params) == nullptr) {
159         return Common::NapiGetUndefined(env);
160     }
161 
162     AsyncCallbackInfoEnable *asynccallbackinfo =
163         new (std::nothrow) AsyncCallbackInfoEnable {.env = env, .asyncWork = nullptr, .params = params};
164     if (!asynccallbackinfo) {
165         ANS_LOGD("Asynccallbackinfo is nullptr.");
166         return Common::JSParaError(env, params.callback);
167     }
168     napi_value promise = nullptr;
169     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
170 
171     ANS_LOGD("Create enableNotification string.");
172     napi_value resourceName = nullptr;
173     napi_create_string_latin1(env, "enableNotification", NAPI_AUTO_LENGTH, &resourceName);
174     // Asynchronous function call
175     napi_create_async_work(env,
176         nullptr,
177         resourceName,
178         [](napi_env env, void *data) {
179             ANS_LOGD("EnableNotification work excute.");
180             AsyncCallbackInfoEnable *asynccallbackinfo = static_cast<AsyncCallbackInfoEnable *>(data);
181             if (asynccallbackinfo) {
182                 std::string deviceId {""};
183                 asynccallbackinfo->info.errorCode = NotificationHelper::SetNotificationsEnabledForSpecifiedBundle(
184                     asynccallbackinfo->params.option, deviceId, asynccallbackinfo->params.enable);
185                 ANS_LOGI("asynccallbackinfo->info.errorCode = %{public}d", asynccallbackinfo->info.errorCode);
186             }
187         },
188         AsyncCompleteCallbackEnableNotification,
189         (void *)asynccallbackinfo,
190         &asynccallbackinfo->asyncWork);
191 
192     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
193 
194     if (asynccallbackinfo->info.isCallback) {
195         ANS_LOGD("enableNotification callback is nullptr.");
196         return Common::NapiGetNull(env);
197     } else {
198         return promise;
199     }
200 }
201 
AsyncCompleteCallbackIsNotificationEnabled(napi_env env,napi_status status,void * data)202 void AsyncCompleteCallbackIsNotificationEnabled(napi_env env, napi_status status, void *data)
203 {
204     ANS_LOGD("enter");
205     if (!data) {
206         ANS_LOGE("Invalid async callback data.");
207         return;
208     }
209     AsyncCallbackInfoIsEnable *asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable *>(data);
210     if (asynccallbackinfo) {
211         napi_value result = nullptr;
212         napi_get_boolean(env, asynccallbackinfo->allowed, &result);
213         if (asynccallbackinfo->newInterface) {
214             Common::CreateReturnValue(env, asynccallbackinfo->info, result);
215         } else {
216             Common::ReturnCallbackPromise(env, asynccallbackinfo->info, result);
217         }
218         if (asynccallbackinfo->info.callback != nullptr) {
219             napi_delete_reference(env, asynccallbackinfo->info.callback);
220         }
221         napi_delete_async_work(env, asynccallbackinfo->asyncWork);
222         delete asynccallbackinfo;
223         asynccallbackinfo = nullptr;
224     }
225 }
226 
IsNotificationEnabled(napi_env env,napi_callback_info info)227 napi_value IsNotificationEnabled(napi_env env, napi_callback_info info)
228 {
229     ANS_LOGD("enter");
230 
231     IsEnableParams params {};
232     if (ParseParameters(env, info, params) == nullptr) {
233         return Common::NapiGetUndefined(env);
234     }
235 
236     AsyncCallbackInfoIsEnable *asynccallbackinfo =
237         new (std::nothrow) AsyncCallbackInfoIsEnable {.env = env, .asyncWork = nullptr, .params = params};
238     if (!asynccallbackinfo) {
239         ANS_LOGD("Failed to create asynccallbackinfo.");
240         return Common::JSParaError(env, params.callback);
241     }
242     napi_value promise = nullptr;
243     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
244 
245     ANS_LOGD("Create isNotificationEnabled string.");
246     napi_value resourceName = nullptr;
247     napi_create_string_latin1(env, "isNotificationEnabled", NAPI_AUTO_LENGTH, &resourceName);
248     // Asynchronous function call
249     napi_create_async_work(env,
250         nullptr,
251         resourceName,
252         [](napi_env env, void *data) {
253             ANS_LOGD("IsNotificationEnabled work excute.");
254             AsyncCallbackInfoIsEnable *asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable *>(data);
255             if (asynccallbackinfo) {
256                 if (asynccallbackinfo->params.hasBundleOption) {
257                     ANS_LOGI("option.bundle : %{public}s option.uid : %{public}d",
258                         asynccallbackinfo->params.option.GetBundleName().c_str(),
259                         asynccallbackinfo->params.option.GetUid());
260                     asynccallbackinfo->info.errorCode = NotificationHelper::IsAllowedNotify(
261                         asynccallbackinfo->params.option, asynccallbackinfo->allowed);
262                 } else if (asynccallbackinfo->params.hasUserId) {
263                     ANS_LOGI("userId = %{public}d", asynccallbackinfo->params.userId);
264                     asynccallbackinfo->info.errorCode = NotificationHelper::IsAllowedNotify(
265                         asynccallbackinfo->params.userId, asynccallbackinfo->allowed);
266                 } else {
267                     asynccallbackinfo->info.errorCode = NotificationHelper::IsAllowedNotify(
268                         asynccallbackinfo->allowed);
269                 }
270                 ANS_LOGI("asynccallbackinfo->info.errorCode = %{public}d, allowed = %{public}d",
271                     asynccallbackinfo->info.errorCode, asynccallbackinfo->allowed);
272             }
273         },
274         AsyncCompleteCallbackIsNotificationEnabled,
275         (void *)asynccallbackinfo,
276         &asynccallbackinfo->asyncWork);
277 
278     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
279 
280     if (asynccallbackinfo->info.isCallback) {
281         ANS_LOGD("isNotificationEnabled callback is nullptr.");
282         return Common::NapiGetNull(env);
283     } else {
284         return promise;
285     }
286 }
287 
IsNotificationEnabledSelf(napi_env env,napi_callback_info info)288 napi_value IsNotificationEnabledSelf(napi_env env, napi_callback_info info)
289 {
290     ANS_LOGD("enter");
291 
292     IsEnableParams params {};
293     if (ParseParameters(env, info, params) == nullptr) {
294         return Common::NapiGetUndefined(env);
295     }
296 
297     AsyncCallbackInfoIsEnable *asynccallbackinfo =
298         new (std::nothrow) AsyncCallbackInfoIsEnable {.env = env, .asyncWork = nullptr, .params = params};
299     if (!asynccallbackinfo) {
300         ANS_LOGD("Create asynccallbackinfo fail.");
301         return Common::JSParaError(env, params.callback);
302     }
303     napi_value promise = nullptr;
304     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
305 
306     ANS_LOGD("Create IsNotificationEnabledSelf string.");
307     napi_value resourceName = nullptr;
308     napi_create_string_latin1(env, "IsNotificationEnabledSelf", NAPI_AUTO_LENGTH, &resourceName);
309     // Asynchronous function call
310     napi_create_async_work(env,
311         nullptr,
312         resourceName,
313         [](napi_env env, void *data) {
314             ANS_LOGD("IsNotificationEnabledSelf work excute.");
315             AsyncCallbackInfoIsEnable *asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable *>(data);
316             if (asynccallbackinfo) {
317                 if (asynccallbackinfo->params.hasBundleOption) {
318                     ANS_LOGE("Not allowed to query another application.");
319                 } else {
320                     asynccallbackinfo->info.errorCode =
321                         NotificationHelper::IsAllowedNotifySelf(asynccallbackinfo->allowed);
322                 }
323                 ANS_LOGI("asynccallbackinfo->info.errorCode = %{public}d, allowed = %{public}d",
324                     asynccallbackinfo->info.errorCode, asynccallbackinfo->allowed);
325             }
326         },
327         AsyncCompleteCallbackIsNotificationEnabled,
328         (void *)asynccallbackinfo,
329         &asynccallbackinfo->asyncWork);
330 
331     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
332 
333     if (asynccallbackinfo->info.isCallback) {
334         ANS_LOGD("isNotificationEnabledSelf callback is nullptr.");
335         return Common::NapiGetNull(env);
336     } else {
337         return promise;
338     }
339 }
340 
AsyncCompleteCallbackRequestEnableNotification(napi_env env,void * data)341 void AsyncCompleteCallbackRequestEnableNotification(napi_env env, void *data)
342 {
343     ANS_LOGD("enter");
344     if (data == nullptr) {
345         ANS_LOGE("Invalid async callback data.");
346         return;
347     }
348     auto* asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable*>(data);
349     napi_value result = nullptr;
350     napi_get_undefined(env, &result);
351     if (asynccallbackinfo->newInterface) {
352         Common::CreateReturnValue(env, asynccallbackinfo->info, result);
353     } else {
354         Common::ReturnCallbackPromise(env, asynccallbackinfo->info, result);
355     }
356     if (asynccallbackinfo->info.callback != nullptr) {
357         napi_delete_reference(env, asynccallbackinfo->info.callback);
358     }
359     napi_delete_async_work(env, asynccallbackinfo->asyncWork);
360     delete asynccallbackinfo;
361 }
362 
RequestEnableNotification(napi_env env,napi_callback_info info)363 napi_value RequestEnableNotification(napi_env env, napi_callback_info info)
364 {
365     ANS_LOGD("enter");
366     IsEnableParams params {};
367     if (ParseParameters(env, info, params) == nullptr) {
368         return Common::NapiGetUndefined(env);
369     }
370 
371     AsyncCallbackInfoIsEnable *asynccallbackinfo =
372         new (std::nothrow) AsyncCallbackInfoIsEnable {.env = env, .asyncWork = nullptr, .params = params};
373 
374     if (!asynccallbackinfo) {
375         ANS_LOGD("Failed to create asynccallbackinfo.");
376         return Common::JSParaError(env, params.callback);
377     }
378     napi_value promise = nullptr;
379     Common::PaddingCallbackPromiseInfo(env, params.callback, asynccallbackinfo->info, promise);
380 
381     napi_value resourceName = nullptr;
382     napi_create_string_latin1(env, "RequestEnableNotification", NAPI_AUTO_LENGTH, &resourceName);
383 
384     auto ipcCall = [](napi_env env, void* data) {
385         ANS_LOGD("enter");
386         if (data == nullptr) {
387             ANS_LOGE("data is invalid");
388             return;
389         }
390         auto* asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable*>(data);
391         std::string deviceId {""};
392         sptr<AnsDialogHostClient> client = nullptr;
393         if (!AnsDialogHostClient::CreateIfNullptr(client)) {
394             asynccallbackinfo->info.errorCode = ERR_ANS_DIALOG_IS_POPPING;
395             return;
396         }
397         asynccallbackinfo->info.errorCode =
398             NotificationHelper::RequestEnableNotification(deviceId, client,
399                 asynccallbackinfo->params.callerToken);
400         ANS_LOGI("done, code is %{public}d.", asynccallbackinfo->info.errorCode);
401     };
402     auto jsCb = [](napi_env env, napi_status status, void* data) {
403         ANS_LOGD("enter");
404         if (data == nullptr) {
405             AnsDialogHostClient::Destroy();
406             return;
407         }
408         auto* asynccallbackinfo = static_cast<AsyncCallbackInfoIsEnable*>(data);
409         ErrCode errCode = asynccallbackinfo->info.errorCode;
410         if (errCode != ERR_ANS_DIALOG_POP_SUCCEEDED) {
411             ANS_LOGE("error, code is %{public}d.", errCode);
412             AnsDialogHostClient::Destroy();
413             AsyncCompleteCallbackRequestEnableNotification(env, static_cast<void*>(asynccallbackinfo));
414             return;
415         }
416         // Dialog is popped
417         auto jsCallback = std::make_unique<JsAnsDialogCallback>();
418         if (!jsCallback->Init(env, asynccallbackinfo, AsyncCompleteCallbackRequestEnableNotification) ||
419             !AnsDialogHostClient::SetDialogCallbackInterface(std::move(jsCallback))
420         ) {
421             ANS_LOGE("error");
422             asynccallbackinfo->info.errorCode = ERROR_INTERNAL_ERROR;
423             AnsDialogHostClient::Destroy();
424             AsyncCompleteCallbackRequestEnableNotification(env, static_cast<void*>(asynccallbackinfo));
425             return;
426         }
427     };
428 
429     // Asynchronous function call
430     napi_create_async_work(env,
431         nullptr,
432         resourceName,
433         ipcCall,
434         jsCb,
435         static_cast<void*>(asynccallbackinfo),
436         &asynccallbackinfo->asyncWork);
437 
438     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
439 
440     if (asynccallbackinfo->info.isCallback) {
441         ANS_LOGD("RequestEnableNotification callback is nullptr.");
442         return Common::NapiGetNull(env);
443     } else {
444         return promise;
445     }
446 }
447 
448 }  // namespace NotificationNapi
449 }  // namespace OHOS
450