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