• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2024 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 "napi_open_settings.h"
17 #include <uv.h>
18 #include "napi_base_context.h"
19 #include "ans_inner_errors.h"
20 
21 namespace OHOS {
22 namespace NotificationNapi {
23 const int OPEN_NOTIFICATION_SETTINGS_MAX_PARA = 1;
24 static napi_env env_ = nullptr;
25 static AsyncCallbackInfoOpenSettings* callbackInfo_ = nullptr;
26 static JsAnsCallbackComplete* complete_ = nullptr;
27 static std::atomic<bool> isExist = false;
28 
NapiAsyncCompleteCallbackOpenSettings(napi_env env,void * data)29 void NapiAsyncCompleteCallbackOpenSettings(napi_env env, void *data)
30 {
31     ANS_LOGD("called");
32     if (data == nullptr) {
33         ANS_LOGE("null data");
34         return;
35     }
36     auto* asynccallbackinfo = static_cast<AsyncCallbackInfoOpenSettings*>(data);
37     napi_value result = nullptr;
38     napi_get_undefined(env, &result);
39     int32_t errorCode = ERR_OK;
40     if (asynccallbackinfo->info.errorCode == ERROR_SETTING_WINDOW_EXIST) {
41         errorCode = ERROR_SETTING_WINDOW_EXIST;
42     } else {
43         errorCode = asynccallbackinfo->info.errorCode ==
44             ERR_OK ? ERR_OK : OHOS::Notification::ErrorToExternal(asynccallbackinfo->info.errorCode);
45     }
46     if (asynccallbackinfo->info.isCallback) {
47         Common::SetCallback(env, asynccallbackinfo->info.callback, errorCode, result, true);
48     } else {
49         Common::SetPromise(env, asynccallbackinfo->info.deferred, errorCode, result, true);
50     }
51     if (asynccallbackinfo->info.callback != nullptr) {
52         napi_delete_reference(env, asynccallbackinfo->info.callback);
53     }
54     napi_delete_async_work(env, asynccallbackinfo->asyncWork);
55     delete asynccallbackinfo;
56 }
57 
NapiOpenNotificationSettings(napi_env env,napi_callback_info info)58 napi_value NapiOpenNotificationSettings(napi_env env, napi_callback_info info)
59 {
60     ANS_LOGD("start");
61     OpenSettingsParams params {};
62     if (ParseOpenSettingsParameters(env, info, params) == nullptr) {
63         Common::NapiThrow(env, ERROR_PARAM_INVALID);
64         return Common::NapiGetUndefined(env);
65     }
66 
67     AsyncCallbackInfoOpenSettings *asynccallbackinfo = new (std::nothrow) AsyncCallbackInfoOpenSettings {
68             .env = env, .params = params};
69     if (!asynccallbackinfo) {
70         return Common::JSParaError(env, nullptr);
71     }
72     napi_value promise = nullptr;
73     Common::PaddingCallbackPromiseInfo(env, nullptr, asynccallbackinfo->info, promise);
74 
75     napi_value resourceName = nullptr;
76     napi_create_string_latin1(env, "openNotificationSettings", NAPI_AUTO_LENGTH, &resourceName);
77 
78     auto createExtension = [](napi_env env, void* data) {
79     };
80     auto jsCb = [](napi_env env, napi_status status, void* data) {
81         ANS_LOGD("called");
82         if (data == nullptr) {
83             ANS_LOGE("null data");
84             return;
85         }
86         auto* asynccallbackinfo = static_cast<AsyncCallbackInfoOpenSettings*>(data);
87         CreateExtension(asynccallbackinfo);
88         ErrCode errCode = asynccallbackinfo->info.errorCode;
89         if (errCode != ERR_ANS_DIALOG_POP_SUCCEEDED) {
90             ANS_LOGE("errCode: %{public}d.", errCode);
91             NapiAsyncCompleteCallbackOpenSettings(env, static_cast<void*>(asynccallbackinfo));
92             if (errCode != ERROR_SETTING_WINDOW_EXIST) {
93                 isExist.store(false);
94             }
95             return;
96         }
97         if (!Init(env, asynccallbackinfo, NapiAsyncCompleteCallbackOpenSettings)) {
98             ANS_LOGE("error");
99             asynccallbackinfo->info.errorCode = ERROR_INTERNAL_ERROR;
100             NapiAsyncCompleteCallbackOpenSettings(env, static_cast<void*>(asynccallbackinfo));
101             return;
102         }
103         ANS_LOGD("jsCb end");
104     };
105 
106     // Asynchronous function call
107     napi_create_async_work(env,
108         nullptr,
109         resourceName,
110         createExtension,
111         jsCb,
112         static_cast<void*>(asynccallbackinfo),
113         &asynccallbackinfo->asyncWork);
114 
115     napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated);
116     ANS_LOGD("end");
117     return promise;
118 }
119 
ParseOpenSettingsParameters(const napi_env & env,const napi_callback_info & info,OpenSettingsParams & params)120 napi_value ParseOpenSettingsParameters(const napi_env &env, const napi_callback_info &info, OpenSettingsParams &params)
121 {
122     ANS_LOGD("called");
123 
124     size_t argc = OPEN_NOTIFICATION_SETTINGS_MAX_PARA;
125     napi_value argv[OPEN_NOTIFICATION_SETTINGS_MAX_PARA] = {nullptr};
126     napi_value thisVar = nullptr;
127     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
128 
129     if (argc == 0) {
130         return Common::NapiGetNull(env);
131     }
132 
133     // argv[0]: context
134     napi_valuetype valuetype = napi_undefined;
135     NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype));
136     if ((valuetype != napi_object) && (valuetype != napi_function)) {
137         ANS_LOGW("Wrong argument type. Function or object expected. Excute promise.");
138         return Common::NapiGetNull(env);
139     }
140     if (valuetype == napi_object) {
141         bool stageMode = false;
142         napi_status status = OHOS::AbilityRuntime::IsStageContext(env, argv[PARAM0], stageMode);
143         if (status == napi_ok && stageMode) {
144             auto context = OHOS::AbilityRuntime::GetStageModeContext(env, argv[PARAM0]);
145             sptr<IRemoteObject> callerToken = context->GetToken();
146             params.context = context;
147         } else {
148             ANS_LOGE("Only support stage mode");
149             std::string msg = "Incorrect parameter types.Only support stage mode.";
150             Common::NapiThrow(env, ERROR_PARAM_INVALID, msg);
151             return nullptr;
152         }
153     }
154     return Common::NapiGetNull(env);
155 }
156 
CreateSettingsUIExtension(std::shared_ptr<OHOS::AbilityRuntime::Context> context,std::string & bundleName)157 bool CreateSettingsUIExtension(std::shared_ptr<OHOS::AbilityRuntime::Context> context, std::string &bundleName)
158 {
159     if (context == nullptr) {
160         ANS_LOGE("null context");
161         return false;
162     }
163 
164     std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
165         OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
166     if (abilityContext == nullptr) {
167         ANS_LOGE("null abilityContex");
168         return false;
169     }
170     auto uiContent = abilityContext->GetUIContent();
171     if (uiContent == nullptr) {
172         ANS_LOGE("null uiContent");
173         return false;
174     }
175 
176     AAFwk::Want want;
177     std::string targetBundleName = "com.ohos.sceneboard";
178     std::string targetAbilityName = "NotificationManangerUIExtensionAbility";
179     want.SetElementName(targetBundleName, targetAbilityName);
180 
181     std::string typeKey = "ability.want.params.uiExtensionType";
182     std::string typeValue = "sys/commonUI";
183     want.SetParam(typeKey, typeValue);
184 
185     auto uiExtCallback = std::make_shared<SettingsModalExtensionCallback>();
186     uiExtCallback->SetAbilityContext(abilityContext);
187     uiExtCallback->SetBundleName(bundleName);
188     Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = {
189         .onRelease =
190             std::bind(&SettingsModalExtensionCallback::OnRelease, uiExtCallback, std::placeholders::_1),
191         .onResult = std::bind(&SettingsModalExtensionCallback::OnResult, uiExtCallback,
192             std::placeholders::_1, std::placeholders::_2),
193         .onReceive =
194             std::bind(&SettingsModalExtensionCallback::OnReceive, uiExtCallback, std::placeholders::_1),
195         .onError = std::bind(&SettingsModalExtensionCallback::OnError, uiExtCallback,
196             std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
197         .onRemoteReady =
198             std::bind(&SettingsModalExtensionCallback::OnRemoteReady, uiExtCallback, std::placeholders::_1),
199         .onDestroy = std::bind(&SettingsModalExtensionCallback::OnDestroy, uiExtCallback),
200     };
201 
202     Ace::ModalUIExtensionConfig config;
203     config.isProhibitBack = true;
204 
205     int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config);
206     ANS_LOGI("Create end, sessionId: %{public}d", sessionId);
207     if (sessionId == 0) {
208         ANS_LOGE("Create component failed, sessionId is 0");
209         return false;
210     }
211     uiExtCallback->SetSessionId(sessionId);
212     return true;
213 }
214 
Init(napi_env env,AsyncCallbackInfoOpenSettings * callbackInfo,JsAnsCallbackComplete complete)215 bool Init(napi_env env, AsyncCallbackInfoOpenSettings* callbackInfo,
216     JsAnsCallbackComplete complete)
217 {
218     ANS_LOGD("called");
219     if (env == nullptr || callbackInfo == nullptr || complete == nullptr) {
220         ANS_LOGE("invalid data");
221         return false;
222     }
223     env_ = env;
224     callbackInfo_ = callbackInfo;
225     complete_ = complete;
226     return true;
227 }
228 
ProcessStatusChanged(int32_t code)229 void ProcessStatusChanged(int32_t code)
230 {
231     ANS_LOGD("called");
232     std::unique_ptr<AsyncCallbackInfoOpenSettings> callbackInfo(callbackInfo_);
233     if (env_ == nullptr || callbackInfo == nullptr || complete_ == nullptr) {
234         ANS_LOGE("invalid data");
235         return;
236     }
237 
238     callbackInfo->info.errorCode = code;
239 
240     uv_loop_s* loop = nullptr;
241     napi_get_uv_event_loop(env_, &loop);
242     if (loop == nullptr) {
243         ANS_LOGE("null loop");
244         return;
245     }
246 
247     auto work = std::make_unique<uv_work_t>();
248     struct WorkData {
249         decltype(env_) env = nullptr;
250         decltype(callbackInfo_) callbackInfo = nullptr;
251         decltype(complete_) complete = nullptr;
252     };
253     auto workData = std::make_unique<WorkData>();
254     workData->env = env_;
255     workData->callbackInfo = callbackInfo_;
256     workData->complete = complete_;
257 
258     work->data = static_cast<void*>(workData.get());
259     auto jsCb = [](uv_work_t* work, int status) {
260         ANS_LOGD("called jsCb");
261         std::unique_ptr<uv_work_t> workSP(work);
262         if (work == nullptr || work->data == nullptr) {
263             ANS_LOGE("invalid data");
264             return;
265         }
266         auto* data = static_cast<WorkData*>(work->data);
267         std::unique_ptr<WorkData> dataSP(data);
268         std::unique_ptr<AsyncCallbackInfoOpenSettings> callbackInfoSP(data->callbackInfo);
269         if (data->env == nullptr ||
270             data->callbackInfo == nullptr ||
271             data->complete == nullptr) {
272             return;
273         }
274         auto* callbackInfoPtr = callbackInfoSP.release();
275         data->complete(data->env, static_cast<void*>(callbackInfoPtr));
276     };
277 
278     int ret = uv_queue_work_with_qos(loop,
279         work.get(),
280         [](uv_work_t *work) {},
281         jsCb,
282         uv_qos_user_initiated);
283     if (ret != 0) {
284         ANS_LOGE("uv_queue_work failed");
285         return;
286     }
287     callbackInfo.release();
288     workData.release();
289     work.release();
290 }
291 
CreateExtension(AsyncCallbackInfoOpenSettings * asynccallbackinfo)292 void CreateExtension(AsyncCallbackInfoOpenSettings* asynccallbackinfo)
293 {
294     if (asynccallbackinfo->params.context != nullptr) {
295         ANS_LOGD("stage mode");
296         std::string bundleName {""};
297         if (isExist.exchange(true)) {
298             ANS_LOGE("SettingsUIExtension existed");
299             asynccallbackinfo->info.errorCode = ERROR_SETTING_WINDOW_EXIST;
300             return;
301         }
302         bool success = CreateSettingsUIExtension(asynccallbackinfo->params.context, bundleName);
303         if (success) {
304             asynccallbackinfo->info.errorCode = ERR_ANS_DIALOG_POP_SUCCEEDED;
305         } else {
306             asynccallbackinfo->info.errorCode = ERROR_INTERNAL_ERROR;
307         }
308     } else {
309         ANS_LOGD("un stage mode");
310     }
311     ANS_LOGI("errorCode: %{public}d", asynccallbackinfo->info.errorCode);
312 }
313 
SettingsModalExtensionCallback()314 SettingsModalExtensionCallback::SettingsModalExtensionCallback()
315 {}
316 
~SettingsModalExtensionCallback()317 SettingsModalExtensionCallback::~SettingsModalExtensionCallback()
318 {}
319 
320 
321 /*
322  * when UIExtensionAbility use terminateSelfWithResult
323  */
OnResult(int32_t resultCode,const AAFwk::Want & result)324 void SettingsModalExtensionCallback::OnResult(int32_t resultCode, const AAFwk::Want& result)
325 {
326     ANS_LOGD("called");
327 }
328 
329 /*
330  * when UIExtensionAbility send message to UIExtensionComponent
331  */
OnReceive(const AAFwk::WantParams & receive)332 void SettingsModalExtensionCallback::OnReceive(const AAFwk::WantParams& receive)
333 {
334     ANS_LOGD("called");
335 }
336 
337 /*
338  * when UIExtensionAbility disconnect or use terminate or process die
339  * releaseCode is 0 when process normal exit
340  */
OnRelease(int32_t releaseCode)341 void SettingsModalExtensionCallback::OnRelease(int32_t releaseCode)
342 {
343     ANS_LOGD("OnRelease");
344     ReleaseOrErrorHandle(releaseCode);
345 }
346 
347 /*
348  * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error
349  */
OnError(int32_t code,const std::string & name,const std::string & message)350 void SettingsModalExtensionCallback::OnError(int32_t code, const std::string& name, const std::string& message)
351 {
352     ANS_LOGD("called, code = %{public}d,name = %{public}s, message = %{public}s", code, name.c_str(), message.c_str());
353     ReleaseOrErrorHandle(code);
354     ProcessStatusChanged(code);
355 }
356 
357 /*
358  * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init,
359  * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy
360  */
OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy> & uiProxy)361 void SettingsModalExtensionCallback::OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy>& uiProxy)
362 {
363     ANS_LOGD("called");
364     ProcessStatusChanged(0);
365 }
366 
367 /*
368  * when UIExtensionComponent destructed
369  */
OnDestroy()370 void SettingsModalExtensionCallback::OnDestroy()
371 {
372     ANS_LOGD("called");
373     isExist.store(false);
374 }
375 
376 
SetSessionId(int32_t sessionId)377 void SettingsModalExtensionCallback::SetSessionId(int32_t sessionId)
378 {
379     this->sessionId_ = sessionId;
380 }
381 
SetBundleName(std::string bundleName)382 void SettingsModalExtensionCallback::SetBundleName(std::string bundleName)
383 {
384     this->bundleName_ = bundleName;
385 }
386 
SetAbilityContext(std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext)387 void SettingsModalExtensionCallback::SetAbilityContext(
388     std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext)
389 {
390     this->abilityContext_ = abilityContext;
391 }
392 
ReleaseOrErrorHandle(int32_t code)393 void SettingsModalExtensionCallback::ReleaseOrErrorHandle(int32_t code)
394 {
395     ANS_LOGD("start");
396     Ace::UIContent* uiContent = this->abilityContext_->GetUIContent();
397     if (uiContent == nullptr) {
398         ANS_LOGE("null uiContent");
399         return;
400     }
401     uiContent->CloseModalUIExtension(this->sessionId_);
402     ANS_LOGD("end");
403     return;
404 }
405 
406 }  // namespace NotificationNapi
407 }  // namespace OHOS