diff --git a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets index 0f685d4eb473a04ad27b422fcfaa70ab90450e44..6328fad93673df2e3d16cc12f011ae497aa683fe 100644 --- a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets +++ b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets @@ -141,10 +141,18 @@ export default namespace abilityAccessCtrl { permissionList: Array, requestCallback: AsyncCallback ): void; - + requestPermissionsFromUser( context: Context, permissionList: Array): Promise; + + requestPermissionOnSettingExecute( + context: Context, + permissionList: Array): Array; + + requestPermissionOnSetting( + context: Context, + permissionList: Array): Promise>; } class AtManagerInner implements AtManager { @@ -219,5 +227,29 @@ export default namespace abilityAccessCtrl { }); return p; } + + native requestPermissionOnSettingExecute( + context: Context, + permissionList: Array): Array; + + requestPermissionOnSetting(context: Context, permissionList: Array): + Promise> { + validateRequestParams(context, permissionList); + let p = new Promise>((resolve: (v: Array) => + void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): Array => + { return new AtManagerInner().requestPermissionOnSettingExecute( + context, permissionList) as Array}); + p1.then((e: NullishType)=> { + hilog.info(LOG_TAG, 'requestPermissionOnSetting',`Promise success.`); + let r : Array = e as Array; + resolve(r); + }).catch((err: BusinessError): void => { + hilog.info(LOG_TAG, 'requestPermissionOnSetting',`Promise failed.`); + reject(err); + }); + }); + return p; + } } } \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h b/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h index c2a0b1e78641dc4491dcf5e181e50c62a056f541..a59e24264d9a65bd0eb47d5e9ab30b3d39a2d22f 100644 --- a/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h +++ b/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h @@ -155,6 +155,57 @@ struct ResultCallback { std::shared_ptr data = nullptr; }; +struct RequestPermOnSettingAsyncContext { + AccessTokenID tokenId = 0; + int32_t result = RET_SUCCESS; + PermissionGrantInfo info; + int32_t resultCode = -1; + + std::vector permissionList; + napi_value requestResult = nullptr; + int32_t errorCode = -1; + std::vector stateList; + + std::shared_ptr abilityContext; + std::shared_ptr uiExtensionContext; + bool uiAbilityFlag = false; + std::mutex loadlock; + +#ifdef EVENTHANDLER_ENABLE + std::shared_ptr handler_ = + std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif +}; + +class PermissonOnSettingUICallback { +public: + explicit PermissonOnSettingUICallback(ani_env* env, + const std::shared_ptr& reqContext); + ~PermissonOnSettingUICallback(); + void SetSessionId(int32_t sessionId); + void ReleaseHandler(int32_t code); + void OnRelease(int32_t releaseCode); + void OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result); + void OnReceive(const OHOS::AAFwk::WantParams& request); + void OnError(int32_t code, const std::string& name, const std::string& message); + void OnRemoteReady(const std::shared_ptr& uiProxy); + void OnDestroy(); + +private: + ani_env* env_; + std::shared_ptr reqContext_ = nullptr; + int32_t sessionId_ = 0; + + std::mutex lockReleaseFlag; + bool releaseFlag = false; +}; + +struct PermissonOnSettingResultCallback { + int32_t jsCode; + std::vector stateList; + std::shared_ptr data = nullptr; +}; + std::map>> RequestAsyncInstanceControl::instanceIdMap_; std::mutex RequestAsyncInstanceControl::instanceIdMutex_; } // namespace AccessToken diff --git a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp index ed15891e1ad077479250d8a671274bac05eddcff..e16776562d7d6612dc8e8c3c80950c048f920883 100644 --- a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp +++ b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp @@ -19,6 +19,7 @@ #include #include "access_token.h" +#include "access_token_error.h" #include "accesstoken_kit.h" #include "accesstoken_log.h" #include "ani_base_context.h" @@ -33,8 +34,11 @@ namespace OHOS { namespace Security { namespace AccessToken { std::mutex g_lockFlag; +std::mutex g_lockWindowFlag; +bool g_windowFlag = false; static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniAbilityAccessCtrl" }; constexpr int32_t MAX_LENGTH = 256; +constexpr int32_t REQUEST_REALDY_EXIST = 1; const std::string PERMISSION_KEY = "ohos.user.grant.permission"; const std::string STATE_KEY = "ohos.user.grant.permission.state"; @@ -49,6 +53,8 @@ const std::string WINDOW_RECTANGLE_TOP_KEY = "ohos.ability.params.request.top"; const std::string WINDOW_RECTANGLE_HEIGHT_KEY = "ohos.ability.params.request.height"; const std::string WINDOW_RECTANGLE_WIDTH_KEY = "ohos.ability.params.request.width"; const std::string REQUEST_TOKEN_KEY = "ohos.ability.params.request.token"; +const std::string RESULT_ERROR_KEY = "ohos.user.setting.error_code"; +const std::string PERMISSION_RESULT_KEY = "ohos.user.setting.permission.result"; #define SETTER_METHOD_NAME(property) "" #property @@ -106,17 +112,20 @@ static bool IsDynamicRequest(std::shared_ptr& asyncContext) return ret == AccessToken::TypePermissionOper::DYNAMIC_OPER; } -static OHOS::Ace::UIContent* GetUIContent(const std::shared_ptr& asyncContext) +static OHOS::Ace::UIContent* GetUIContent(const std::shared_ptr& abilityContext, + std::shared_ptr& uiExtensionContext, bool uiAbilityFlag) { - if (asyncContext == nullptr) { - ACCESSTOKEN_LOG_ERROR(LABEL, "asyncContext nullptr"); - return nullptr; - } OHOS::Ace::UIContent* uiContent = nullptr; - if (asyncContext->uiAbilityFlag) { - uiContent = asyncContext->abilityContext->GetUIContent(); + if (uiAbilityFlag) { + if (abilityContext == nullptr) { + return nullptr; + } + uiContent = abilityContext->GetUIContent(); } else { - uiContent = asyncContext->uiExtensionContext->GetUIContent(); + if (uiExtensionContext == nullptr) { + return nullptr; + } + uiContent = uiExtensionContext->GetUIContent(); } return uiContent; } @@ -126,7 +135,8 @@ static void CreateUIExtensionMainThread(std::shared_ptr& as const std::shared_ptr& uiExtCallback) { auto task = [asyncContext, want, uiExtensionCallbacks, uiExtCallback]() { - OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); asyncContext->result = AccessToken::RET_FAILED; @@ -226,7 +236,8 @@ static bool CreateUIExtension(std::shared_ptr& asyncContext static void GetInstanceId(std::shared_ptr& asyncContext) { auto task = [asyncContext]() { - OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); return; @@ -585,7 +596,8 @@ static ani_object RequestPermissionsFromUserExecute([[maybe_unused]] ani_env* en static void CloseModalUIExtensionMainThread(std::shared_ptr& asyncContext, int32_t sessionId) { auto task = [asyncContext, sessionId]() { - Ace::UIContent* uiContent = GetUIContent(asyncContext); + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); asyncContext->result = RET_FAILED; @@ -754,7 +766,8 @@ void AuthorizationResult::WindowShownCallback() if (asyncContext == nullptr) { return; } - OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if ((uiContent == nullptr) || !(asyncContext->uiContentFlag)) { return; } @@ -882,6 +895,353 @@ static ani_int CheckAccessTokenSync([[maybe_unused]] ani_env* env, [[maybe_unuse return static_cast(asyncContext->result); } +static ani_status GetContext( + ani_env* env, const ani_object& aniContext, std::shared_ptr& asyncContext) +{ + auto context = OHOS::AbilityRuntime::GetStageModeContext(env, aniContext); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetStageModeContext failed"); + return ANI_ERROR; + } + asyncContext->abilityContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->abilityContext != nullptr) { + auto abilityInfo = asyncContext->abilityContext->GetApplicationInfo(); + if (abilityInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->uiAbilityFlag = true; + asyncContext->tokenId = abilityInfo->accessTokenId; + } else { + asyncContext->uiExtensionContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->uiExtensionContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertTo UIExtensionContext failed"); + return ANI_ERROR; + } + auto uiExtensionInfo = asyncContext->uiExtensionContext->GetApplicationInfo(); + if (uiExtensionInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->tokenId = uiExtensionInfo->accessTokenId; + } + return ANI_OK; +} + +static bool ParseRequestPermissionOnSetting(ani_env* env, ani_object& aniContext, ani_array_ref& permissionList, + std::shared_ptr& asyncContext) +{ + if (GetContext(env, aniContext, asyncContext) != ANI_OK) { + BusinessErrorAni::ThrowParameterTypeError(env, STSErrorCode::STS_ERROR_PARAM_ILLEGAL, + GetParamErrorMsg("context", "UIAbility or UIExtension Context")); + return false; + } + if (!ProcessArrayString(env, nullptr, permissionList, asyncContext->permissionList)) { + BusinessErrorAni::ThrowParameterTypeError(env, STSErrorCode::STS_ERROR_PARAM_ILLEGAL, + GetParamErrorMsg("permissionList", "Array")); + return false; + } + return true; +} + +static void ThrowError(ani_env* env, int32_t errCode) +{ + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(errCode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); +} + +static ani_array_ref ReturnResult(ani_env* env, std::shared_ptr& asyncContext) +{ + if (asyncContext->result != RET_SUCCESS) { + ThrowError(env, asyncContext->result); + return ani_array_ref{}; + } + + const char* classDescriptor = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/GrantStatus;"; + ani_class aClass; + if (env->FindClass(classDescriptor, &aClass) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass name %{public}s failed!", classDescriptor); + return ani_array_ref{}; + } + + ani_ref initialElement; + if (env->GetUndefined(&initialElement) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetUndefined failed!"); + return ani_array_ref{}; + } + ani_array_ref result; + ani_size length = static_cast(asyncContext->stateList.size()); + if (env->Array_New_Ref(aClass, length, initialElement, &result) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_New_Ref failed!"); + return ani_array_ref{}; + } + + const char* enumDescriptor = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/GrantStatus;"; + ani_enum enumType; + if (env->FindEnum(enumDescriptor, &enumType) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindEnum name %{public}s failed!", enumDescriptor); + return ani_array_ref{}; + } + for (const auto& state : asyncContext->stateList) { + ani_size index = 0; + ani_enum_item enumItem; + if (env->Enum_GetEnumItemByIndex(enumType, static_cast(state), &enumItem) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Enum_GetEnumItemByIndex value %{public}u failed!", state); + continue; + } + + if (env->Array_Set_Ref(result, index, enumItem) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_Set_Ref failed!"); + continue; + } + ++index; + } + + return result; +} + +PermissonOnSettingUICallback::PermissonOnSettingUICallback(ani_env* env, + const std::shared_ptr& reqContext) +{ + this->env_ = env; + this->reqContext_ = reqContext; +} + +PermissonOnSettingUICallback::~PermissonOnSettingUICallback() +{} + +void PermissonOnSettingUICallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_ = sessionId; +} + +static void CloseSettingModalUIExtensionMainThread(std::shared_ptr& asyncContext, + int32_t sessionId) +{ + auto task = [asyncContext, sessionId]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + asyncContext->result = RET_FAILED; + return; + } + uiContent->CloseModalUIExtension(sessionId); + ACCESSTOKEN_LOG_INFO(LABEL, "Close end, sessionId: %{public}d", sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CloseModalUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +void PermissonOnSettingUICallback::ReleaseHandler(int32_t code) +{ + { + std::lock_guard lock(this->lockReleaseFlag); + if (this->releaseFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "Callback has executed."); + return; + } + this->releaseFlag = true; + } + CloseSettingModalUIExtensionMainThread(this->reqContext_, this->sessionId_); + if (code == -1) { + this->reqContext_->errorCode = code; + } + + this->reqContext_->loadlock.unlock(); + std::lock_guard lock(g_lockWindowFlag); + g_windowFlag = false; +} + +/* + * when UIExtensionAbility use terminateSelfWithResult + */ +void PermissonOnSettingUICallback::OnResult(int32_t resultCode, const AAFwk::Want& result) +{ + this->reqContext_->errorCode = result.GetIntParam(RESULT_ERROR_KEY, 0); + this->reqContext_->stateList = result.GetIntArrayParam(PERMISSION_RESULT_KEY); + ACCESSTOKEN_LOG_INFO(LABEL, "ResultCode is %{public}d, errorCode=%{public}d, listSize=%{public}zu.", + resultCode, this->reqContext_->errorCode, this->reqContext_->stateList.size()); + ReleaseHandler(0); +} + +/* + * when UIExtensionAbility send message to UIExtensionComponent + */ +void PermissonOnSettingUICallback::OnReceive(const AAFwk::WantParams& receive) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Called!"); +} + +/* + * when UIExtensionAbility disconnect or use terminate or process die + * releaseCode is 0 when process normal exit + */ +void PermissonOnSettingUICallback::OnRelease(int32_t releaseCode) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseCode is %{public}d", releaseCode); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error + */ +void PermissonOnSettingUICallback::OnError(int32_t code, const std::string& name, const std::string& message) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Code is %{public}d, name is %{public}s, message is %{public}s", + code, name.c_str(), message.c_str()); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init, + * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy + */ +void PermissonOnSettingUICallback::OnRemoteReady(const std::shared_ptr& uiProxy) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Connect to UIExtensionAbility successfully."); +} + +/* + * when UIExtensionComponent destructed + */ +void PermissonOnSettingUICallback::OnDestroy() +{ + ACCESSTOKEN_LOG_INFO(LABEL, "UIExtensionAbility destructed."); + ReleaseHandler(-1); +} + +static void CreateSettingUIExtensionMainThread(std::shared_ptr& asyncContext, + const AAFwk::Want& want, const Ace::ModalUIExtensionCallbacks& uiExtensionCallbacks, + const std::shared_ptr& uiExtCallback) +{ + auto task = [asyncContext, want, uiExtensionCallbacks, uiExtCallback]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to get ui content!"); + asyncContext->result = RET_FAILED; + return; + } + + Ace::ModalUIExtensionConfig config; + config.isProhibitBack = true; + int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config); + ACCESSTOKEN_LOG_INFO(LABEL, "Create end, sessionId: %{public}d, tokenId: %{public}d.", + sessionId, asyncContext->tokenId); + if (sessionId == 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to create component, sessionId is 0."); + asyncContext->result = RET_FAILED; + return; + } + uiExtCallback->SetSessionId(sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CreateUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +static void StartUIExtension(ani_env* env, std::shared_ptr& asyncContext) +{ + AccessTokenKit::GetPermissionManagerInfo(asyncContext->info); + + OHOS::AAFwk::Want want; + want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.permStateAbilityName); + want.SetParam(PERMISSION_KEY, asyncContext->permissionList); + want.SetParam(EXTENSION_TYPE_KEY, UI_EXTENSION_TYPE); + + auto uiExtCallback = std::make_shared(env, asyncContext); + Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = { + [uiExtCallback](int32_t releaseCode) { + uiExtCallback->OnRelease(releaseCode); + }, + [uiExtCallback](int32_t resultCode, const AAFwk::Want& result) { + uiExtCallback->OnResult(resultCode, result); + }, + [uiExtCallback](const AAFwk::WantParams& receive) { + uiExtCallback->OnReceive(receive); + }, + [uiExtCallback](int32_t code, const std::string& name, [[maybe_unused]] const std::string& message) { + uiExtCallback->OnError(code, name, name); + }, + [uiExtCallback](const std::shared_ptr& uiProxy) { + uiExtCallback->OnRemoteReady(uiProxy); + }, + [uiExtCallback]() { + uiExtCallback->OnDestroy(); + }, + }; + + { + std::lock_guard lock(g_lockWindowFlag); + if (g_windowFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "The request already exists."); + asyncContext->result = RET_FAILED; + asyncContext->errorCode = REQUEST_REALDY_EXIST; + asyncContext->loadlock.unlock(); + return; + } + g_windowFlag = true; + } + CreateSettingUIExtensionMainThread(asyncContext, want, uiExtensionCallbacks, uiExtCallback); + if (asyncContext->result == RET_FAILED) { + asyncContext->loadlock.unlock(); + std::lock_guard lock(g_lockWindowFlag); + g_windowFlag = false; + } +} + +static ani_array_ref RequestPermissionOnSettingExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_object aniContext, ani_array_ref permissionList) +{ + if (env == nullptr || permissionList == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "permissionList or env null"); + return ani_array_ref{}; + } + + std::shared_ptr asyncContext = + std::make_shared(); + if (asyncContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "AsyncContext is null."); + ThrowError(env, AccessTokenError::ERR_MALLOC_FAILED); + return ani_array_ref{}; + } + + if (!ParseRequestPermissionOnSetting(env, aniContext, permissionList, asyncContext)) { + return ani_array_ref{}; + } + + static AccessTokenID selfTokenID = static_cast(GetSelfTokenID()); + if (selfTokenID != asyncContext->tokenId) { + ACCESSTOKEN_LOG_ERROR(LABEL, "The context tokenID %{public}d is not same with selfTokenID %{public}d.", + asyncContext->tokenId, selfTokenID); + ThrowError(env, RET_FAILED); + return ani_array_ref{}; + } + + asyncContext->loadlock.lock(); // wait for user operate pop window + StartUIExtension(env, asyncContext); + asyncContext->loadlock.lock(); + return ReturnResult(env, asyncContext); +} + extern "C" { ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) { @@ -923,6 +1283,9 @@ ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) ani_native_function { "requestPermissionsFromUserExecute", "Lapplication/Context/Context;Lescompat/Array;:Lsecurity/PermissionRequestResult/PermissionRequestResult;", reinterpret_cast(RequestPermissionsFromUserExecute) }, + ani_native_function { "requestPermissionOnSettingExecute", + "Lapplication/Context/Context;Lescompat/Array;:Lescompat/Array;", + reinterpret_cast(RequestPermissionOnSettingExecute) }, }; if (ANI_OK != env->Class_BindNativeMethods(cls, claMethods.data(), claMethods.size())) { ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot bind native methods to %{public}s", className);