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