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 #include "ani_request_permission.h"
16
17 #include <atomic>
18 #include <map>
19 #include <mutex>
20 #include <thread>
21
22 #include "ability_manager_client.h"
23 #include "accesstoken_kit.h"
24 #include "accesstoken_common_log.h"
25 #include "ani_common.h"
26 #include "hisysevent.h"
27 #include "token_setproc.h"
28 #include "want.h"
29
30 namespace OHOS {
31 namespace Security {
32 namespace AccessToken {
33 namespace {
34 #define SETTER_METHOD_NAME(property) "" #property
35 const std::string PERMISSION_KEY = "ohos.user.grant.permission";
36 const std::string STATE_KEY = "ohos.user.grant.permission.state";
37 const std::string RESULT_KEY = "ohos.user.grant.permission.result";
38 const std::string EXTENSION_TYPE_KEY = "ability.want.params.uiExtensionType";
39 const std::string UI_EXTENSION_TYPE = "sys/commonUI";
40 const std::string ORI_PERMISSION_MANAGER_BUNDLE_NAME = "com.ohos.permissionmanager";
41 const std::string TOKEN_KEY = "ohos.ability.params.token";
42 const std::string CALLBACK_KEY = "ohos.ability.params.callback";
43 const std::string WINDOW_RECTANGLE_LEFT_KEY = "ohos.ability.params.request.left";
44 const std::string WINDOW_RECTANGLE_TOP_KEY = "ohos.ability.params.request.top";
45 const std::string WINDOW_RECTANGLE_HEIGHT_KEY = "ohos.ability.params.request.height";
46 const std::string WINDOW_RECTANGLE_WIDTH_KEY = "ohos.ability.params.request.width";
47 const std::string REQUEST_TOKEN_KEY = "ohos.ability.params.request.token";
48 std::shared_ptr<RequestInstanceControl> g_requestInstanceControl = nullptr;
49 std::mutex g_requestInstanceControlLock;
50 }
51
GetRequestInstanceControl()52 static std::shared_ptr<RequestInstanceControl> GetRequestInstanceControl()
53 {
54 std::lock_guard<std::mutex> lock(g_requestInstanceControlLock);
55 if (g_requestInstanceControl == nullptr) {
56 g_requestInstanceControl = std::make_shared<RequestInstanceControl>();
57 }
58 return g_requestInstanceControl;
59 }
60
RequestAsyncContext(ani_vm * vm,ani_env * env)61 RequestAsyncContext::RequestAsyncContext(ani_vm* vm, ani_env* env)
62 : RequestAsyncContextBase(vm, env, REQUEST_PERMISSIONS_FROM_USER)
63 {}
64
~RequestAsyncContext()65 RequestAsyncContext::~RequestAsyncContext()
66 {
67 Clear();
68 }
69
CreateServiceExtension(std::shared_ptr<RequestAsyncContext> asyncContext)70 static void CreateServiceExtension(std::shared_ptr<RequestAsyncContext> asyncContext)
71 {
72 auto abilityContext = OHOS::AbilityRuntime::Context::ConvertTo<OHOS::AbilityRuntime::AbilityContext>(
73 asyncContext->stageContext_);
74 if (abilityContext == nullptr) {
75 LOGE(ATM_DOMAIN, ATM_TAG, "Convert to AbilityContext failed. " \
76 "UIExtension ability can not pop service ablility window!");
77 asyncContext->needDynamicRequest = false;
78 asyncContext->result.errorCode = RET_FAILED;
79 return;
80 }
81 OHOS::sptr<IRemoteObject> remoteObject = new (std::nothrow) AuthorizationResult(asyncContext);
82 if (remoteObject == nullptr) {
83 LOGE(ATM_DOMAIN, ATM_TAG, "Create window failed!");
84 asyncContext->needDynamicRequest = false;
85 asyncContext->result.errorCode = RET_FAILED;
86 return;
87 }
88 OHOS::AAFwk::Want want;
89 want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.grantServiceAbilityName);
90 want.SetParam(PERMISSION_KEY, asyncContext->permissionList);
91 want.SetParam(STATE_KEY, asyncContext->permissionsState);
92 want.SetParam(TOKEN_KEY, abilityContext->GetToken());
93 want.SetParam(CALLBACK_KEY, remoteObject);
94
95 int32_t left = 0;
96 int32_t top = 0;
97 int32_t width = 0;
98 int32_t height = 0;
99 abilityContext->GetWindowRect(left, top, width, height);
100 want.SetParam(WINDOW_RECTANGLE_LEFT_KEY, left);
101 want.SetParam(WINDOW_RECTANGLE_TOP_KEY, top);
102 want.SetParam(WINDOW_RECTANGLE_WIDTH_KEY, width);
103 want.SetParam(WINDOW_RECTANGLE_HEIGHT_KEY, height);
104 want.SetParam(REQUEST_TOKEN_KEY, abilityContext->GetToken());
105 int32_t ret = OHOS::AAFwk::AbilityManagerClient::GetInstance()->RequestDialogService(
106 want, abilityContext->GetToken());
107 LOGI(ATM_DOMAIN, ATM_TAG, "Request end, ret: %{public}d, tokenId: %{public}d, permNum: %{public}zu", ret,
108 asyncContext->tokenId, asyncContext->permissionList.size());
109 }
110
StartExtensionAbility(std::shared_ptr<RequestAsyncContextBase> asyncContext)111 void RequestAsyncContext::StartExtensionAbility(std::shared_ptr<RequestAsyncContextBase> asyncContext)
112 {
113 if (asyncContext == nullptr) {
114 LOGI(ATM_DOMAIN, ATM_TAG, "asyncContext is nullptr.");
115 return;
116 }
117 if (uiExtensionFlag) {
118 OHOS::AAFwk::Want want;
119 want.SetElementName(this->info.grantBundleName, this->info.grantAbilityName);
120 want.SetParam(PERMISSION_KEY, this->permissionList);
121 want.SetParam(STATE_KEY, this->permissionsState);
122 want.SetParam(EXTENSION_TYPE_KEY, UI_EXTENSION_TYPE);
123 CreateUIExtension(want,
124 std::static_pointer_cast<RequestAsyncContext>(asyncContext),
125 GetRequestInstanceControl());
126 } else {
127 CreateServiceExtension(std::static_pointer_cast<RequestAsyncContext>(asyncContext));
128 }
129 }
130
131 template<typename valueType>
CallSetter(ani_env * env,ani_class cls,ani_object object,const char * setterName,valueType value)132 static inline bool CallSetter(ani_env* env, ani_class cls, ani_object object, const char* setterName, valueType value)
133 {
134 ani_status status = ANI_ERROR;
135 ani_field fieldValue;
136 if (env->Class_FindField(cls, setterName, &fieldValue) != ANI_OK) {
137 LOGE(ATM_DOMAIN, ATM_TAG, "Class_FindField Fail %{public}d, name: %{public}s.",
138 static_cast<int32_t>(status), setterName);
139 return false;
140 }
141 if ((status = env->Object_SetField_Ref(object, fieldValue, value)) != ANI_OK) {
142 LOGE(ATM_DOMAIN, ATM_TAG, "Object_SetField_Ref Fail %{public}d, name: %{public}s.",
143 static_cast<int32_t>(status), setterName);
144 return false;
145 }
146 return true;
147 }
148
WrapResult(ani_env * env)149 ani_object RequestAsyncContext::WrapResult(ani_env* env)
150 {
151 ani_status status = ANI_ERROR;
152 ani_class cls = nullptr;
153 if (env == nullptr) {
154 LOGE(ATM_DOMAIN, ATM_TAG, "env is nullptr");
155 return nullptr;
156 }
157 if ((status = env->FindClass("Lsecurity/PermissionRequestResult/PermissionRequestResult;", &cls)) != ANI_OK) {
158 LOGE(ATM_DOMAIN, ATM_TAG, "FindClass status %{public}d ", static_cast<int32_t>(status));
159 return nullptr;
160 }
161 if (cls == nullptr) {
162 LOGE(ATM_DOMAIN, ATM_TAG, "null cls");
163 return nullptr;
164 }
165 ani_method method = nullptr;
166 if ((status = env->Class_FindMethod(cls, "<ctor>", ":V", &method)) != ANI_OK) {
167 LOGE(ATM_DOMAIN, ATM_TAG, "Class_FindMethod status %{public}d ", static_cast<int32_t>(status));
168 return nullptr;
169 }
170 ani_object aObject = nullptr;
171 if ((status = env->Object_New(cls, method, &aObject)) != ANI_OK) {
172 LOGE(ATM_DOMAIN, ATM_TAG, "Object_New status %{public}d ", static_cast<int32_t>(status));
173 return nullptr;
174 }
175 auto state = this->needDynamicRequest ? this->grantResults : this->permissionsState;
176 ani_ref aniPerms = CreateAniArrayString(env, permissionList);
177 ani_ref aniAuthRes = CreateAniArrayInt(env, state);
178 ani_ref aniDiasShownRes = CreateAniArrayBool(env, dialogShownResults);
179 ani_ref aniErrorReasons = CreateAniArrayInt(env, errorReasons);
180 if (aniPerms == nullptr || aniAuthRes == nullptr || aniDiasShownRes == nullptr || aniErrorReasons == nullptr ||
181 !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(permissions), aniPerms) ||
182 !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(authResults), aniAuthRes) ||
183 !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(dialogShownResults), aniDiasShownRes) ||
184 !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(errorReasons), aniErrorReasons)) {
185 aObject = nullptr;
186 }
187 DeleteReference(env, aniPerms);
188 DeleteReference(env, aniAuthRes);
189 DeleteReference(env, aniDiasShownRes);
190 DeleteReference(env, aniErrorReasons);
191 return aObject;
192 }
193
HandleResult(const std::vector<std::string> & permissionList,const std::vector<int32_t> & grantResults)194 void RequestAsyncContext::HandleResult(const std::vector<std::string>& permissionList,
195 const std::vector<int32_t>& grantResults)
196 {
197 std::vector<int32_t> newGrantResults;
198 size_t size = permissionList.size();
199 for (size_t i = 0; i < size; i++) {
200 int32_t result = static_cast<int32_t>(this->permissionsState[i]);
201 if (this->permissionsState[i] == AccessToken::DYNAMIC_OPER) {
202 result = this->result.errorCode == AccessToken::RET_SUCCESS ?
203 grantResults[i] : AccessToken::INVALID_OPER;
204 }
205 newGrantResults.emplace_back(result);
206 }
207
208 if (newGrantResults.empty()) {
209 LOGE(ATM_DOMAIN, ATM_TAG, "GrantResults empty");
210 result.errorCode = RET_FAILED;
211 }
212 this->grantResults.assign(newGrantResults.begin(), newGrantResults.end());
213 }
214
IsDynamicRequest()215 bool RequestAsyncContext::IsDynamicRequest()
216 {
217 std::vector<AccessToken::PermissionListState> permList;
218 for (const auto& permission : this->permissionList) {
219 AccessToken::PermissionListState permState;
220 permState.permissionName = permission;
221 permState.errorReason = SERVICE_ABNORMAL;
222 permState.state = AccessToken::INVALID_OPER;
223 permList.emplace_back(permState);
224 }
225 auto ret = AccessToken::AccessTokenKit::GetSelfPermissionsState(permList, this->info);
226 LOGI(ATM_DOMAIN, ATM_TAG,
227 "TokenID: %{public}d, bundle: %{public}s, uiExAbility: %{public}s, serExAbility: %{public}s.",
228 this->tokenId, this->info.grantBundleName.c_str(), this->info.grantAbilityName.c_str(),
229 this->info.grantServiceAbilityName.c_str());
230 if (ret == AccessToken::FORBIDDEN_OPER) {
231 LOGE(ATM_DOMAIN, ATM_TAG, "FORBIDDEN_OPER");
232 for (auto& perm : permList) {
233 perm.state = AccessToken::INVALID_OPER;
234 perm.errorReason = PRIVACY_STATEMENT_NOT_AGREED;
235 }
236 }
237 for (const auto& permState : permList) {
238 LOGI(ATM_DOMAIN, ATM_TAG, "Permission: %{public}s: state: %{public}d, errorReason: %{public}d",
239 permState.permissionName.c_str(), permState.state, permState.errorReason);
240 this->permissionsState.emplace_back(permState.state);
241 this->dialogShownResults.emplace_back(permState.state == AccessToken::TypePermissionOper::DYNAMIC_OPER);
242 this->errorReasons.emplace_back(permState.errorReason);
243 }
244 if (permList.size() != this->permissionList.size()) {
245 LOGE(ATM_DOMAIN, ATM_TAG, "permList.size: %{public}zu, permissionList.size: %{public}zu", permList.size(),
246 this->permissionList.size());
247 return false;
248 }
249 return ret == AccessToken::TypePermissionOper::DYNAMIC_OPER;
250 }
251
CheckDynamicRequest()252 bool RequestAsyncContext::CheckDynamicRequest()
253 {
254 permissionsState.clear();
255 dialogShownResults.clear();
256 if (!IsDynamicRequest()) {
257 HandleResult(this->permissionList, this->permissionsState);
258 FinishCallback();
259 return false;
260 }
261 return true;
262 }
263
ProcessUIExtensionCallback(const OHOS::AAFwk::Want & result)264 void RequestAsyncContext::ProcessUIExtensionCallback(const OHOS::AAFwk::Want& result)
265 {
266 this->permissionList = result.GetStringArrayParam(PERMISSION_KEY);
267 this->permissionsState = result.GetIntArrayParam(RESULT_KEY);
268 HandleResult(permissionList, permissionsState);
269 }
270
ConvertErrorCode(int32_t code)271 int32_t RequestAsyncContext::ConvertErrorCode(int32_t code)
272 {
273 return BusinessErrorAni::GetStsErrorCode(result.errorCode);
274 }
275
NoNeedUpdate()276 bool RequestAsyncContext::NoNeedUpdate()
277 {
278 return true;
279 }
280
RequestPermissionsFromUserProcess(std::shared_ptr<RequestAsyncContext> & asyncContext)281 static void RequestPermissionsFromUserProcess(std::shared_ptr<RequestAsyncContext>& asyncContext)
282 {
283 if (!asyncContext->IsDynamicRequest()) {
284 LOGE(ATM_DOMAIN, ATM_TAG, "It does not need to request permission");
285 asyncContext->needDynamicRequest = false;
286 if ((asyncContext->permissionsState.empty()) && (asyncContext->result.errorCode == RET_SUCCESS)) {
287 LOGE(ATM_DOMAIN, ATM_TAG, "GrantResults empty");
288 asyncContext->result.errorCode = RET_FAILED;
289 }
290 return;
291 }
292
293 asyncContext->GetInstanceId();
294 if (asyncContext->info.grantBundleName == ORI_PERMISSION_MANAGER_BUNDLE_NAME) {
295 LOGI(ATM_DOMAIN, ATM_TAG,
296 "Pop service extension dialog, uiContentFlag=%{public}d", asyncContext->uiContentFlag);
297 if (asyncContext->uiContentFlag) {
298 GetRequestInstanceControl()->AddCallbackByInstanceId(asyncContext);
299 } else {
300 CreateServiceExtension(asyncContext);
301 }
302 } else if (asyncContext->instanceId == -1) {
303 LOGE(ATM_DOMAIN, ATM_TAG, "Pop service extension dialog, instanceId is -1.");
304 CreateServiceExtension(asyncContext);
305 (void)HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::ACCESS_TOKEN, "REQUEST_PERMISSIONS_FROM_USER",
306 HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "BUNDLENAME", asyncContext->bundleName.c_str(),
307 "UIEXTENSION_FLAG", false);
308 } else {
309 LOGI(ATM_DOMAIN, ATM_TAG, "Pop ui extension dialog");
310 asyncContext->uiExtensionFlag = true;
311 GetRequestInstanceControl()->AddCallbackByInstanceId(asyncContext);
312 (void)HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::ACCESS_TOKEN, "REQUEST_PERMISSIONS_FROM_USER",
313 HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "BUNDLENAME", asyncContext->bundleName, "UIEXTENSION_FLAG",
314 false);
315 if (!asyncContext->uiExtensionFlag) {
316 LOGW(ATM_DOMAIN, ATM_TAG, "Pop uiextension dialog fail, start to pop service extension dialog.");
317 GetRequestInstanceControl()->AddCallbackByInstanceId(asyncContext);
318 }
319 }
320 }
321
ParseParameter(ani_env * env,ani_object aniContext,ani_array_ref aniPermissionList,ani_object callback,std::shared_ptr<RequestAsyncContext> & asyncContext)322 static bool ParseParameter(ani_env* env, ani_object aniContext, ani_array_ref aniPermissionList,
323 ani_object callback, std::shared_ptr<RequestAsyncContext>& asyncContext)
324 {
325 if (!asyncContext->FillInfoFromContext(aniContext)) {
326 LOGE(ATM_DOMAIN, ATM_TAG, "FillInfoFromContext failed.");
327 BusinessErrorAni::ThrowParameterTypeError(env, STS_ERROR_PARAM_ILLEGAL,
328 GetParamErrorMsg("context", "UIAbility or UIExtension Context"));
329 return false;
330 }
331 asyncContext->permissionList = ParseAniStringVector(env, aniPermissionList);
332 if (!AniParseCallback(env, reinterpret_cast<ani_ref>(callback), asyncContext->callbackRef)) {
333 return false;
334 }
335 return true;
336 }
337
RequestPermissionsFromUserExecute(ani_env * env,ani_object object,ani_object aniContext,ani_array_ref aniPermissionList,ani_object callback)338 void RequestPermissionsFromUserExecute([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object,
339 ani_object aniContext, ani_array_ref aniPermissionList, ani_object callback)
340 {
341 if (env == nullptr || aniPermissionList == nullptr || callback == nullptr) {
342 LOGE(ATM_DOMAIN, ATM_TAG, "env or aniPermissionList or callback is null.");
343 return;
344 }
345 ani_vm* vm;
346 ani_status status = env->GetVM(&vm);
347 if (status != ANI_OK) {
348 LOGE(ATM_DOMAIN, ATM_TAG, "GetVM failed, error=%{public}d.", static_cast<int32_t>(status));
349 return;
350 }
351 std::shared_ptr<RequestAsyncContext> asyncContext = std::make_shared<RequestAsyncContext>(vm, env);
352 if (!ParseParameter(env, aniContext, aniPermissionList, callback, asyncContext)) {
353 return;
354 }
355
356 static AccessTokenID selfTokenID = static_cast<AccessTokenID>(GetSelfTokenID());
357 if (selfTokenID != asyncContext->tokenId) {
358 LOGE(ATM_DOMAIN, ATM_TAG,
359 "The context tokenID: %{public}d, selfTokenID: %{public}d.", asyncContext->tokenId, selfTokenID);
360
361 ani_ref nullRef = nullptr;
362 env->GetNull(&nullRef);
363 ani_object result = reinterpret_cast<ani_object>(nullRef);
364 ani_object error = BusinessErrorAni::CreateError(env, STS_ERROR_INNER, GetErrorMessage(STS_ERROR_INNER,
365 "The specified context does not belong to the current application."));
366 (void)ExecuteAsyncCallback(env, callback, error, result);
367 return;
368 }
369 RequestPermissionsFromUserProcess(asyncContext);
370 if (asyncContext->needDynamicRequest) {
371 return;
372 }
373 asyncContext->FinishCallback();
374 LOGI(ATM_DOMAIN, ATM_TAG, "uiExtensionFlag: %{public}d, uiContentFlag: %{public}d",
375 asyncContext->uiExtensionFlag, asyncContext->uiContentFlag);
376 }
377
378
AuthorizationResult(std::shared_ptr<RequestAsyncContext> & data)379 AuthorizationResult::AuthorizationResult(std::shared_ptr<RequestAsyncContext>& data) : data_(data)
380 {
381 LOGI(ATM_DOMAIN, ATM_TAG, "AuthorizationResult");
382 }
383
~AuthorizationResult()384 AuthorizationResult::~AuthorizationResult()
385 {
386 LOGI(ATM_DOMAIN, ATM_TAG, "~AuthorizationResult");
387 }
388
GrantResultsCallback(const std::vector<std::string> & permissionList,const std::vector<int32_t> & grantResults)389 void AuthorizationResult::GrantResultsCallback(
390 const std::vector<std::string>& permissionList, const std::vector<int32_t>& grantResults)
391 {
392 std::shared_ptr<RequestAsyncContext> asyncContext = data_;
393 if (asyncContext == nullptr) {
394 return;
395 }
396 LOGI(ATM_DOMAIN, ATM_TAG, "GrantResultsCallback");
397 asyncContext->HandleResult(permissionList, grantResults);
398 asyncContext->FinishCallback();
399 }
400
WindowShownCallback()401 void AuthorizationResult::WindowShownCallback()
402 {
403 std::shared_ptr<RequestAsyncContext> asyncContext = data_;
404 if (asyncContext == nullptr) {
405 return;
406 }
407 OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->stageContext_);
408 if ((uiContent == nullptr) || !(asyncContext->uiContentFlag)) {
409 return;
410 }
411 GetRequestInstanceControl()->ExecCallback(asyncContext->instanceId);
412 }
413 } // namespace AccessToken
414 } // namespace Security
415 } // namespace OHOS