1 /*
2 * Copyright (c) 2025 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 "ani_open_settings.h"
17
18 #include "ans_log_wrapper.h"
19 #include "ets_error_utils.h"
20 #include "notification_helper.h"
21 #include "ani_common_util.h"
22 #include "sts_throw_erro.h"
23 #include "ani_ans_dialog_callback.h"
24
25 namespace OHOS {
26 namespace NotificationManagerSts {
27 using namespace OHOS::Notification;
28 static std::atomic<bool> isExist = false;
29 const int32_t ERR__INVALID_WANT = 1011;
GetOpenSettingsInfo(ani_env * env,ani_object content,std::shared_ptr<OpenSettingsInfo> & info)30 bool GetOpenSettingsInfo(ani_env *env, ani_object content, std::shared_ptr<OpenSettingsInfo> &info)
31 {
32 ANS_LOGD("enter");
33
34 ani_status status = ANI_OK;
35 ani_boolean stageMode = ANI_FALSE;
36 status = OHOS::AbilityRuntime::IsStageContext(env, content, stageMode);
37 ANS_LOGD("status %{public}d, stageMode %{public}d", status, stageMode);
38 if (ANI_OK != status || stageMode != ANI_TRUE) {
39 ANS_LOGE("Only support stage mode");
40 std::string msg = "Incorrect parameter types.Only support stage mode.";
41 ANS_LOGE("sts GetOpenSettingsInfo ERROR_PARAM_INVALID");
42 OHOS::NotificationSts::ThrowError(env, ERROR_PARAM_INVALID, msg);
43 return false;
44 }
45 info->context = OHOS::AbilityRuntime::GetStageModeContext(env, content);
46 return true;
47 }
48
CreateUiExtCallback(ani_env * env,std::shared_ptr<SettingsModalExtensionCallback> & uiExtCallback,Ace::ModalUIExtensionCallbacks & uiExtensionCallbacks,std::shared_ptr<OpenSettingsInfo> & info,std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> & abilityContext,std::string & bundleName)49 bool CreateUiExtCallback(ani_env *env, std::shared_ptr<SettingsModalExtensionCallback>& uiExtCallback,
50 Ace::ModalUIExtensionCallbacks& uiExtensionCallbacks, std::shared_ptr<OpenSettingsInfo> &info,
51 std::shared_ptr<OHOS::AbilityRuntime::AbilityContext>& abilityContext, std::string &bundleName)
52 {
53 if (!uiExtCallback->Init(env, info, StsAsyncCompleteCallbackOpenSettings)) {
54 ANS_LOGE("error");
55 info->errorCode = OHOS::Notification::ERROR_INTERNAL_ERROR;
56 StsAsyncCompleteCallbackOpenSettings(env, info);
57 return false;
58 }
59 uiExtCallback->SetAbilityContext(abilityContext);
60 uiExtCallback->SetBundleName(bundleName);
61 uiExtensionCallbacks = {
62 .onRelease =
63 std::bind(&SettingsModalExtensionCallback::OnRelease, uiExtCallback, std::placeholders::_1),
64 .onResult = std::bind(&SettingsModalExtensionCallback::OnResult, uiExtCallback,
65 std::placeholders::_1, std::placeholders::_2),
66 .onReceive =
67 std::bind(&SettingsModalExtensionCallback::OnReceive, uiExtCallback, std::placeholders::_1),
68 .onError = std::bind(&SettingsModalExtensionCallback::OnError, uiExtCallback,
69 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
70 .onRemoteReady =
71 std::bind(&SettingsModalExtensionCallback::OnRemoteReady, uiExtCallback, std::placeholders::_1),
72 .onDestroy = std::bind(&SettingsModalExtensionCallback::OnDestroy, uiExtCallback),
73 };
74 return true;
75 }
76
CreateSettingsUIExtension(std::shared_ptr<OHOS::AbilityRuntime::Context> context,std::string & bundleName,ani_env * env,std::shared_ptr<OpenSettingsInfo> & info)77 bool CreateSettingsUIExtension(std::shared_ptr<OHOS::AbilityRuntime::Context> context, std::string &bundleName,
78 ani_env *env, std::shared_ptr<OpenSettingsInfo> &info)
79 {
80 if (context == nullptr) {
81 ANS_LOGE("Get context failed");
82 return false;
83 }
84
85 std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext =
86 OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(context);
87 if (abilityContext == nullptr) {
88 ANS_LOGE("abilityContext is null");
89 return false;
90 }
91 auto uiContent = abilityContext->GetUIContent();
92 if (uiContent == nullptr) {
93 ANS_LOGE("uiContent is null");
94 return false;
95 }
96
97 AAFwk::Want want;
98 std::string targetBundleName = "com.ohos.sceneboard";
99 std::string targetAbilityName = "NotificationManangerUIExtensionAbility";
100 want.SetElementName(targetBundleName, targetAbilityName);
101
102 std::string typeKey = "ability.want.params.uiExtensionType";
103 std::string typeValue = "sys/commonUI";
104 want.SetParam(typeKey, typeValue);
105
106 auto uiExtCallback = std::make_shared<SettingsModalExtensionCallback>();
107 Ace::ModalUIExtensionCallbacks uiExtensionCallbacks;
108 if (!CreateUiExtCallback(env, uiExtCallback, uiExtensionCallbacks, info, abilityContext,
109 bundleName)) {
110 ANS_LOGE("CreateUiExtCallback fail");
111 return false;
112 }
113
114 Ace::ModalUIExtensionConfig config;
115 config.isProhibitBack = true;
116
117 int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config);
118 ANS_LOGI("Create end, sessionId: %{public}d", sessionId);
119 if (sessionId == 0) {
120 ANS_LOGE("Create component failed, sessionId is 0");
121 return false;
122 }
123 uiExtCallback->SetSessionId(sessionId);
124 return true;
125 }
126
StsAsyncCompleteCallbackOpenSettings(ani_env * env,std::shared_ptr<OpenSettingsInfo> info)127 void StsAsyncCompleteCallbackOpenSettings(ani_env *env, std::shared_ptr<OpenSettingsInfo> info)
128 {
129 ANS_LOGD("enter");
130 if (env == nullptr) {
131 ANS_LOGD("env is null");
132 return;
133 }
134 ani_status status;
135 int32_t errorCode = ERR_OK;
136 if (info->errorCode == OHOS::Notification::ERROR_SETTING_WINDOW_EXIST) {
137 errorCode = OHOS::Notification::ERROR_SETTING_WINDOW_EXIST;
138 } else if (info->errorCode == ERR__INVALID_WANT) {
139 errorCode = ERR__INVALID_WANT;
140 } else {
141 errorCode = info->errorCode ==
142 ERR_OK ? ERR_OK : NotificationSts::GetExternalCode(info->errorCode);
143 }
144
145 if (errorCode == ERR_OK) {
146 ANS_LOGD("Resolve. errorCode %{public}d", errorCode);
147 ani_object ret = OHOS::AppExecFwk::CreateInt(env, errorCode);
148 if (ret == nullptr) {
149 ANS_LOGD("createInt faild");
150 NotificationSts::ThrowErrorWithMsg(env, "");
151 return;
152 }
153 if (ANI_OK != (status = env->PromiseResolver_Resolve(info->resolver, static_cast<ani_ref>(ret)))) {
154 ANS_LOGD("PromiseResolver_Resolve faild. status %{public}d", status);
155 NotificationSts::ThrowErrorWithMsg(env, "");
156 }
157 } else {
158 std::string errMsg = OHOS::NotificationSts::FindAnsErrMsg(errorCode);
159 ANS_LOGD("reject. errorCode %{public}d errMsg %{public}s", errorCode, errMsg.c_str());
160 ani_error rejection =
161 static_cast<ani_error>(OHOS::AbilityRuntime::EtsErrorUtil::CreateError(env, errorCode, errMsg));
162 if (ANI_OK != (status = env->PromiseResolver_Reject(info->resolver, rejection))) {
163 ANS_LOGD("PromiseResolver_Resolve faild. status %{public}d", status);
164 NotificationSts::ThrowErrorWithMsg(env, "");
165 }
166 }
167 }
168
AniOpenNotificationSettings(ani_env * env,ani_object content)169 ani_object AniOpenNotificationSettings(ani_env *env, ani_object content)
170 {
171 ANS_LOGD("sts AniOpenNotificationSettings call");
172 std::shared_ptr<OpenSettingsInfo> info = std::make_shared<OpenSettingsInfo>();
173 if (!GetOpenSettingsInfo(env, content, info)) {
174 ANS_LOGE("sts AniOpenNotificationSettings GetOpenSettingsInfo fail");
175 return nullptr;
176 }
177 if (info->context == nullptr) {
178 ANS_LOGE("sts AniOpenNotificationSettings context is null");
179 NotificationSts::ThrowErrorWithMsg(env, "");
180 return nullptr;
181 }
182 std::string bundleName {""};
183 if (isExist.exchange(true)) {
184 ANS_LOGE("sts AniOpenNotificationSettings ERROR_SETTING_WINDOW_EXIST");
185 OHOS::NotificationSts::ThrowError(env, OHOS::Notification::ERROR_SETTING_WINDOW_EXIST,
186 NotificationSts::FindAnsErrMsg(OHOS::Notification::ERROR_SETTING_WINDOW_EXIST));
187 return nullptr;
188 }
189 ani_object aniPromise {};
190 ani_resolver aniResolver {};
191 if (ANI_OK != env->Promise_New(&aniResolver, &aniPromise)) {
192 ANS_LOGD("Promise_New faild");
193 return nullptr;
194 }
195 info->resolver = aniResolver;
196 bool success = CreateSettingsUIExtension(info->context, bundleName, env, info);
197 if (success) {
198 info->errorCode = OHOS::Notification::ERR_ANS_DIALOG_POP_SUCCEEDED;
199 } else {
200 info->errorCode = OHOS::Notification::ERROR_INTERNAL_ERROR;
201 }
202 if (info->errorCode != ERR_ANS_DIALOG_POP_SUCCEEDED) {
203 ANS_LOGE("error, code is %{public}d.", info->errorCode);
204 StsAsyncCompleteCallbackOpenSettings(env, info);
205 isExist.store(false);
206 return nullptr;
207 }
208 ANS_LOGD("sts AniOpenNotificationSettings end");
209
210 return aniPromise;
211 }
212
SettingsModalExtensionCallback()213 SettingsModalExtensionCallback::SettingsModalExtensionCallback()
214 {}
215
~SettingsModalExtensionCallback()216 SettingsModalExtensionCallback::~SettingsModalExtensionCallback()
217 {}
218
Init(ani_env * env,std::shared_ptr<OpenSettingsInfo> info,StsSettingsModalExtensionCallbackComplete * complete)219 bool SettingsModalExtensionCallback::Init(ani_env *env, std::shared_ptr<OpenSettingsInfo> info,
220 StsSettingsModalExtensionCallbackComplete *complete)
221 {
222 if (env == nullptr || info == nullptr || complete == nullptr) {
223 ANS_LOGE("invalid data");
224 return false;
225 }
226 ani_status status = ANI_OK;
227 if ((status = env->GetVM(&vm_)) != ANI_OK) {
228 ANS_LOGD("GetVM faild. status %{public}d", status);
229 return false;
230 }
231 info_ = info;
232 complete_ = complete;
233 return true;
234 }
235
ProcessStatusChanged(int32_t code,bool isAsync)236 void SettingsModalExtensionCallback::ProcessStatusChanged(int32_t code, bool isAsync)
237 {
238 ANS_LOGD("enter");
239 if (vm_ == nullptr || info_ == nullptr || complete_ == nullptr) {
240 ANS_LOGE("invalid data");
241 AnsDialogHostClient::Destroy();
242 return;
243 }
244 info_->errorCode = code;
245
246 ani_env* env;
247 ani_status aniResult = ANI_ERROR;
248 ani_options aniArgs { 0, nullptr };
249 if (isAsync) {
250 aniResult = vm_->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env);
251 } else {
252 aniResult = vm_->GetEnv(ANI_VERSION_1, &env);
253 }
254 if (aniResult != ANI_OK) {
255 ANS_LOGD("AttachCurrentThread error. result: %{public}d.", aniResult);
256 return;
257 }
258 if (complete_) {
259 complete_(env, info_);
260 }
261 if (isAsync && (aniResult = vm_->DetachCurrentThread()) != ANI_OK) {
262 ANS_LOGD("DetachCurrentThread error. result: %{public}d.", aniResult);
263 return;
264 }
265 }
266
267 /*
268 * when UIExtensionAbility use terminateSelfWithResult
269 */
OnResult(int32_t resultCode,const AAFwk::Want & result)270 void SettingsModalExtensionCallback::OnResult(int32_t resultCode, const AAFwk::Want& result)
271 {
272 ANS_LOGD("OnResult");
273 }
274
275 /*
276 * when UIExtensionAbility send message to UIExtensionComponent
277 */
OnReceive(const AAFwk::WantParams & receive)278 void SettingsModalExtensionCallback::OnReceive(const AAFwk::WantParams& receive)
279 {
280 ANS_LOGD("OnReceive");
281 }
282
283 /*
284 * when UIExtensionAbility disconnect or use terminate or process die
285 * releaseCode is 0 when process normal exit
286 */
OnRelease(int32_t releaseCode)287 void SettingsModalExtensionCallback::OnRelease(int32_t releaseCode)
288 {
289 ANS_LOGD("OnRelease");
290 ReleaseOrErrorHandle(releaseCode);
291 }
292
293 /*
294 * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error
295 */
OnError(int32_t code,const std::string & name,const std::string & message)296 void SettingsModalExtensionCallback::OnError(int32_t code, const std::string& name, const std::string& message)
297 {
298 ANS_LOGE("OnError, code = %{public}d,name = %{public}s, message = %{public}s", code, name.c_str(), message.c_str());
299 ReleaseOrErrorHandle(code);
300 ProcessStatusChanged(code, false);
301 }
302
303 /*
304 * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init,
305 * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy
306 */
OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy> & uiProxy)307 void SettingsModalExtensionCallback::OnRemoteReady(const std::shared_ptr<Ace::ModalUIExtensionProxy>& uiProxy)
308 {
309 ANS_LOGI("OnRemoteReady");
310 ProcessStatusChanged(0, true);
311 }
312
313 /*
314 * when UIExtensionComponent destructed
315 */
OnDestroy()316 void SettingsModalExtensionCallback::OnDestroy()
317 {
318 ANS_LOGI("OnDestroy");
319 isExist.store(false);
320 }
321
322
SetSessionId(int32_t sessionId)323 void SettingsModalExtensionCallback::SetSessionId(int32_t sessionId)
324 {
325 this->sessionId_ = sessionId;
326 }
327
SetBundleName(std::string bundleName)328 void SettingsModalExtensionCallback::SetBundleName(std::string bundleName)
329 {
330 this->bundleName_ = bundleName;
331 }
332
SetAbilityContext(std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext)333 void SettingsModalExtensionCallback::SetAbilityContext(
334 std::shared_ptr<OHOS::AbilityRuntime::AbilityContext> abilityContext)
335 {
336 this->abilityContext_ = abilityContext;
337 }
338
ReleaseOrErrorHandle(int32_t code)339 void SettingsModalExtensionCallback::ReleaseOrErrorHandle(int32_t code)
340 {
341 ANS_LOGD("ReleaseOrErrorHandle start");
342 Ace::UIContent* uiContent = this->abilityContext_->GetUIContent();
343 if (uiContent == nullptr) {
344 ANS_LOGE("uiContent is null");
345 return;
346 }
347 uiContent->CloseModalUIExtension(this->sessionId_);
348 ANS_LOGD("ReleaseOrErrorHandle end");
349 return;
350 }
351 }
352 }