diff --git a/bundle.json b/bundle.json index 9a569db586553ce268493299cd781be24b951201..963dbf16eabf95258f270eb69072ab265d35d99c 100644 --- a/bundle.json +++ b/bundle.json @@ -59,7 +59,8 @@ "selinux_adapter", "sqlite", "window_manager", - "zlib" + "zlib", + "runtime_core" ], "third_party": [] }, @@ -71,7 +72,8 @@ "fwk_group": [ "//base/security/access_token/interfaces/kits/capi:capi_packages", "//base/security/access_token/interfaces/kits/cj:cj_packages", - "//base/security/access_token/frameworks/js/napi:napi_packages" + "//base/security/access_token/frameworks/js/napi:napi_packages", + "//base/security/access_token/frameworks/ets/ani:arkts_package" ], "service_group": [ "//base/security/access_token/interfaces/innerkits/tokensync:libtokensync_sdk", diff --git a/frameworks/ets/ani/BUILD.gn b/frameworks/ets/ani/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..213648161dc7989669c4c318d24bfc577b7cf158 --- /dev/null +++ b/frameworks/ets/ani/BUILD.gn @@ -0,0 +1,25 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//base/security/access_token/access_token.gni") + +group("arkts_package") { + deps = [] + if (support_jsapi) { + deps += [ + "accesstoken:accesstoken_ets", + "common:common_ets", + "privacy:privacy_ets", + ] + } +} diff --git a/frameworks/ets/ani/accesstoken/BUILD.gn b/frameworks/ets/ani/accesstoken/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6fe0a656122a59623b4de891af30a7b0119da6af --- /dev/null +++ b/frameworks/ets/ani/accesstoken/BUILD.gn @@ -0,0 +1,92 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//base/security/access_token/access_token.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//build/ohos/ace/ace.gni") + +ohos_shared_library("accesstoken_ani") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + cflags_cc = [ "-DHILOG_ENABLE" ] + include_dirs = [ + "${access_token_path}/frameworks/common/include", + "${access_token_path}/interfaces/innerkits/accesstoken/include", + "./include", + "${access_token_path}/interfaces/innerkits/token_setproc/include", + "${access_token_path}/interfaces/innerkits/token_callback/include", + "${access_token_path}/frameworks/ets/ani/common/include", + ] + sources = [ "src/ability_access_ctrl.cpp" ] + + deps = [ + "${access_token_path}/frameworks/ets/ani/common:libani_common", + "${access_token_path}/interfaces/innerkits/accesstoken:libaccesstoken_sdk", + "${access_token_path}/interfaces/innerkits/token_callback:libtoken_callback_sdk", + "${access_token_path}/interfaces/innerkits/token_setproc:libtokensetproc_shared", + ] + + external_deps = [ + "ability_base:want", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:abilitykit_native", + "ability_runtime:ani_base_context", + "ability_runtime:appkit_native", + "ability_runtime:ui_extension", + "ace_engine:ace_uicontent", + "c_utils:utils", + "hilog:libhilog", + "hisysevent:libhisysevent", + "init:libbegetutil", + "ipc:ipc_single", + "napi:ace_napi", + "runtime_core:ani", + "runtime_core:libarkruntime", + ] + + if (eventhandler_enable == true) { + cflags_cc += [ "-DEVENTHANDLER_ENABLE" ] + external_deps += [ "eventhandler:libeventhandler" ] + } + + part_name = "access_token" + subsystem_name = "security" +} + +generate_static_abc("ability_access_ctrl") { + base_url = "./ets" + files = [ "./ets/@ohos.abilityAccessCtrl.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/ability_access_ctrl.abc" +} + +ohos_prebuilt_etc("ability_access_ctrl_etc") { + source = "$target_out_dir/ability_access_ctrl.abc" + deps = [ ":ability_access_ctrl" ] + module_install_dir = "framework" + part_name = "access_token" + subsystem_name = "security" +} + +group("accesstoken_ets") { + deps = [ + ":ability_access_ctrl_etc", + ":accesstoken_ani", + ] +} diff --git a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets new file mode 100644 index 0000000000000000000000000000000000000000..181c6128360107d925dbc696b24078fd5fb36870 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AsyncCallback, Callback, BusinessError } from '@ohos.base'; +import { Permissions } from 'permissions'; +import type _PermissionRequestResult from 'security.PermissionRequestResult'; +import Context from 'application.Context'; + +function PARAM_ERROR_MSG(param: string, type: string): string { + return `Parameter Error. The type of "${param}" must be ${type}.`; +} + +export const LOG_TAG = 0xC05A01; + +export { Permissions }; + +export type PermissionRequestResult = _PermissionRequestResult; + +export default namespace abilityAccessCtrl { + loadLibrary("accesstoken_ani.z"); + + export enum GrantStatus { + PERMISSION_DENIED = -1, + PERMISSION_GRANTED = 0 + } + + export enum PermissionRequestToggleStatus { + CLOSED = 0, + OPEN = 1, + } + + export enum PermissionStatus { + DENIED = -1, + GRANTED = 0, + NOT_DETERMINED = 1, + INVALID = 2, + RESTRICTED = 3 + } + + interface PermissionStateChangeInfo { + change: PermissionStateChangeType; + tokenID: int; + permissionName: Permissions; + } + + export enum PermissionStateChangeType { + PERMISSION_REVOKED_OPER = 0, + PERMISSION_GRANTED_OPER = 1 + } + + export enum SwitchType { + CAMERA = 0, + MICROPHONE = 1, + LOCATION = 2, + } + + enum STSErrorCode { + STS_OK = 0, + STS_ERROR_PERMISSION_DENIED = 201, + STS_ERROR_NOT_SYSTEM_APP = 202, + STS_ERROR_PARAM_ILLEGAL = 401, + STS_ERROR_SYSTEM_CAPABILITY_NOT_SUPPORT = 801, + + } ; + function validateRequestParams(context: Context, permissionList: Array): void { + if (typeof context === "undefined" || context == null) { + let err = new BusinessError(); + err.code = STSErrorCode.STS_ERROR_PARAM_ILLEGAL; + err.data = PARAM_ERROR_MSG("context", "UIAbility or UIExtension Context"); + throw err; + } + if (typeof permissionList !== "object" || !Array.isArray(permissionList) || + permissionList.length == 0 || typeof permissionList[0] !== "string") { + let err = new BusinessError(); + err.code = STSErrorCode.STS_ERROR_PARAM_ILLEGAL; + err.data = PARAM_ERROR_MSG("permissionList", "Array"); + throw err; + } + } + native function createAtManager(): AtManager; + + interface AtManager { + checkAccessTokenANI(tokenID: int, permissionName: Permissions): int; + checkAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus; + checkAccessToken(tokenID: int, permissionName: Permissions): Promise + + verifyAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus; + + requestPermissionsFromUserExecute( + context: Context, + permissionList: Array): PermissionRequestResult; + + requestPermissionsFromUser( + context: Context, + permissionList: Array, + requestCallback: AsyncCallback + ): void; + + requestPermissionsFromUser( + context: Context, + permissionList: Array): Promise; + } + + class AtManagerInner implements AtManager { + native checkAccessTokenANI(tokenID: int, permissionName: Permissions): int; + + verifyAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus { + let result = this.checkAccessTokenANI(tokenID, permissionName); + if (result == 0) { + return GrantStatus.PERMISSION_GRANTED; + } + return GrantStatus.PERMISSION_DENIED; + } + + checkAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus { + let result = this.checkAccessTokenANI(tokenID, permissionName); + if (result == 0) { + return GrantStatus.PERMISSION_GRANTED; + } + return GrantStatus.PERMISSION_DENIED; + } + + checkAccessToken(tokenID: int, permissionName: Permissions): Promise { + let p = new Promise((resolve: (v: GrantStatus) + => void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): GrantStatus => { return new AtManagerInner().checkAccessTokenANI(tokenID, + permissionName) as GrantStatus;}); + p1.then((e: NullishType) => { + resolve(e as GrantStatus); + }).catch((err: BusinessError): void => { + reject(err); + }); + }); + return p; + } + + native requestPermissionsFromUserExecute(context: Context, + permissionList: Array): PermissionRequestResult; + + requestPermissionsFromUser(context: Context, permissionList: Array, + callback: AsyncCallback): void { + validateRequestParams(context, permissionList); + let p2 = taskpool.execute((): PermissionRequestResult => { + return new AtManagerInner().requestPermissionsFromUserExecute(context, permissionList)}); + p2.then((e: NullishType) => { + let retError = new BusinessError(); + callback(retError, e as PermissionRequestResult); + }, (err: BusinessError): void=> { + if (err.code == STSErrorCode.STS_ERROR_PARAM_ILLEGAL) { + throw err; + } + callback(err, PermissionRequestResult); + }) + } + + requestPermissionsFromUser(context: Context, permissionList: Array): + Promise { + validateRequestParams(context, permissionList); + let p = new Promise(( + resolve: (v: PermissionRequestResult) => void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): PermissionRequestResult => { + return new AtManagerInner().requestPermissionsFromUserExecute(context, permissionList)}); + p1.then((e: NullishType)=> { + let r : PermissionRequestResult = e as PermissionRequestResult; + resolve(r); + }).catch((err: BusinessError): void => { + if (err.code == STSErrorCode.STS_ERROR_PARAM_ILLEGAL) { + throw err; + } + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f7844ba9ff1335a041403f587b6048f1912150f6 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ABILITY_ACCESS_CTRL_H +#define ABILITY_ACCESS_CTRL_H + +#include +#include +#include +#include + +#include "ability.h" +#include "ability_manager_client.h" +#include "access_token.h" +#include "ani.h" +#include "permission_grant_info.h" +#include "token_callback_stub.h" +#include "ui_content.h" +#include "ui_extension_context.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +std::condition_variable g_loadedCond; +std::mutex g_lockCache; +static std::atomic g_cnt = 0; +constexpr uint32_t REPORT_CNT = 10; +constexpr uint32_t VERIFY_TOKENID_INCONSISTENCY = 0; +const int32_t PARAM_DEFAULT_VALUE = -1; + +static constexpr const char* PERMISSION_STATUS_CHANGE_KEY = "accesstoken.permission.change"; + +struct AtManagerAsyncContext { + AccessTokenID tokenId = 0; + std::string permissionName; + union { + uint32_t flag = 0; + uint32_t status; + }; + int32_t result = RET_FAILED; + int32_t errorCode = 0; +}; + +class AniContextCommon { +public: + static constexpr int32_t MAX_PARAMS_ONE = 1; + static constexpr int32_t MAX_PARAMS_TWO = 2; + static constexpr int32_t MAX_PARAMS_THREE = 3; + static constexpr int32_t MAX_PARAMS_FOUR = 4; + static constexpr int32_t MAX_LENGTH = 256; + static constexpr int32_t MAX_WAIT_TIME = 1000; + static constexpr int32_t VALUE_MAX_LEN = 32; +}; + +struct PermissionParamCache { + long long sysCommitIdCache = PARAM_DEFAULT_VALUE; + int32_t commitIdCache = PARAM_DEFAULT_VALUE; + int32_t handle = PARAM_DEFAULT_VALUE; + std::string sysParamCache; +}; + +struct PermissionStatusCache { + int32_t status; + std::string paramValue; +}; + +static PermissionParamCache g_paramCache; +std::map g_cache; + +struct RequestAsyncContext { + AccessTokenID tokenId = 0; + std::string bundleName; + bool needDynamicRequest = true; + int32_t result = RET_SUCCESS; + int32_t instanceId = -1; + std::vector permissionList; + std::vector grantResults; + std::vector permissionsState; + ani_object requestResult = nullptr; + std::vector dialogShownResults; + std::vector permissionQueryResults; + std::vector errorReasons; + Security::AccessToken::PermissionGrantInfo info; + std::shared_ptr abilityContext; + std::shared_ptr uiExtensionContext; + bool uiAbilityFlag = false; + bool uiExtensionFlag = false; + bool uiContentFlag = false; + bool releaseFlag = false; + std::mutex loadlock; +#ifdef EVENTHANDLER_ENABLE + std::shared_ptr handler_ = nullptr; +#endif +}; + +class UIExtensionCallback { +public: + explicit UIExtensionCallback(const std::shared_ptr& reqContext); + ~UIExtensionCallback(); + void SetSessionId(int32_t sessionId); + 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(); + void ReleaseHandler(int32_t code); + +private: + int32_t sessionId_ = 0; + std::shared_ptr reqContext_ = nullptr; +}; + +class AuthorizationResult : public Security::AccessToken::TokenCallbackStub { +public: + AuthorizationResult(std::shared_ptr& data) : data_(data) {} + virtual ~AuthorizationResult() override = default; + + virtual void GrantResultsCallback( + const std::vector& permissionList, const std::vector& grantResults) override; + virtual void WindowShownCallback() override; + +private: + std::shared_ptr data_ = nullptr; +}; + +class RequestAsyncInstanceControl { +public: + static bool AddCallbackByInstanceId(std::shared_ptr& asyncContext); + static void ExecCallback(int32_t id); + static void CheckDynamicRequest(std::shared_ptr& asyncContext, bool& isDynamic); + +private: + static std::map>> instanceIdMap_; + static std::mutex instanceIdMutex_; +}; + +struct ResultCallback { + std::vector permissions; + std::vector grantResults; + std::vector dialogShownResults; + std::shared_ptr data = nullptr; +}; + +std::map>> RequestAsyncInstanceControl::instanceIdMap_; +std::mutex RequestAsyncInstanceControl::instanceIdMutex_; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS + +#endif // ABILITY_ACCESS_CTRL_H \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b8ca465f0e20784c07d039c1831941ee25eb858 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp @@ -0,0 +1,935 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ability_access_ctrl.h" + +#include +#include + +#include "access_token.h" +#include "accesstoken_kit.h" +#include "accesstoken_log.h" +#include "ani_base_context.h" +#include "ani_error.h" +#include "hisysevent.h" +#include "parameter.h" +#include "permission_list_state.h" +#include "token_setproc.h" +#include "want.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +std::mutex g_lockFlag; +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniAbilityAccessCtrl" }; +constexpr int32_t MAX_LENGTH = 256; + +const std::string PERMISSION_KEY = "ohos.user.grant.permission"; +const std::string STATE_KEY = "ohos.user.grant.permission.state"; +const std::string RESULT_KEY = "ohos.user.grant.permission.result"; +const std::string EXTENSION_TYPE_KEY = "ability.want.params.uiExtensionType"; +const std::string UI_EXTENSION_TYPE = "sys/commonUI"; +const std::string ORI_PERMISSION_MANAGER_BUNDLE_NAME = "com.ohos.permissionmanager"; +const std::string TOKEN_KEY = "ohos.ability.params.token"; +const std::string CALLBACK_KEY = "ohos.ability.params.callback"; +const std::string WINDOW_RECTANGLE_LEFT_KEY = "ohos.ability.params.request.left"; +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"; + +#define SETTER_METHOD_NAME(property) "" #property + +static void UpdateGrantPermissionResultOnly( + const std::vector& permissions, const std::vector& grantResults, + std::shared_ptr& data, std::vector& newGrantResults) +{ + size_t size = permissions.size(); + for (size_t i = 0; i < size; i++) { + int32_t result = static_cast(data->permissionsState[i]); + if (data->permissionsState[i] == AccessToken::DYNAMIC_OPER) { + result = data->result == AccessToken::RET_SUCCESS ? grantResults[i] : AccessToken::INVALID_OPER; + } + newGrantResults.emplace_back(result); + } +} + +static bool IsDynamicRequest(std::shared_ptr& asyncContext) +{ + if (asyncContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "asyncContext nullptr"); + return false; + } + std::vector permList; + for (const auto& permission : asyncContext->permissionList) { + AccessToken::PermissionListState permState; + permState.permissionName = permission; + permState.errorReason = SERVICE_ABNORMAL; + permState.state = AccessToken::INVALID_OPER; + permList.emplace_back(permState); + } + auto ret = AccessToken::AccessTokenKit::GetSelfPermissionsState(permList, asyncContext->info); + ACCESSTOKEN_LOG_INFO(LABEL, + "TokenID: %{public}d, bundle: %{public}s, uiExAbility: %{public}s, serExAbility: %{public}s.", + asyncContext->tokenId, asyncContext->info.grantBundleName.c_str(), asyncContext->info.grantAbilityName.c_str(), + asyncContext->info.grantServiceAbilityName.c_str()); + if (ret == AccessToken::FORBIDDEN_OPER) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FORBIDDEN_OPER"); + for (auto& perm : permList) { + perm.state = AccessToken::INVALID_OPER; + perm.errorReason = PRIVACY_STATEMENT_NOT_AGREED; + } + } + for (const auto& permState : permList) { + ACCESSTOKEN_LOG_INFO(LABEL, "Permission: %{public}s: state: %{public}d, errorReason: %{public}d", + permState.permissionName.c_str(), permState.state, permState.errorReason); + asyncContext->permissionsState.emplace_back(permState.state); + asyncContext->dialogShownResults.emplace_back(permState.state == AccessToken::TypePermissionOper::DYNAMIC_OPER); + asyncContext->errorReasons.emplace_back(permState.errorReason); + } + if (permList.size() != asyncContext->permissionList.size()) { + ACCESSTOKEN_LOG_ERROR(LABEL, "permList.size: %{public}zu, permissionList.size: %{public}zu", permList.size(), + asyncContext->permissionList.size()); + return false; + } + return ret == AccessToken::TypePermissionOper::DYNAMIC_OPER; +} + +static OHOS::Ace::UIContent* GetUIContent(const std::shared_ptr& asyncContext) +{ + if (asyncContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "asyncContext nullptr"); + return nullptr; + } + OHOS::Ace::UIContent* uiContent = nullptr; + if (asyncContext->uiAbilityFlag) { + uiContent = asyncContext->abilityContext->GetUIContent(); + } else { + uiContent = asyncContext->uiExtensionContext->GetUIContent(); + } + return uiContent; +} + +static void CreateUIExtensionMainThread(std::shared_ptr& asyncContext, + const OHOS::AAFwk::Want& want, const OHOS::Ace::ModalUIExtensionCallbacks& uiExtensionCallbacks, + const std::shared_ptr& uiExtCallback) +{ + auto task = [asyncContext, want, uiExtensionCallbacks, uiExtCallback]() { + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + asyncContext->result = AccessToken::RET_FAILED; + asyncContext->uiExtensionFlag = false; + asyncContext->loadlock.unlock(); + return; + } + + OHOS::Ace::ModalUIExtensionConfig config; + config.isProhibitBack = true; + int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config); + if (sessionId == 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Create component failed, sessionId is 0"); + asyncContext->result = AccessToken::RET_FAILED; + asyncContext->uiExtensionFlag = false; + asyncContext->loadlock.unlock(); + return; + } + uiExtCallback->SetSessionId(sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CreateUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +static bool CreateServiceExtension(std::shared_ptr& asyncContext) +{ + if (!asyncContext->uiAbilityFlag) { + ACCESSTOKEN_LOG_ERROR(LABEL, "UIExtension ability can not pop service ablility window!"); + asyncContext->needDynamicRequest = false; + asyncContext->result = RET_FAILED; + return false; + } + OHOS::sptr remoteObject = new (std::nothrow) AuthorizationResult(asyncContext); + if (remoteObject == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Create window failed!"); + asyncContext->needDynamicRequest = false; + asyncContext->result = RET_FAILED; + return false; + } + OHOS::AAFwk::Want want; + want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.grantServiceAbilityName); + want.SetParam(PERMISSION_KEY, asyncContext->permissionList); + want.SetParam(STATE_KEY, asyncContext->permissionsState); + want.SetParam(TOKEN_KEY, asyncContext->abilityContext->GetToken()); + want.SetParam(CALLBACK_KEY, remoteObject); + + int32_t left = 0; + int32_t top = 0; + int32_t width = 0; + int32_t height = 0; + asyncContext->abilityContext->GetWindowRect(left, top, width, height); + want.SetParam(WINDOW_RECTANGLE_LEFT_KEY, left); + want.SetParam(WINDOW_RECTANGLE_TOP_KEY, top); + want.SetParam(WINDOW_RECTANGLE_WIDTH_KEY, width); + want.SetParam(WINDOW_RECTANGLE_HEIGHT_KEY, height); + want.SetParam(REQUEST_TOKEN_KEY, asyncContext->abilityContext->GetToken()); + int32_t ret = OHOS::AAFwk::AbilityManagerClient::GetInstance()->RequestDialogService( + want, asyncContext->abilityContext->GetToken()); + ACCESSTOKEN_LOG_INFO(LABEL, "Request end, ret: %{public}d, tokenId: %{public}d, permNum: %{public}zu", ret, + asyncContext->tokenId, asyncContext->permissionList.size()); + return true; +} + +static bool CreateUIExtension(std::shared_ptr& asyncContext) +{ + OHOS::AAFwk::Want want; + want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.grantAbilityName); + want.SetParam(PERMISSION_KEY, asyncContext->permissionList); + want.SetParam(STATE_KEY, asyncContext->permissionsState); + want.SetParam(EXTENSION_TYPE_KEY, UI_EXTENSION_TYPE); + + auto uiExtCallback = std::make_shared(asyncContext); + OHOS::Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = { + [uiExtCallback](int32_t releaseCode) { uiExtCallback->OnRelease(releaseCode); }, + [uiExtCallback]( + int32_t resultCode, const OHOS::AAFwk::Want& result) { uiExtCallback->OnResult(resultCode, result); }, + [uiExtCallback](const OHOS::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(); }, + }; + CreateUIExtensionMainThread(asyncContext, want, uiExtensionCallbacks, uiExtCallback); + return true; +} + +static void GetInstanceId(std::shared_ptr& asyncContext) +{ + auto task = [asyncContext]() { + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + return; + } + asyncContext->uiContentFlag = true; + asyncContext->instanceId = uiContent->GetInstanceId(); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:GetInstanceId"); + } else { + task(); + } +#else + task(); +#endif +} + +static ani_ref ConvertAniArrayString(ani_env* env, const std::vector& cArray) +{ + ani_size length = cArray.size(); + ani_array_ref aArrayRef = nullptr; + ani_class aStringcls = nullptr; + if (env->FindClass("Lstd/core/String;", &aStringcls) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayString FindClass String failed"); + return nullptr; + } + if (env->Array_New_Ref(aStringcls, length, nullptr, &aArrayRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayString Array_New_Ref failed "); + return nullptr; + } + ani_string aString = nullptr; + for (ani_size i = 0; i < length; ++i) { + env->String_NewUTF8(cArray[i].c_str(), cArray[i].size(), &aString); + env->Array_Set_Ref(aArrayRef, i, aString); + } + ani_ref aRef = nullptr; + if (env->GlobalReference_Create(aArrayRef, &aRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayString GlobalReference_Create failed "); + return nullptr; + } + return aRef; +} + +static ani_ref ConvertAniArrayInt(ani_env* env, const std::vector& cArray) +{ + ani_size length = cArray.size(); + ani_array_int aArrayInt = nullptr; + if (env->Array_New_Int(length, &aArrayInt) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayInt Array_New_Ref failed "); + return nullptr; + } + for (ani_size i = 0; i < length; ++i) { + env->Array_SetRegion_Int(aArrayInt, i, length, &cArray[i]); + } + ani_ref aRef = nullptr; + if (env->GlobalReference_Create(aArrayInt, &aRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayInt GlobalReference_Create failed "); + return nullptr; + } + return aRef; +} + +static ani_ref ConvertAniArrayBool(ani_env* env, const std::vector& cArray) +{ + ani_size length = cArray.size(); + ani_array_boolean aArrayBool = nullptr; + if (env->Array_New_Boolean(length, &aArrayBool) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayBool Array_New_Boolean failed "); + return nullptr; + } + std::vector boolArray(length); + for (ani_size i = 0; i < length; ++i) { + boolArray[i] = cArray[i]; + } + for (ani_size i = 0; i < length; ++i) { + env->Array_SetRegion_Boolean(aArrayBool, i, length, &boolArray[i]); + } + ani_ref aRef = nullptr; + if (env->GlobalReference_Create(aArrayBool, &aRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertAniArrayBool GlobalReference_Create failed "); + return nullptr; + } + return aRef; +} + +template +static inline bool CallSetter(ani_env* env, ani_class cls, ani_object object, const char* setterName, valueType value) +{ + ani_status status = ANI_ERROR; + ani_field fieldValue; + if (env->Class_FindField(cls, setterName, &fieldValue) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindField Fail %{public}d ", status); + return false; + } + if ((status = env->Object_SetField_Ref(object, fieldValue, value)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Ref Fail %{public}d ", status); + return false; + } + return true; +} + +std::string ANIUtils_ANIStringToStdString(ani_env* env, ani_string aniStr) +{ + ani_size strSize; + if (env->String_GetUTF8Size(aniStr, &strSize) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_GetUTF8Size error"); + return ""; + } + std::vector buffer(strSize + 1); + char* utf8Buffer = buffer.data(); + ani_size bytesWritten = 0; + if (env->String_GetUTF8(aniStr, utf8Buffer, strSize + 1, &bytesWritten) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_GetUTF8 error"); + return ""; + } + utf8Buffer[bytesWritten] = '\0'; + std::string content = std::string(utf8Buffer); + return content; +} + +static bool ProcessArrayString([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_array_ref arrayObj, std::vector& permissionList) +{ + ani_size length; + if (ANI_OK != env->Array_GetLength(arrayObj, &length)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_GetLength FAILED"); + return false; + } + for (ani_size i = 0; i < length; i++) { + ani_ref stringEntryRef; + if (ANI_OK != env->Object_CallMethodByName_Ref( + arrayObj, "$_get", "I:Lstd/core/Object;", &stringEntryRef, static_cast(i))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_CallMethodByName_Ref _get Failed"); + return false; + } + auto strEntryRef = ANIUtils_ANIStringToStdString(env, static_cast(stringEntryRef)); + if (strEntryRef.empty()) { + return false; + } else { + permissionList.emplace_back(strEntryRef); + } + } + return true; +} + +static ani_object WrapResult(ani_env* env, std::shared_ptr& asyncContext) +{ + ani_status status = ANI_ERROR; + ani_class cls = nullptr; + if ((status = env->FindClass("Lsecurity/PermissionRequestResult/PermissionRequestResult;", &cls)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass status %{public}d ", static_cast(status)); + return nullptr; + } + if (cls == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "null cls"); + return nullptr; + } + ani_method method = nullptr; + if ((status = env->Class_FindMethod(cls, "", ":V", &method)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindMethod status %{public}d ", static_cast(status)); + return nullptr; + } + ani_object aObject = nullptr; + if ((status = env->Object_New(cls, method, &aObject)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New status %{public}d ", static_cast(status)); + return nullptr; + } + auto state = asyncContext->needDynamicRequest ? asyncContext->grantResults : asyncContext->permissionsState; + ani_ref strPermissions = ConvertAniArrayString(env, asyncContext->permissionList); + ani_ref intAuthResults = ConvertAniArrayInt(env, state); + ani_ref boolDialogShownResults = ConvertAniArrayBool(env, asyncContext->dialogShownResults); + ani_ref intPermissionQueryResults = ConvertAniArrayInt(env, asyncContext->permissionQueryResults); + if (strPermissions == nullptr || intAuthResults == nullptr || boolDialogShownResults == nullptr || + intPermissionQueryResults == nullptr) { + asyncContext->result = RET_FAILED; + return nullptr; + } + if (!CallSetter(env, cls, aObject, SETTER_METHOD_NAME(permissions), strPermissions) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(authResults), intAuthResults) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(dialogShownResults), boolDialogShownResults) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(errorReasons), intPermissionQueryResults)) { + asyncContext->result = RET_FAILED; + return nullptr; + } + return aObject; +} + +static ani_object DealWithResult(ani_env* env, std::shared_ptr& asyncContext) +{ + ani_object resultObj = nullptr; + if (asyncContext->result == RET_SUCCESS) { + resultObj = WrapResult(env, asyncContext); + } + if (asyncContext->result != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(asyncContext->result); + BusinessErrorAni::ThrowParameterTypeError(env, stsCode, "WrapResult", GetErrorMessage(stsCode)); + return nullptr; + } + return resultObj; +} + +static void RequestResultsHandler(const std::vector& permissionList, + const std::vector& permissionStates, std::shared_ptr& data) +{ + int32_t result; + if (data->result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Result is: %{public}d", data->result); + result = RET_FAILED; + } + std::vector newGrantResults; + UpdateGrantPermissionResultOnly(permissionList, permissionStates, data, newGrantResults); + if (newGrantResults.empty()) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GrantResults empty"); + result = RET_FAILED; + } + data->grantResults.assign(newGrantResults.begin(), newGrantResults.end()); + data->loadlock.unlock(); + g_loadedCond.notify_all(); +} + +static ani_status ConvertContext( + 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; + asyncContext->bundleName = abilityInfo->bundleName; + } 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; + asyncContext->bundleName = uiExtensionInfo->bundleName; + } + return ANI_OK; +} + +static bool ParseRequestPermissionFromUser(ani_env* env, ani_object aniContext, ani_array_ref permissionList, + std::shared_ptr& asyncContext) +{ + if (ConvertContext(env, aniContext, asyncContext) != ANI_OK) { + BusinessErrorAni::ThrowParameterTypeError(env, STSErrorCode::STS_ERROR_PARAM_ILLEGAL, + "RequestPermissionsFromUserExecute", GetParamErrorMsg("context", "UIAbility or UIExtension Context")); + return false; + } + if (!ProcessArrayString(env, nullptr, permissionList, asyncContext->permissionList)) { + return false; + } + return true; +} + +static bool RequestPermissionsFromUserProcess([[maybe_unused]] ani_env* env, + std::shared_ptr& asyncContext) +{ + if (!IsDynamicRequest(asyncContext)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "It does not need to request permission"); + asyncContext->needDynamicRequest = false; + if ((asyncContext->permissionsState.empty()) && (asyncContext->result == STSErrorCode::STS_OK)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GrantResults empty"); + asyncContext->result = RET_FAILED; + } + return false; + } + GetInstanceId(asyncContext); + asyncContext->loadlock.lock(); + bool lockFlag = false; + if (asyncContext->info.grantBundleName == ORI_PERMISSION_MANAGER_BUNDLE_NAME) { + ACCESSTOKEN_LOG_INFO( + LABEL, "Pop service extension dialog, uiContentFlag=%{public}d", asyncContext->uiContentFlag); + if (asyncContext->uiContentFlag) { + lockFlag = RequestAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); + } else { + lockFlag = CreateServiceExtension(asyncContext); + } + } else if (asyncContext->instanceId == -1) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Pop service extension dialog, instanceId is -1."); + lockFlag = CreateServiceExtension(asyncContext); + HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::ACCESS_TOKEN, "REQUEST_PERMISSIONS_FROM_USER", + HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "BUNDLENAME", asyncContext->bundleName.c_str(), + "UIEXTENSION_FLAG", false); + } else { + ACCESSTOKEN_LOG_INFO(LABEL, "Pop ui extension dialog"); + asyncContext->uiExtensionFlag = true; + lockFlag = RequestAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); + HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::ACCESS_TOKEN, "REQUEST_PERMISSIONS_FROM_USER", + HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "BUNDLENAME", asyncContext->bundleName, "UIEXTENSION_FLAG", + false); + if (!asyncContext->uiExtensionFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "Pop uiextension dialog fail, start to pop service extension dialog."); + lockFlag = RequestAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); + } + } + if (!lockFlag) { + asyncContext->loadlock.unlock(); + } + return true; +} + +static ani_object RequestPermissionsFromUserExecute([[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 nullptr; + } + std::shared_ptr asyncContext = std::make_shared(); + if (!ParseRequestPermissionFromUser(env, aniContext, permissionList, asyncContext)) { + return nullptr; + } +#ifdef EVENTHANDLER_ENABLE + asyncContext->handler_ = std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif + bool errTokenID = true; + static AccessTokenID selfTokenID = static_cast(GetSelfTokenID()); + if (selfTokenID != asyncContext->tokenId) { + ACCESSTOKEN_LOG_ERROR( + LABEL, "The context tokenID: %{public}d, selfTokenID: %{public}d.", asyncContext->tokenId, selfTokenID); + asyncContext->result = RET_FAILED; + errTokenID = false; + } + if (!RequestPermissionsFromUserProcess(env, asyncContext) || !errTokenID) { + return DealWithResult(env, asyncContext); + } + ACCESSTOKEN_LOG_INFO(LABEL, "uiExtensionFlag: %{public}d, uiContentFlag: %{public}d, uiAbilityFlag: %{public}d ", + asyncContext->uiExtensionFlag, asyncContext->uiContentFlag, asyncContext->uiAbilityFlag); + asyncContext->loadlock.lock(); + auto resultObj = DealWithResult(env, asyncContext); + asyncContext->loadlock.unlock(); + return resultObj; +} + +static void CloseModalUIExtensionMainThread(std::shared_ptr& asyncContext, int32_t sessionId) +{ + auto task = [asyncContext, sessionId]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext); + 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 + ACCESSTOKEN_LOG_INFO(LABEL, "Instance id: %{public}d", asyncContext->instanceId); +} + +void RequestAsyncInstanceControl::ExecCallback(int32_t id) +{ + std::shared_ptr asyncContext = nullptr; + bool isDynamic = false; + { + std::lock_guard lock(instanceIdMutex_); + auto iter = RequestAsyncInstanceControl::instanceIdMap_.find(id); + if (iter == RequestAsyncInstanceControl::instanceIdMap_.end()) { + ACCESSTOKEN_LOG_ERROR(LABEL, "instanceIdMap_ empty"); + return; + } + while (!iter->second.empty()) { + asyncContext = iter->second[0]; + iter->second.erase(iter->second.begin()); + CheckDynamicRequest(asyncContext, isDynamic); + if (isDynamic) { + break; + } + } + if (iter->second.empty()) { + RequestAsyncInstanceControl::instanceIdMap_.erase(id); + } + } + bool lockFlag = true; + if (isDynamic) { + if (asyncContext->uiExtensionFlag) { + lockFlag = CreateUIExtension(asyncContext); + } else { + lockFlag = CreateServiceExtension(asyncContext); + } + } + if (!lockFlag) { + asyncContext->loadlock.unlock(); + } +} + +void RequestAsyncInstanceControl::CheckDynamicRequest( + std::shared_ptr& asyncContext, bool& isDynamic) +{ + asyncContext->permissionsState.clear(); + asyncContext->dialogShownResults.clear(); + if (!IsDynamicRequest(asyncContext)) { + RequestResultsHandler(asyncContext->permissionList, asyncContext->permissionsState, asyncContext); + return; + } + isDynamic = true; +} + +bool RequestAsyncInstanceControl::AddCallbackByInstanceId(std::shared_ptr& asyncContext) +{ + std::lock_guard lock(instanceIdMutex_); + auto iter = RequestAsyncInstanceControl::instanceIdMap_.find(asyncContext->instanceId); + if (iter != RequestAsyncInstanceControl::instanceIdMap_.end()) { + RequestAsyncInstanceControl::instanceIdMap_[asyncContext->instanceId].emplace_back(asyncContext); + return true; + } + RequestAsyncInstanceControl::instanceIdMap_[asyncContext->instanceId] = {}; + bool lockFlag = true; + if (asyncContext->uiExtensionFlag) { + lockFlag = CreateUIExtension(asyncContext); + } else { + lockFlag = CreateServiceExtension(asyncContext); + } + return lockFlag; +} + +UIExtensionCallback::UIExtensionCallback(const std::shared_ptr& reqContext) +{ + this->reqContext_ = reqContext; +} + +UIExtensionCallback::~UIExtensionCallback() {} + +void UIExtensionCallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_ = sessionId; +} + +void UIExtensionCallback::ReleaseHandler(int32_t code) +{ + { + std::lock_guard lock(g_lockFlag); + if (this->reqContext_->releaseFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "Callback has executed."); + return; + } + this->reqContext_->releaseFlag = true; + } + CloseModalUIExtensionMainThread(this->reqContext_, this->sessionId_); + this->reqContext_->result = code; + RequestAsyncInstanceControl::ExecCallback(this->reqContext_->instanceId); + RequestResultsHandler(this->reqContext_->permissionList, this->reqContext_->permissionsState, this->reqContext_); +} + +void UIExtensionCallback::OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ResultCode is %{public}d", resultCode); + this->reqContext_->permissionList = result.GetStringArrayParam(PERMISSION_KEY); + this->reqContext_->permissionsState = result.GetIntArrayParam(RESULT_KEY); + ReleaseHandler(0); +} + +void UIExtensionCallback::OnReceive(const OHOS::AAFwk::WantParams& receive) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Called!"); +} + +void UIExtensionCallback::OnRelease(int32_t releaseCode) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseCode is %{public}d", releaseCode); + ReleaseHandler(-1); +} + +void UIExtensionCallback::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); +} + +void UIExtensionCallback::OnRemoteReady(const std::shared_ptr& uiProxy) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Connect to UIExtensionAbility successfully."); +} + +void UIExtensionCallback::OnDestroy() +{ + ACCESSTOKEN_LOG_INFO(LABEL, "UIExtensionAbility destructed."); + ReleaseHandler(-1); +} + +void AuthorizationResult::GrantResultsCallback( + const std::vector& permissionList, const std::vector& grantResults) +{ + std::shared_ptr asyncContext = data_; + if (asyncContext == nullptr) { + return; + } + RequestResultsHandler(permissionList, grantResults, asyncContext); +} + +void AuthorizationResult::WindowShownCallback() +{ + std::shared_ptr asyncContext = data_; + if (asyncContext == nullptr) { + return; + } + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + if ((uiContent == nullptr) || !(asyncContext->uiContentFlag)) { + return; + } + RequestAsyncInstanceControl::ExecCallback(asyncContext->instanceId); +} + +static ani_object CreateAtManager([[maybe_unused]] ani_env* env) +{ + ani_object atManagerObj = {}; + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return atManagerObj; + } + + static const char* className = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/AtManagerInner;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Not found %{public}s", className); + return atManagerObj; + } + + ani_method ctor; + if (ANI_OK != env->Class_FindMethod(cls, "", nullptr, &ctor)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "get ctor Failed %{public}s", className); + return atManagerObj; + } + + if (ANI_OK != env->Object_New(cls, ctor, &atManagerObj)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Create Object Failed %{public}s", className); + return atManagerObj; + } + return atManagerObj; +} + +static std::string GetPermParamValue() +{ + long long sysCommitId = GetSystemCommitId(); + if (sysCommitId == g_paramCache.sysCommitIdCache) { + ACCESSTOKEN_LOG_DEBUG(LABEL, "SysCommitId = %{public}lld", sysCommitId); + return g_paramCache.sysParamCache; + } + g_paramCache.sysCommitIdCache = sysCommitId; + if (g_paramCache.handle == PARAM_DEFAULT_VALUE) { + int32_t handle = static_cast(FindParameter(PERMISSION_STATUS_CHANGE_KEY)); + if (handle == PARAM_DEFAULT_VALUE) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindParameter failed"); + return "-1"; + } + g_paramCache.handle = handle; + } + + int32_t currCommitId = static_cast(GetParameterCommitId(g_paramCache.handle)); + if (currCommitId != g_paramCache.commitIdCache) { + char value[AniContextCommon::VALUE_MAX_LEN] = { 0 }; + auto ret = GetParameterValue(g_paramCache.handle, value, AniContextCommon::VALUE_MAX_LEN - 1); + if (ret < 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Return default value, ret=%{public}d", ret); + return "-1"; + } + std::string resStr(value); + g_paramCache.sysParamCache = resStr; + g_paramCache.commitIdCache = currCommitId; + } + return g_paramCache.sysParamCache; +} + +static void UpdatePermissionCache(AtManagerAsyncContext* asyncContext) +{ + std::lock_guard lock(g_lockCache); + auto iter = g_cache.find(asyncContext->permissionName); + if (iter != g_cache.end()) { + std::string currPara = GetPermParamValue(); + if (currPara != iter->second.paramValue) { + asyncContext->result = + AccessToken::AccessTokenKit::VerifyAccessToken(asyncContext->tokenId, asyncContext->permissionName); + iter->second.status = asyncContext->result; + iter->second.paramValue = currPara; + ACCESSTOKEN_LOG_DEBUG(LABEL, "Param changed currPara %{public}s", currPara.c_str()); + } else { + asyncContext->result = iter->second.status; + } + } else { + asyncContext->result = + AccessToken::AccessTokenKit::VerifyAccessToken(asyncContext->tokenId, asyncContext->permissionName); + g_cache[asyncContext->permissionName].status = asyncContext->result; + g_cache[asyncContext->permissionName].paramValue = GetPermParamValue(); + ACCESSTOKEN_LOG_DEBUG( + LABEL, "G_cacheParam set %{public}s", g_cache[asyncContext->permissionName].paramValue.c_str()); + } +} + +static ani_int CheckAccessTokenSync([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_int tokenID, ani_string permissionName) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return AccessToken::PermissionState::PERMISSION_DENIED; + } + if (tokenID == 0) { + BusinessErrorAni::ThrowParameterTypeError( + env, STSErrorCode::STS_ERROR_PARAM_INVALID, "CheckAccessTokenSync", GetParamErrorMsg("tokenID", "int32_t")); + return AccessToken::PermissionState::PERMISSION_DENIED; + } + std::string stdPermissionName = ANIUtils_ANIStringToStdString(env, static_cast(permissionName)); + if (stdPermissionName.empty() || stdPermissionName.length() > MAX_LENGTH) { + BusinessErrorAni::ThrowParameterTypeError(env, STSErrorCode::STS_ERROR_PARAM_INVALID, "CheckAccessTokenSync", + GetParamErrorMsg("permissionName", "Permissions")); + return AccessToken::PermissionState::PERMISSION_DENIED; + } + auto* asyncContext = new (std::nothrow) AtManagerAsyncContext(); + if (asyncContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "New struct fail."); + return AccessToken::PermissionState::PERMISSION_DENIED; + } + std::unique_ptr context {asyncContext}; + asyncContext->tokenId = static_cast(tokenID); + asyncContext->permissionName = stdPermissionName; + static uint64_t selfTokenId = GetSelfTokenID(); + if (asyncContext->tokenId != static_cast(selfTokenId)) { + asyncContext->result = AccessToken::AccessTokenKit::VerifyAccessToken(tokenID, stdPermissionName); + return static_cast(asyncContext->result); + } + UpdatePermissionCache(asyncContext); + ACCESSTOKEN_LOG_INFO(LABEL, "CheckAccessTokenSync result : %{public}d", asyncContext->result); + return static_cast(asyncContext->result); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) +{ + if (vm == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr vm"); + return ANI_INVALID_ARGS; + } + ani_env* env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Unsupported ANI_VERSION_1"); + return ANI_OUT_OF_MEMORY; + } + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return ANI_NOT_FOUND; + } + const char* spaceName = "L@ohos/abilityAccessCtrl/abilityAccessCtrl;"; + ani_namespace spc; + if (ANI_OK != env->FindNamespace(spaceName, &spc)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Not found %{public}s", spaceName); + return ANI_NOT_FOUND; + } + std::array methods = { + ani_native_function { "createAtManager", nullptr, reinterpret_cast(CreateAtManager) }, + }; + if (ANI_OK != env->Namespace_BindNativeFunctions(spc, methods.data(), methods.size())) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot bind native methods to %{public}s", spaceName); + return ANI_ERROR; + }; + const char* className = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/AtManagerInner;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Not found %{public}s", className); + return ANI_ERROR; + } + std::array claMethods = { + ani_native_function { + "checkAccessTokenANI", "ILstd/core/String;:I", reinterpret_cast(CheckAccessTokenSync) }, + ani_native_function { "requestPermissionsFromUserExecute", + "Lapplication/Context/Context;Lescompat/Array;:Lsecurity/PermissionRequestResult/PermissionRequestResult;", + reinterpret_cast(RequestPermissionsFromUserExecute) }, + }; + if (ANI_OK != env->Class_BindNativeMethods(cls, claMethods.data(), claMethods.size())) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot bind native methods to %{public}s", className); + return ANI_ERROR; + }; + *result = ANI_VERSION_1; + return ANI_OK; +} +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ani/common/BUILD.gn b/frameworks/ets/ani/common/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..1f19f85aeab918dc9833d313c98dd60a4e4df6fb --- /dev/null +++ b/frameworks/ets/ani/common/BUILD.gn @@ -0,0 +1,82 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//base/security/access_token/access_token.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") + +ohos_static_library("libani_common") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + branch_protector_ret = "pac_ret" + + include_dirs = [ + "./include", + "${access_token_path}/interfaces/innerkits/accesstoken/include", + ] + + sources = [ "src/ani_error.cpp" ] + + deps = [ "${access_token_path}/frameworks/common:accesstoken_common_cxx" ] + cflags_cc = [ "-DHILOG_ENABLE" ] + external_deps = [ + "hilog:libhilog", + "runtime_core:ani", + ] + + subsystem_name = "security" + part_name = "access_token" +} + +generate_static_abc("Permission_Request_Result") { + base_url = "./ets" + files = [ "./ets/security/PermissionRequestResult.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/Permission_Request_Result.abc" +} + +ohos_prebuilt_etc("Permission_Request_Result_etc") { + source = "$target_out_dir/Permission_Request_Result.abc" + deps = [ ":Permission_Request_Result" ] + module_install_dir = "framework" + part_name = "access_token" + subsystem_name = "security" +} + +generate_static_abc("permissions") { + base_url = "./ets" + files = [ "./ets/permissions.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/permissions.abc" +} + +ohos_prebuilt_etc("permissions_etc") { + source = "$target_out_dir/permissions.abc" + deps = [ ":permissions" ] + module_install_dir = "framework" + part_name = "access_token" + subsystem_name = "security" +} + +group("common_ets") { + deps = [ + ":Permission_Request_Result_etc", + ":libani_common", + ":permissions_etc", + ] +} diff --git a/frameworks/ets/ani/common/ets/permissions.ets b/frameworks/ets/ani/common/ets/permissions.ets new file mode 100644 index 0000000000000000000000000000000000000000..c06103b9640f0ebf38d4c86f9e106cb2d5c2d8e5 --- /dev/null +++ b/frameworks/ets/ani/common/ets/permissions.ets @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Defines all permissions. + * @kit AbilityKit + */ + +/** + * Indicates permissions. + * + * @typedef { string } + * @syscap SystemCapability.Security.AccessToken + * @since 9 + */ + /** + * Indicates permissions. + * + * @typedef { string } + * @syscap SystemCapability.Security.AccessToken + * @atomicservice + * @since 11 + */ +export type Permissions = string; \ No newline at end of file diff --git a/frameworks/ets/ani/common/ets/security/PermissionRequestResult.ets b/frameworks/ets/ani/common/ets/security/PermissionRequestResult.ets new file mode 100644 index 0000000000000000000000000000000000000000..ba08345a99963339afd0fba9f88a734e81d99a4a --- /dev/null +++ b/frameworks/ets/ani/common/ets/security/PermissionRequestResult.ets @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @kit AbilityKit + */ + +/** + * The result of requestPermissionsFromUser with asynchronous callback. + * + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @since 9 + */ +/** + * The result of requestPermissionsFromUser with asynchronous callback. + * + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @since 10 + */ +/** + * The result of requestPermissionsFromUser with asynchronous callback. + * + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @atomicservice + * @since 11 + */ +export default class PermissionRequestResult { + /** + * The permissions passed in by the user. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @since 9 + */ + /** + * The permissions passed in by the user. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @since 10 + */ + /** + * The permissions passed in by the user. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @atomicservice + * @since 11 + */ + permissions: Array = new Array(); + + /** + * The results for the corresponding request permissions. The value 0 indicates that a + * permission is granted, the value -1 indicates not, and the value 2 indicates the request is invalid. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @since 9 + */ + /** + * The results for the corresponding request permissions. The value 0 indicates that a + * permission is granted, the value -1 indicates not, and the value 2 indicates the request is invalid. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @since 10 + */ + /** + * The results for the corresponding request permissions. The value 0 indicates that a + * permission is granted, the value -1 indicates not, and the value 2 indicates the request is invalid. + * + * @type { Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @atomicservice + * @since 11 + */ + authResults: Array = new Array(); + + /** + * Specifies whether a dialog box is shown for each requested permission. + * The value true means that a dialog box is shown, and false means the opposite. + * + * @type { ?Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @atomicservice + * @since 12 + */ + dialogShownResults?: Array; + + /** + * Enumerates the return values of the permission request operation. + * 0 The operation is successful. + * 1 The permission name is invalid. + * 2 The requested permission has not been declared. + * 3 The conditions for requesting the permission are not met. + * 4 The user does not agree to the Privacy Statement. + * 5 The permission cannot be requested in a pop-up window. + * 12 The service is abnormal. + * + * @type { ?Array } + * @syscap SystemCapability.Security.AccessToken + * @stagemodelonly + * @crossplatform + * @atomicservice + * @since 18 + */ + errorReasons?: Array; +} \ No newline at end of file diff --git a/frameworks/ets/ani/common/include/ani_error.h b/frameworks/ets/ani/common/include/ani_error.h new file mode 100644 index 0000000000000000000000000000000000000000..2af6c5ca3c0e4cb5da6613ffb85bc7d9e07ac41b --- /dev/null +++ b/frameworks/ets/ani/common/include/ani_error.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_PRIVACY_KITS_ANI_ERROR_H +#define INTERFACES_PRIVACY_KITS_ANI_ERROR_H + +#include +#include + +namespace OHOS { +namespace Security { +namespace AccessToken { +typedef enum { + STS_OK = 0, + STS_ERROR_PERMISSION_DENIED = 201, + STS_ERROR_NOT_SYSTEM_APP = 202, + STS_ERROR_PARAM_ILLEGAL = 401, + STS_ERROR_SYSTEM_CAPABILITY_NOT_SUPPORT = 801, + STS_ERROR_START_ABILITY_FAIL = 1011, + STS_ERROR_BACKGROUND_FAIL = 1012, + STS_ERROR_TERMINATE_FAIL = 1013, + STS_ERROR_PARAM_INVALID = 12100001, + STS_ERROR_TOKENID_NOT_EXIST, + STS_ERROR_PERMISSION_NOT_EXIST, + STS_ERROR_NOT_USE_TOGETHER, + STS_ERROR_REGISTERS_EXCEED_LIMITATION, + STS_ERROR_PERMISSION_OPERATION_NOT_ALLOWED, + STS_ERROR_SERVICE_NOT_RUNNING, + STS_ERROR_OUT_OF_MEMORY, + STS_ERROR_INNER, + STS_ERROR_REQUEST_IS_ALREADY_EXIST = 12100010, + STS_ERROR_ALL_PERM_GRANTED = 12100011, + STS_ERROR_PERM_REVOKE_BY_USER = 12100012, + STS_ERROR_GLOBAL_SWITCH_IS_ALREADY_OPEN = 12100013, +} STSErrorCode; +std::string GetParamErrorMsg(const std::string& param, const std::string& type); +std::string GetErrorMessage(uint32_t errCode); +class BusinessErrorAni { +public: + static ani_object CreateError(ani_env* env, ani_int code, const std::string& msg); + static ani_object CreateCommonError( + ani_env* env, int32_t err, const std::string& functionName = "", const std::string& permissionName = ""); + static ani_object CreateEnumError(ani_env* env, const std::string& parameter, const std::string& enumClass); + static void ThrowTooFewParametersError(ani_env* env, int32_t err); + static void ThrowParameterTypeError( + ani_env* env, int32_t err, const std::string& parameter, const std::string& type); + static void ThrowEnumError(ani_env* env, const std::string& parameter, const std::string& type); + static void ThrowError(ani_env* env, int32_t err, const std::string& msg = ""); + static int32_t GetStsErrorCode(int32_t errCode); + +private: + static void ThrowError(ani_env* env, ani_object err); +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* INTERFACES_PRIVACY_KITS_ANI_ERROR_H */ diff --git a/frameworks/ets/ani/common/src/ani_error.cpp b/frameworks/ets/ani/common/src/ani_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e8a2ffb7839280716b47d9c3c2547d967433e85 --- /dev/null +++ b/frameworks/ets/ani/common/src/ani_error.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ani_error.h" + +#include + +#include "access_token_error.h" +#include "accesstoken_log.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_PRIVACY, "CommonAni" }; +} // namespace +constexpr const int32_t RET_SUCCESS = 0; +constexpr const char* BUSINESS_ERROR_CLASS = "L@ohos/base/BusinessError;"; +constexpr const char* ERR_MSG_PARAM_NUMBER_ERROR = + "BusinessError 401: Parameter error. The number of parameters is incorrect."; +constexpr const char* ERR_MSG_ENUM_EROOR = "Parameter error. The value of $ is not a valid enum $."; +constexpr const char* ERR_MSG_BUSINESS_ERROR = "BusinessError $: "; +constexpr const char* ERR_MSG_PARAM_TYPE_ERROR = "Parameter error. The type of $ must be $."; +static const std::unordered_map g_errorStringMap = { + { STS_ERROR_PERMISSION_DENIED, "Permission denied." }, + { STS_ERROR_NOT_SYSTEM_APP, "Not system app." }, + { STS_ERROR_SYSTEM_CAPABILITY_NOT_SUPPORT, "Not support system capability." }, + { STS_ERROR_START_ABILITY_FAIL, "Start grant ability failed." }, + { STS_ERROR_BACKGROUND_FAIL, "Ui extension turn background failed." }, + { STS_ERROR_TERMINATE_FAIL, "Ui extension terminate failed." }, + { STS_ERROR_PARAM_INVALID, "Invalid parameter $." }, + { STS_ERROR_TOKENID_NOT_EXIST, "The specified token id does not exist." }, + { STS_ERROR_PERMISSION_NOT_EXIST, "The specified permission does not exist." }, + { STS_ERROR_NOT_USE_TOGETHER, "The API is not used in pair with others." }, + { STS_ERROR_REGISTERS_EXCEED_LIMITATION, "The number of registered listeners exceeds limitation." }, + { STS_ERROR_PERMISSION_OPERATION_NOT_ALLOWED, "The operation of specified permission is not allowed." }, + { STS_ERROR_SERVICE_NOT_RUNNING, "The service is abnormal." }, + { STS_ERROR_OUT_OF_MEMORY, "Out of memory." }, + { STS_ERROR_INNER, "Common inner error." }, + { STS_ERROR_REQUEST_IS_ALREADY_EXIST, "The request already exists." }, + { STS_ERROR_ALL_PERM_GRANTED, "All permissions in the permission list have been granted." }, + { STS_ERROR_PERM_REVOKE_BY_USER, + "The permission list contains the permission that has not been revoked by the user." }, + { STS_ERROR_GLOBAL_SWITCH_IS_ALREADY_OPEN, "The specific global switch is already open." }, + { STS_ERROR_PARAM_ILLEGAL, ERR_MSG_PARAM_TYPE_ERROR }, +}; + +void BusinessErrorAni::ThrowError(ani_env* env, int32_t err, const std::string& msg) +{ + if (env == nullptr) { + return; + } + ani_object error = CreateError(env, err, msg); + ThrowError(env, error); +} + +ani_object BusinessErrorAni::CreateError(ani_env* env, ani_int code, const std::string& msg) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env is nullptr"); + return nullptr; + } + ani_class cls = nullptr; + ani_field field = nullptr; + ani_method method = nullptr; + ani_object obj = nullptr; + + ani_status status = env->FindClass(BUSINESS_ERROR_CLASS, &cls); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass : %{public}d", status); + return nullptr; + } + status = env->Class_FindMethod(cls, "", ":V", &method); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindMethod : %{public}d", status); + return nullptr; + } + status = env->Object_New(cls, method, &obj); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New : %{public}d", status); + return nullptr; + } + status = env->Class_FindField(cls, "code", &field); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindField : %{public}d", status); + return nullptr; + } + status = env->Object_SetField_Double(obj, field, code); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Double : %{public}d", status); + return nullptr; + } + status = env->Class_FindField(cls, "data", &field); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindField : %{public}d", status); + return nullptr; + } + ani_string string = nullptr; + env->String_NewUTF8(msg.c_str(), msg.size(), &string); + status = env->Object_SetField_Ref(obj, field, static_cast(string)); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Ref : %{public}d", status); + return nullptr; + } + return obj; +} + +std::string GetParamErrorMsg(const std::string& param, const std::string& type) +{ + std::string msg = "Parameter Error. The type of \"" + param + "\" must be " + type + "."; + return msg; +} + +std::string GetErrorMessage(uint32_t errCode) +{ + auto iter = g_errorStringMap.find(errCode); + if (iter != g_errorStringMap.end()) { + return iter->second; + } + std::string errMsg = "Unknown error, errCode + " + std::to_string(errCode) + "."; + return errMsg; +} + +void BusinessErrorAni::ThrowParameterTypeError( + ani_env* env, int32_t err, const std::string& parameter, const std::string& type) +{ + if (env == nullptr) { + return; + } + ani_object error = CreateCommonError(env, err, parameter, type); + ThrowError(env, error); +} + +void BusinessErrorAni::ThrowTooFewParametersError(ani_env* env, int32_t err) +{ + if (env == nullptr) { + return; + } + ThrowError(env, err, ERR_MSG_PARAM_NUMBER_ERROR); +} + +ani_object BusinessErrorAni::CreateCommonError( + ani_env* env, int32_t err, const std::string& functionName, const std::string& permissionName) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env is nullptr"); + return nullptr; + } + std::string errMessage = ERR_MSG_BUSINESS_ERROR; + auto iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, std::to_string(err)); + } + if (g_errorStringMap.find(err) != g_errorStringMap.end()) { + errMessage += g_errorStringMap.at(err); + } + iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, functionName); + iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, permissionName); + } + } + return CreateError(env, err, errMessage); +} + +void BusinessErrorAni::ThrowEnumError(ani_env* env, const std::string& parameter, const std::string& type) +{ + if (env == nullptr) { + return; + } + ani_object error = CreateEnumError(env, parameter, type); + ThrowError(env, error); +} + +ani_object BusinessErrorAni::CreateEnumError(ani_env* env, const std::string& parameter, const std::string& enumClass) +{ + if (env == nullptr) { + return nullptr; + } + std::string errMessage = ERR_MSG_BUSINESS_ERROR; + auto iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, std::to_string(STS_ERROR_PARAM_ILLEGAL)); + } + errMessage += ERR_MSG_ENUM_EROOR; + iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, parameter); + iter = errMessage.find("$"); + if (iter != std::string::npos) { + errMessage = errMessage.replace(iter, 1, enumClass); + } + } + return CreateError(env, STS_ERROR_PARAM_ILLEGAL, errMessage); +} + +void BusinessErrorAni::ThrowError(ani_env* env, ani_object err) +{ + if (err == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "err is nullptr"); + return; + } + env->ThrowError(static_cast(err)); +} + +int32_t BusinessErrorAni::GetStsErrorCode(int32_t errCode) +{ + int32_t stsCode; + switch (errCode) { + case RET_SUCCESS: + stsCode = STS_OK; + break; + case ERR_PERMISSION_DENIED: + stsCode = STS_ERROR_PERMISSION_DENIED; + break; + case ERR_NOT_SYSTEM_APP: + stsCode = STS_ERROR_NOT_SYSTEM_APP; + break; + case ERR_PARAM_INVALID: + stsCode = STS_ERROR_PARAM_INVALID; + break; + case ERR_TOKENID_NOT_EXIST: + stsCode = STS_ERROR_TOKENID_NOT_EXIST; + break; + case ERR_PERMISSION_NOT_EXIST: + stsCode = STS_ERROR_PERMISSION_NOT_EXIST; + break; + case ERR_INTERFACE_NOT_USED_TOGETHER: + case ERR_CALLBACK_ALREADY_EXIST: + stsCode = STS_ERROR_NOT_USE_TOGETHER; + break; + case ERR_CALLBACKS_EXCEED_LIMITATION: + stsCode = STS_ERROR_REGISTERS_EXCEED_LIMITATION; + break; + case ERR_IDENTITY_CHECK_FAILED: + stsCode = STS_ERROR_PERMISSION_OPERATION_NOT_ALLOWED; + break; + case ERR_SERVICE_ABNORMAL: + case ERROR_IPC_REQUEST_FAIL: + case ERR_READ_PARCEL_FAILED: + case ERR_WRITE_PARCEL_FAILED: + stsCode = STS_ERROR_SERVICE_NOT_RUNNING; + break; + case ERR_MALLOC_FAILED: + stsCode = STS_ERROR_OUT_OF_MEMORY; + break; + default: + stsCode = STS_ERROR_INNER; + break; + } + ACCESSTOKEN_LOG_DEBUG(LABEL, "GetStsErrorCode nativeCode(%{public}d) stsCode(%{public}d).", errCode, stsCode); + return stsCode; +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS diff --git a/frameworks/ets/ani/privacy/BUILD.gn b/frameworks/ets/ani/privacy/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..be7b9417ff747068ab6fcd417845aa0fb21b749d --- /dev/null +++ b/frameworks/ets/ani/privacy/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//base/security/access_token/access_token.gni") +import("//build/config/components/ets_frontend/ets2abc_config.gni") +import("//build/ohos.gni") +import("//build/ohos/ace/ace.gni") + +ohos_shared_library("privacy_ani") { + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + + cflags_cc = [ "-DHILOG_ENABLE" ] + include_dirs = [ + "${access_token_path}/frameworks/common/include", + "${access_token_path}/interfaces/innerkits/accesstoken/include", + "${access_token_path}/interfaces/innerkits/privacy/include", + "${access_token_path}/frameworks/ets/ani/common/include", + ] + sources = [ "src/privacy_manager.cpp" ] + + deps = [ + "${access_token_path}/frameworks/ets/ani/common:libani_common", + "${access_token_path}/interfaces/innerkits/privacy:libprivacy_sdk", + ] + + external_deps = [ + "hilog:libhilog", + "runtime_core:ani", + "runtime_core:libarkruntime", + ] + + part_name = "access_token" + subsystem_name = "security" +} + +generate_static_abc("privacy_manager") { + base_url = "./ets" + files = [ "./ets/@ohos.privacyManager.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/privacy_manager.abc" +} + +ohos_prebuilt_etc("privacy_manager_etc") { + source = "$target_out_dir/privacy_manager.abc" + deps = [ ":privacy_manager" ] + module_install_dir = "framework" + + part_name = "access_token" + subsystem_name = "security" +} + +group("privacy_ets") { + deps = [ + ":privacy_ani", + ":privacy_manager_etc", + ] +} diff --git a/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..e308a928fb7e4f5fa1482235e57d946d782d0de8 --- /dev/null +++ b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AsyncCallback, Callback, BusinessError} from '@ohos.base'; +import { Permissions } from 'permissions'; + +function PARAM_ERROR_MSG(param: string, type: string): string { + return `Parameter Error. The type of "${param}" must be ${type}.`; +} + +export { Permissions } + +export default namespace privacyManager { + loadLibrary("privacy_ani.z"); + + enum PermissionUsedType { + NORMAL_TYPE = 0, + PICKER_TYPE = 1, + SECURITY_COMPONENT_TYPE = 2 + }; + + enum STSErrorCode { + STS_OK = 0, + STS_ERROR_PERMISSION_DENIED = 201, + STS_ERROR_NOT_SYSTEM_APP = 202, + STS_ERROR_PARAM_ILLEGAL = 401, + STS_ERROR_SYSTEM_CAPABILITY_NOT_SUPPORT = 801, + }; + + interface AddPermissionUsedRecordOptions { + usedType?: PermissionUsedType; + } + + class AddPermissionUsedRecordOptionsInner implements AddPermissionUsedRecordOptions { + usedType: PermissionUsedType|undefined; + } + + class PrivacyManagerInner { + native addPermissionUsedRecordSync(tokenID: int, permissionName: Permissions, successCount: int, failCount: int, + options: AddPermissionUsedRecordOptionsInner): void; + } + + interface UsedRecordDetail { + status: number; + lockScreenStatus?: number; + timestamp: number; + count?: number; + accessDuration: number; + usedType?: PermissionUsedType; + } + + interface PermissionUsedTypeInfo { + tokenId: number; + permissionName: Permissions; + usedType: PermissionUsedType; + } + + function addPermissionUsedRecord(tokenID: int, permissionName: Permissions, successCount: int, + failCount: int, options?: AddPermissionUsedRecordOptions): Promise { + if (typeof permissionName !== "string") { + let err = new BusinessError(); + err.code = STSErrorCode.STS_ERROR_PARAM_ILLEGAL; + err.data = PARAM_ERROR_MSG("permissionName", "Permissions"); + throw err; + } + + let usedTypeTemp : PermissionUsedType|undefined = options ? options.usedType : PermissionUsedType.NORMAL_TYPE; + let optionsLocal: AddPermissionUsedRecordOptionsInner = { usedType: usedTypeTemp }; + let p = new Promise((resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + return new PrivacyManagerInner().addPermissionUsedRecordSync( + tokenID, permissionName, successCount, failCount, optionsLocal) + }); + p1.then(() => { + resolve(undefined); + }).catch((err: BusinessError): void => { + reject(err); + }); + }); + return p; + } + + function addPermissionUsedRecord(tokenID: int, permissionName: Permissions, successCount: int, + failCount: int, callback: AsyncCallback): void { + if (typeof permissionName !== "string") { + let err = new BusinessError(); + err.code = STSErrorCode.STS_ERROR_PARAM_ILLEGAL; + err.data = PARAM_ERROR_MSG("permissionName", "Permissions"); + throw err; + } + let optionsLocal: AddPermissionUsedRecordOptionsInner = { usedType: PermissionUsedType.NORMAL_TYPE }; + let p2 = taskpool.execute((): void => { + return new PrivacyManagerInner().addPermissionUsedRecordSync(tokenID, permissionName, + successCount, failCount, optionsLocal)}); + p2.then(() => { + let err = new BusinessError(); + callback(err, undefined); + },(err: BusinessError): void=> { + callback(err, undefined); + }) + } +} \ No newline at end of file diff --git a/frameworks/ets/ani/privacy/src/privacy_manager.cpp b/frameworks/ets/ani/privacy/src/privacy_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..37f362595276d16d5abc0efa935ebcc320a1165e --- /dev/null +++ b/frameworks/ets/ani/privacy/src/privacy_manager.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "accesstoken_log.h" +#include "ani.h" +#include "ani_error.h" +#include "privacy_error.h" +#include "privacy_kit.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_PRIVACY, "AniPrivacyManager" }; + +static int32_t GetStsErrorCode(int32_t errCode) +{ + int32_t stsCode; + switch (errCode) { + case RET_SUCCESS: + stsCode = STS_OK; + break; + case ERR_PERMISSION_DENIED: + stsCode = STS_ERROR_PERMISSION_DENIED; + break; + case ERR_NOT_SYSTEM_APP: + stsCode = STS_ERROR_NOT_SYSTEM_APP; + break; + case ERR_PARAM_INVALID: + stsCode = STS_ERROR_PARAM_INVALID; + break; + case ERR_TOKENID_NOT_EXIST: + stsCode = STS_ERROR_TOKENID_NOT_EXIST; + break; + case ERR_PERMISSION_NOT_EXIST: + stsCode = STS_ERROR_PERMISSION_NOT_EXIST; + break; + case ERR_CALLBACK_ALREADY_EXIST: + case ERR_CALLBACK_NOT_EXIST: + case ERR_PERMISSION_ALREADY_START_USING: + case ERR_PERMISSION_NOT_START_USING: + stsCode = STS_ERROR_NOT_USE_TOGETHER; + break; + case ERR_CALLBACKS_EXCEED_LIMITATION: + stsCode = STS_ERROR_REGISTERS_EXCEED_LIMITATION; + break; + case ERR_IDENTITY_CHECK_FAILED: + stsCode = STS_ERROR_PERMISSION_OPERATION_NOT_ALLOWED; + break; + case ERR_SERVICE_ABNORMAL: + case ERROR_IPC_REQUEST_FAIL: + case ERR_READ_PARCEL_FAILED: + case ERR_WRITE_PARCEL_FAILED: + stsCode = STS_ERROR_SERVICE_NOT_RUNNING; + break; + case ERR_MALLOC_FAILED: + stsCode = STS_ERROR_OUT_OF_MEMORY; + break; + default: + stsCode = STS_ERROR_INNER; + break; + } + ACCESSTOKEN_LOG_DEBUG(LABEL, "GetStsErrorCode nativeCode(%{public}d) stsCode(%{public}d).", errCode, stsCode); + return stsCode; +} + +static void AddPermissionUsedRecordSync(ani_env* env, const AddPermParamInfo& info) +{ + auto retCode = PrivacyKit::AddPermissionUsedRecord(info); + if (retCode != RET_SUCCESS) { + int32_t stsCode = GetStsErrorCode(retCode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return; + } + ACCESSTOKEN_LOG_INFO(LABEL, "call addPermissionUsedRecord retCode : %{public}d", retCode); +} + +static void AddPermissionUsedRecord([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_int tokenID, ani_string permissionName, ani_int successCount, ani_int failCount, ani_object options) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return; + } + ani_size strSize; + ani_status status = ANI_ERROR; + if (ANI_OK != (status = env->String_GetUTF8Size(permissionName, &strSize))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_GetUTF8Size_Faild status : %{public}d", status); + return; + } + std::vector buffer(strSize + 1); + char* utf8Buffer = buffer.data(); + ani_size bytesWritten = 0; + if (ANI_OK != (status = env->String_GetUTF8(permissionName, utf8Buffer, strSize + 1, &bytesWritten))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "get String_GetUTF8 Faild status : %{public}d", status); + return; + } + utf8Buffer[bytesWritten] = '\0'; + std::string outputPermissionName = std::string(utf8Buffer); + ani_ref usedTypeRef; + if (ANI_OK != (status = env->Object_GetPropertyByName_Ref(options, "usedType", &usedTypeRef))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_GetFieldByName_Ref Faild status : %{public}d", status); + return; + } + ani_int usedType = 0; + ani_boolean isUndefined = true; + if (ANI_OK != (status = env->Reference_IsUndefined(usedTypeRef, &isUndefined))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "status : %{public}d", status); + return; + } + if (!isUndefined) { + ani_enum_item usedTypeEnum = static_cast(usedTypeRef); + if (ANI_OK != env->EnumItem_GetValue_Int(usedTypeEnum, &usedType)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "EnumItem_GetValue_Int Faild"); + return; + } + } + AddPermParamInfo info; + info.tokenId = static_cast(tokenID); + info.permissionName = outputPermissionName; + info.successCount = successCount; + info.failCount = failCount; + info.type = static_cast(usedType); + AddPermissionUsedRecordSync(env, info); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) +{ + if (vm == nullptr || result == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr vm or result"); + return ANI_INVALID_ARGS; + } + ani_env* env; + if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Unsupported ANI_VERSION_1"); + return ANI_OUT_OF_MEMORY; + } + + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return ANI_NOT_FOUND; + } + + const char* className = "L@ohos/privacyManager/privacyManager/PrivacyManagerInner;"; + ani_class cls; + if (ANI_OK != env->FindClass(className, &cls)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Not found %{public}s", className); + return ANI_NOT_FOUND; + } + + std::array methods = { + ani_native_function { "addPermissionUsedRecordSync", + "ILstd/core/String;IIL@ohos/privacyManager/privacyManager/AddPermissionUsedRecordOptionsInner;:V", + reinterpret_cast(AddPermissionUsedRecord) }, + }; + + if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot bind native methods to %{public}s", className); + return ANI_ERROR; + }; + + *result = ANI_VERSION_1; + return ANI_OK; +} +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file