diff --git a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets index 0f685d4eb473a04ad27b422fcfaa70ab90450e44..d30685d5f6dec9b7bb102cecc59477ac2944dccb 100644 --- a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets +++ b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets @@ -109,7 +109,7 @@ export default namespace abilityAccessCtrl { 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"); + err.data = PARAM_ERROR_MSG("permissionList", "Array"); throw err; } } @@ -118,7 +118,7 @@ export default namespace abilityAccessCtrl { if (typeof permissionName !== "string") { let err = new BusinessError(); err.code = STSErrorCode.STS_ERROR_PARAM_ILLEGAL; - err.data = PARAM_ERROR_MSG("permissionName", "string"); + err.data = PARAM_ERROR_MSG("permissionName", "Permissions"); throw err; } } diff --git a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp index ed15891e1ad077479250d8a671274bac05eddcff..78155fb44fccdc25dea5e70142e079cc1102ffb5 100644 --- a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp +++ b/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp @@ -496,7 +496,7 @@ static bool ParseRequestPermissionFromUser(ani_env* env, ani_object aniContext, } if (!ProcessArrayString(env, nullptr, permissionList, asyncContext->permissionList)) { BusinessErrorAni::ThrowParameterTypeError(env, STSErrorCode::STS_ERROR_PARAM_ILLEGAL, - GetParamErrorMsg("permissionList", "Array")); + GetParamErrorMsg("permissionList", "Array")); return false; } return true; @@ -861,7 +861,7 @@ static ani_int CheckAccessTokenSync([[maybe_unused]] ani_env* env, [[maybe_unuse std::string stdPermissionName = ANIUtils_ANIStringToStdString(env, static_cast(permissionName)); if (stdPermissionName.empty() || stdPermissionName.length() > MAX_LENGTH) { BusinessErrorAni::ThrowError(env, STSErrorCode::STS_ERROR_PARAM_INVALID, - GetErrorMessage(STSErrorCode::STS_ERROR_PARAM_INVALID)); + GetParamErrorMsg("permissionName", "Permissions")); return AccessToken::PermissionState::PERMISSION_DENIED; } auto* asyncContext = new (std::nothrow) AtManagerAsyncContext(); diff --git a/frameworks/ets/ani/common/BUILD.gn b/frameworks/ets/ani/common/BUILD.gn index 33085dc353214984d40cad66058f23940dd84756..66cbbd3aba1c1e0a2c0f6544fffe2b323461bde6 100644 --- a/frameworks/ets/ani/common/BUILD.gn +++ b/frameworks/ets/ani/common/BUILD.gn @@ -28,7 +28,10 @@ ohos_static_library("libani_common") { "${access_token_path}/interfaces/innerkits/accesstoken/include", ] - sources = [ "src/ani_error.cpp" ] + sources = [ + "src/ani_error.cpp", + "src/ani_utils.cpp" + ] deps = [ "${access_token_path}/frameworks/common:accesstoken_common_cxx" ] cflags_cc = [ "-DHILOG_ENABLE" ] diff --git a/frameworks/ets/ani/common/include/ani_error.h b/frameworks/ets/ani/common/include/ani_error.h index 705f5ba59df53ad5affae5aa572f5ea11a24979e..d698fd7bf22b1730f6fc805722854e3ddf8ac4aa 100644 --- a/frameworks/ets/ani/common/include/ani_error.h +++ b/frameworks/ets/ani/common/include/ani_error.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Huawei Device Co., Ltd. + * 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 @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef INTERFACES_PRIVACY_KITS_ANI_ERROR_H -#define INTERFACES_PRIVACY_KITS_ANI_ERROR_H +#ifndef INTERFACES_ETS_ANI_COMMON_ANI_ERROR_H +#define INTERFACES_ETS_ANI_COMMON_ANI_ERROR_H #include #include @@ -64,4 +64,4 @@ private: } // namespace AccessToken } // namespace Security } // namespace OHOS -#endif /* INTERFACES_PRIVACY_KITS_ANI_ERROR_H */ +#endif /* INTERFACES_ETS_ANI_COMMON_ANI_ERROR_H */ diff --git a/frameworks/ets/ani/common/include/ani_utils.h b/frameworks/ets/ani/common/include/ani_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..f86989e112f4e8e7ad8a4160c188b8497859da97 --- /dev/null +++ b/frameworks/ets/ani/common/include/ani_utils.h @@ -0,0 +1,60 @@ +/* + * 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_ETS_ANI_COMMON_ANI_UTILS_H +#define INTERFACES_ETS_ANI_COMMON_ANI_UTILS_H + +#include +#include +#include + +#include "ani.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +bool AniFindNameSpace(ani_env* env, const char* namespaceDescriptor, ani_namespace& out); +bool AniFindClass(ani_env* env, const char* classDescriptor, ani_class& out); +bool AniClassFindMethod(ani_env* env, const ani_class& aniClass, const char* methodDescriptor, const char* signature, + ani_method& out); +bool AniClassFindField(ani_env* env, const ani_class& aniClass, const char *fieldName, ani_field& out); +bool AniFindEnum(ani_env* env, const char *enumDescriptor, ani_enum& out); +bool AniGetEnumItemByIndex(ani_env* env, const ani_enum& aniEnum, ani_size index, ani_enum_item& out); + +bool AniParseString(ani_env* env, const ani_string& ani_str, std::string& out); +bool AniParseStringArray(ani_env* env, const ani_array_ref& ani_str_arr, std::vector& out); +bool AniParseCallback(ani_env* env, const ani_ref& ani_callback, ani_ref& out); +bool AniIsRefUndefined(ani_env* env, const ani_ref& ref, bool& isUndefined); + +bool AniNewString(ani_env* env, const std::string in, ani_string& out); +bool AniNewEnumIteam(ani_env* env, const char* enumDescriptor, ani_size index, ani_enum_item& out); +bool AniNewClassObject(ani_env* env, const ani_class aniClass, const char* methodDescriptor, const char* signature, + ani_object& out); + +bool AniObjectSetFieldInt(ani_env* env, const ani_class& aniClass, ani_object& aniObject, const char* fieldName, + ani_int in); +bool AniObjectSetFieldRef(ani_env* env, const ani_class& aniClass, ani_object& aniObject, const char* fieldName, + const ani_ref& in); +bool AniObjectSetPropertyByNameInt(ani_env* env, ani_object& object, const char *propertyName, int32_t in); +bool AniObjectSetPropertyByNameRef(ani_env* env, ani_object& object, const char *propertyName, ani_ref in); + +bool IsCurrentThread(std::thread::id threadId); +bool AniIsCallbackRefEqual(ani_env* env, const ani_ref& compareRef, const ani_ref& targetRref, std::thread::id threadId, + bool& isEqual); +bool AniFunctionalObjectCall(ani_env *env, const ani_fn_object& fn, ani_size size, ani_ref* argv, ani_ref& result); +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* INTERFACES_ETS_ANI_COMMON_ANI_UTILS_H */ diff --git a/frameworks/ets/ani/common/src/ani_error.cpp b/frameworks/ets/ani/common/src/ani_error.cpp index d09a3ee02ef254b131def8e5bd8afb5ea86fbd96..0f8d42f6bccefb9d84bb8d1f330f98906939fbf5 100644 --- a/frameworks/ets/ani/common/src/ani_error.cpp +++ b/frameworks/ets/ani/common/src/ani_error.cpp @@ -118,7 +118,7 @@ ani_object BusinessErrorAni::CreateError(ani_env* env, ani_int code, const std:: std::string GetParamErrorMsg(const std::string& param, const std::string& errMsg) { - std::string msg = "Parameter Error. The errMsg of \"" + param + "\" must be " + errMsg + "."; + std::string msg = "Parameter Error. The type of \"" + param + "\" must be " + errMsg + "."; return msg; } diff --git a/frameworks/ets/ani/common/src/ani_utils.cpp b/frameworks/ets/ani/common/src/ani_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22200802f27bfe0a4c9aa552c2f0dff4cf8c14db --- /dev/null +++ b/frameworks/ets/ani/common/src/ani_utils.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_utils.h" + +#include "accesstoken_log.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AccessTokenAniUtils" }; +} // namespace + +bool AniFindNameSpace(ani_env* env, const char* namespaceDescriptor, ani_namespace& out) +{ + if (env->FindNamespace(namespaceDescriptor, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindNamespace failed!"); + return false; + } + return true; +} + +bool AniFindClass(ani_env* env, const char* classDescriptor, ani_class& out) +{ + if (env->FindClass(classDescriptor, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass failed!"); + return false; + } + return true; +} + +bool AniClassFindMethod(ani_env* env, const ani_class& aniClass, const char* methodDescriptor, const char* signature, + ani_method& out) +{ + if (env->Class_FindMethod(aniClass, methodDescriptor, signature, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindMethod failed!"); + return false; + } + return true; +} + +bool AniClassFindField(ani_env* env, const ani_class& aniClass, const char *fieldName, ani_field& out) +{ + if (env->Class_FindField(aniClass, fieldName, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindField failed!"); + return false; + } + return true; +} + +bool AniFindEnum(ani_env* env, const char *enumDescriptor, ani_enum& out) +{ + if (env->FindEnum(enumDescriptor, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindEnum failed!"); + return false; + } + return true; +} + +bool AniGetEnumItemByIndex(ani_env* env, const ani_enum& aniEnum, ani_size index, ani_enum_item& out) +{ + if (env->Enum_GetEnumItemByIndex(aniEnum, index, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Enum_GetEnumItemByIndex failed!"); + return false; + } + return true; +} + + +bool AniParseString(ani_env* env, const ani_string& ani_str, std::string& out) +{ + ani_size strSize; + if (env->String_GetUTF8Size(ani_str, &strSize) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_GetUTF8Size failed!"); + return false; + } + + std::vector buffer(strSize + 1); // +1 for null terminator + char* utf8Buffer = buffer.data(); + ani_size bytesWritten = 0; + if (env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytesWritten) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_GetUTF8 failed!"); + return false; + } + if (bytesWritten == 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String is empty!"); + return false; + } + utf8Buffer[bytesWritten] = '\0'; + + out = std::string(utf8Buffer); + return true; +} + +bool AniParseStringArray(ani_env* env, const ani_array_ref& ani_str_arr, std::vector& out) +{ + ani_size size = 0; + if (env->Array_GetLength(ani_str_arr, &size) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_GetLength failed!"); + return false; + } + + for (ani_size i = 0; i < size; ++i) { + ani_ref aniRef; + if (env->Array_Get_Ref(ani_str_arr, i, &aniRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_Get_Ref failed!"); + return false; + } + + std::string stdStr; + if (!AniParseString(env, static_cast(aniRef), stdStr)) { + return false; + } + + out.emplace_back(stdStr); + } + return true; +} + +bool AniParseCallback(ani_env* env, const ani_ref& ani_callback, ani_ref& out) +{ + if (env->GlobalReference_Create(ani_callback, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GlobalReference_Create failed!"); + return false; + } + return true; +} + +bool AniIsRefUndefined(ani_env* env, const ani_ref& ref, bool& isUndefined) +{ + ani_boolean isUnd; + if (env->Reference_IsUndefined(ref, &isUnd) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Reference_IsUndefined failed!"); + return false; + } + isUndefined = isUnd ? true : false; + return true; +} + +bool AniNewString(ani_env* env, const std::string in, ani_string& out) +{ + if (env->String_NewUTF8(in.c_str(), in.size(), &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_NewUTF8 failed!"); + return false; + } + return true; +} + +bool AniNewEnumIteam(ani_env* env, const char* enumDescriptor, ani_size index, ani_enum_item& out) +{ + ani_enum aniEnum; + if (!AniFindEnum(env, enumDescriptor, aniEnum)) { + return false; + } + return AniGetEnumItemByIndex(env, aniEnum, index, out); +} + +bool AniNewClassObject(ani_env* env, const ani_class aniClass, const char* methodDescriptor, const char* signature, + ani_object& out) +{ + ani_method aniMethod; + if (!AniClassFindMethod(env, aniClass, methodDescriptor, signature, aniMethod)) { + return false; + } + + if (env->Object_New(aniClass, aniMethod, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New failed!"); + return false; + } + return true; +} + +bool AniObjectSetFieldInt(ani_env* env, const ani_class& aniClass, ani_object& aniObject, const char* fieldName, + ani_int in) +{ + ani_field aniField; + if (!AniClassFindField(env, aniClass, fieldName, aniField)) { + return false; + } + + if (env->Object_SetField_Int(aniObject, aniField, in) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Int failed!"); + return false; + } + return true; +} + +bool AniObjectSetFieldRef(ani_env* env, const ani_class& aniClass, ani_object& aniObject, const char* fieldName, + const ani_ref& in) +{ + ani_field aniField; + if (!AniClassFindField(env, aniClass, fieldName, aniField)) { + return false; + } + + if (env->Object_SetField_Ref(aniObject, aniField, in) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Ref failed!"); + return false; + } + return true; +} + +bool AniObjectSetPropertyByNameInt(ani_env* env, ani_object& object, const char *propertyName, ani_int in) +{ + if (env->Object_SetPropertyByName_Int(object, propertyName, in) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetPropertyByName_Int failed!"); + return false; + } + return true; +} + +bool AniObjectSetPropertyByNameRef(ani_env* env, ani_object& object, const char *propertyName, ani_ref in) +{ + if (env->Object_SetPropertyByName_Ref(object, propertyName, in) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetPropertyByName_Ref failed!"); + return false; + } + return true; +} + +bool IsCurrentThread(std::thread::id threadId) +{ + std::thread::id currentThread = std::this_thread::get_id(); + if (threadId != currentThread) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Ani_ref can not be compared, different threadId."); + return false; + } + return true; +} + +bool AniIsCallbackRefEqual(ani_env* env, const ani_ref& compareRef, const ani_ref& targetRref, std::thread::id threadId, + bool& isEqual) +{ + if (!IsCurrentThread(threadId)) { + return false; + } + + ani_boolean isEq = false; + if (env->Reference_StrictEquals(compareRef, targetRref, &isEq) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Reference_StrictEquals failed."); + return false; + } + isEqual = isEq ? true : false; + return true; +} + +bool AniFunctionalObjectCall(ani_env *env, const ani_fn_object& fn, ani_size size, ani_ref* argv, ani_ref& result) +{ + if (env->FunctionalObject_Call(fn, size, argv, &result) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "AniFunctionalObjectCall failed."); + return false; + } + return true; +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS diff --git a/frameworks/ets/ani/privacy/BUILD.gn b/frameworks/ets/ani/privacy/BUILD.gn index be7b9417ff747068ab6fcd417845aa0fb21b749d..cb1ddd50250328bff9049eab799990c576ea846d 100644 --- a/frameworks/ets/ani/privacy/BUILD.gn +++ b/frameworks/ets/ani/privacy/BUILD.gn @@ -29,6 +29,7 @@ ohos_shared_library("privacy_ani") { "${access_token_path}/interfaces/innerkits/accesstoken/include", "${access_token_path}/interfaces/innerkits/privacy/include", "${access_token_path}/frameworks/ets/ani/common/include", + "${access_token_path}/frameworks/ets/ani/privacy/include", ] sources = [ "src/privacy_manager.cpp" ] diff --git a/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets index cd954c35ef2657fce6c231d588d34d9dfcd90a35..f91c45fe68efc7913a29e3d5e1dbd13a496b7a36 100644 --- a/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets +++ b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets @@ -32,6 +32,12 @@ export default namespace privacyManager { SECURITY_COMPONENT_TYPE = 2 } + enum PermissionActiveStatus { + PERM_INACTIVE = 0, + PERM_ACTIVE_IN_FOREGROUND = 1, + PERM_ACTIVE_IN_BACKGROUND = 2 + } + enum STSErrorCode { STS_OK = 0, @@ -74,6 +80,24 @@ export default namespace privacyManager { usedType: PermissionUsedType; } + interface ActiveChangeResponse { + callingTokenId?: int; + tokenId: int; + permissionName: Permissions; + deviceId: string; + activeStatus: PermissionActiveStatus; + usedType?: PermissionUsedType; + } + + class ActiveChangeResponseInner implements ActiveChangeResponse { + callingTokenId?: int; + tokenId: int; + permissionName: Permissions; + deviceId: string; + activeStatus: PermissionActiveStatus; + usedType?: PermissionUsedType; + } + function addPermissionUsedRecord(tokenID: int, permissionName: Permissions, successCount: int, failCount: int, options?: AddPermissionUsedRecordOptions): Promise { if (typeof permissionName !== "string") { @@ -116,4 +140,10 @@ export default namespace privacyManager { callback(err, undefined); }) } + + export native function on(type: 'activeStateChange', permissionList: Array, + callback: Callback): void; + + export native function off(type: 'activeStateChange', permissionList: Array, + callback?: Callback): void; } \ No newline at end of file diff --git a/frameworks/ets/ani/privacy/include/privacy_manager.h b/frameworks/ets/ani/privacy/include/privacy_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..4129447bb91a3f0ff2a8f488de250b30bcf30db8 --- /dev/null +++ b/frameworks/ets/ani/privacy/include/privacy_manager.h @@ -0,0 +1,58 @@ +/* + * 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_ETS_ANI_PRIVACY_MANAGER_H +#define INTERFACES_ETS_ANI_PRIVACY_MANAGER_H + +#include +#include +#include +#include + +#include "ani.h" +#include "perm_active_status_customized_cbk.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +class PermActiveStatusPtr : public std::enable_shared_from_this, + public PermActiveStatusCustomizedCbk { +public: + explicit PermActiveStatusPtr(const std::vector& permList); + ~PermActiveStatusPtr() override; + void ActiveStatusChangeCallback(ActiveChangeResponse& result) override; + void SetVm(ani_vm* vm); + void SetEnv(ani_env* env); + void SetCallbackRef(const ani_ref& ref); + void SetThreadId(const std::thread::id threadId); +private: + ani_env* env_ = nullptr; + ani_vm* vm_ = nullptr; + ani_ref ref_ = nullptr; + std::thread::id threadId_; +}; + +struct RegisterPermActiveChangeContext { + ani_env* env = nullptr; + ani_ref callbackRef = nullptr; + std::string type; + std::vector permissionList; + std::shared_ptr subscriber = nullptr; + std::thread::id threadId; +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* INTERFACES_ETS_ANI_PRIVACY_MANAGER_H */ diff --git a/frameworks/ets/ani/privacy/src/privacy_manager.cpp b/frameworks/ets/ani/privacy/src/privacy_manager.cpp index 01d0d55727043d1e2f9fca9ba875c03151772fde..3528b7caa0350e3278addd07f2afd4ade92445fd 100644 --- a/frameworks/ets/ani/privacy/src/privacy_manager.cpp +++ b/frameworks/ets/ani/privacy/src/privacy_manager.cpp @@ -13,18 +13,35 @@ * limitations under the License. */ +#include "privacy_manager.h" + #include #include "accesstoken_log.h" -#include "ani.h" #include "ani_error.h" +#include "ani_utils.h" #include "privacy_error.h" #include "privacy_kit.h" namespace OHOS { namespace Security { namespace AccessToken { +namespace { static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_PRIVACY, "AniPrivacyManager" }; +std::mutex g_mutex; +std::vector g_subScribers; +static constexpr size_t MAX_CALLBACK_SIZE = 200; +static constexpr ani_size ACTIVE_CHANGE_TYPE_INDEX_ONE = 1; +static constexpr ani_size ACTIVE_CHANGE_TYPE_INDEX_TWO = 2; +static constexpr ani_size PERMMISSION_USED_TYPE_INDEX_ONE = 1; +static constexpr ani_size PERMMISSION_USED_TYPE_INDEX_TWO = 2; +constexpr const char* ACTIVE_CHANGE_FIELD_CALLING_TOKEN_ID = "callingTokenId"; +constexpr const char* ACTIVE_CHANGE_FIELD_TOKEN_ID = "tokenId"; +constexpr const char* ACTIVE_CHANGE_FIELD_PERMISSION_NAME = "permissionName"; +constexpr const char* ACTIVE_CHANGE_FIELD_DEVICE_ID = "deviceId"; +constexpr const char* ACTIVE_CHANGE_FIELD_ACTIVE_STATUS = "activeStatus"; +constexpr const char* ACTIVE_CHANGE_FIELD_USED_TYPE = "usedType"; +} static int32_t GetStsErrorCode(int32_t errCode) { @@ -141,6 +158,514 @@ static void AddPermissionUsedRecord([[maybe_unused]] ani_env* env, [[maybe_unuse AddPermissionUsedRecordSync(env, info); } +PermActiveStatusPtr::PermActiveStatusPtr(const std::vector& permList) + : PermActiveStatusCustomizedCbk(permList) +{} + +PermActiveStatusPtr::~PermActiveStatusPtr() +{ + if (vm_ == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "vm is nullptr;"); + return; + } + + bool isSameThread = (threadId_ == std::this_thread::get_id()); + ani_env* env; + if (isSameThread) { + env = env_; + } else { + ani_option interopEnabled {"--interop=disable", nullptr}; + ani_options aniArgs {1, &interopEnabled}; + vm_->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env); + } + + if (ref_ != nullptr) { + env->GlobalReference_Delete(ref_); + ref_ = nullptr; + } + + if (!isSameThread) { + vm_->DetachCurrentThread(); + } +} + +void PermActiveStatusPtr::SetVm(ani_vm* vm) +{ + vm_ = vm; +} + +void PermActiveStatusPtr::SetEnv(ani_env* env) +{ + env_ = env; +} + +void PermActiveStatusPtr::SetCallbackRef(const ani_ref& ref) +{ + ref_ = ref; +} + +void PermActiveStatusPtr::SetThreadId(const std::thread::id threadId) +{ + threadId_ = threadId; +} + +static bool GenerateAniClassAndObject(ani_env* env, ani_class& aniClass, ani_object& aniObject) +{ + const char* classDescriptor = "L@ohos/privacyManager/privacyManager/ActiveChangeResponseInner;"; + if (!AniFindClass(env, classDescriptor, aniClass)) { + return false; + } + + const char* methodDescriptor = ""; + const char* signature = nullptr; + if (!AniNewClassObject(env, aniClass, methodDescriptor, signature, aniObject)) { + return false; + } + + return true; +} + +static bool SetOptionalIntProperty(ani_env* env, ani_object& aniObject, const char* propertyName, const ani_int in) +{ + ani_class aniClass; + const char* classDescriptor = "Lstd/core/Int;"; + if (!AniFindClass(env, classDescriptor, aniClass)) { + return false; + } + + const char* methodDescriptor = ""; // means constructor + const char* signature = "I:V"; // I:V means input is int, return is void + ani_method aniMethod; + if (!AniClassFindMethod(env, aniClass, methodDescriptor, signature, aniMethod)) { + return false; + } + + ani_object intObject; + if (env->Object_New(aniClass, aniMethod, &intObject, in) != ANI_OK) { + return false; + } + + if (!AniObjectSetPropertyByNameRef(env, aniObject, propertyName, intObject)) { + return false; + } + + return true; +} + +static bool SetStringProperty(ani_env* env, ani_object& aniObject, const char* propertyName, const std::string in) +{ + ani_string aniString; + if (!AniNewString(env, in, aniString)) { + return false; + } + + if (!AniObjectSetPropertyByNameRef(env, aniObject, propertyName, aniString)) { + return false; + } + + return true; +} + +static bool SetEnumProperty(ani_env* env, ani_object& aniObject, const char* enumDescription, const char* propertyName, + ani_size index) +{ + ani_enum_item aniEnumItem; + if (!AniNewEnumIteam(env, enumDescription, index, aniEnumItem)) { + return false; + } + + if (!AniObjectSetPropertyByNameRef(env, aniObject, propertyName, aniEnumItem)) { + return false; + } + + return true; +} + +static bool TransFormActiveChangeTypeToIndex(ActiveChangeType type, ani_size& index) +{ + if (type == ActiveChangeType::PERM_INACTIVE) { + index = 0; + } else if (type == ActiveChangeType::PERM_ACTIVE_IN_FOREGROUND) { + index = ACTIVE_CHANGE_TYPE_INDEX_ONE; + } else if (type == ActiveChangeType::PERM_ACTIVE_IN_BACKGROUND) { + index = ACTIVE_CHANGE_TYPE_INDEX_TWO; + } else { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid ActiveChangeType value!"); + return false; + } + return true; +} + +static bool TransFormPermissionUsedTypeToIndex(PermissionUsedType type, ani_size& index) +{ + if (type == PermissionUsedType::NORMAL_TYPE) { + index = 0; + } else if (type == PermissionUsedType::PICKER_TYPE) { + index = PERMMISSION_USED_TYPE_INDEX_ONE; + } else if (type == PermissionUsedType::SECURITY_COMPONENT_TYPE) { + index = PERMMISSION_USED_TYPE_INDEX_TWO; + } else { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid PermissionUsedType value!"); + return false; + } + return true; +} + +static void ConvertActiveChangeResponse(ani_env* env, const ActiveChangeResponse& result, ani_object& aniObject) +{ + // class implements from interface should use property, independent class use field + ani_class aniClass; + if (!GenerateAniClassAndObject(env, aniClass, aniObject)) { + return; + } + + // set callingTokenID?: int optional parameter callingTokenID need box as a object + if (!SetOptionalIntProperty(env, aniObject, ACTIVE_CHANGE_FIELD_CALLING_TOKEN_ID, + static_cast(result.callingTokenID))) { + return; + } + + // set tokenID: int + if (!AniObjectSetPropertyByNameInt(env, aniObject, ACTIVE_CHANGE_FIELD_TOKEN_ID, + static_cast(result.tokenID))) { + return; + } + + // set permissionName: string + if (!SetStringProperty(env, aniObject, ACTIVE_CHANGE_FIELD_PERMISSION_NAME, result.permissionName)) { + return; + } + + // set deviceId: string + if (!SetStringProperty(env, aniObject, ACTIVE_CHANGE_FIELD_DEVICE_ID, result.deviceId)) { + return; + } + + // set activeStatus: PermissionActiveStatus + ani_size index = 0; + if (!TransFormActiveChangeTypeToIndex(result.type, index)) { + return; + } + const char* activeStatusDes = "L@ohos/privacyManager/privacyManager/PermissionActiveStatus;"; + if (!SetEnumProperty(env, aniObject, activeStatusDes, ACTIVE_CHANGE_FIELD_ACTIVE_STATUS, index)) { + return; + } + + // set usedType?: PermissionUsedType + index = 0; + if (!TransFormPermissionUsedTypeToIndex(result.usedType, index)) { + return; + } + const char* permUsedTypeDes = "L@ohos/privacyManager/privacyManager/PermissionUsedType;"; + if (!SetEnumProperty(env, aniObject, permUsedTypeDes, ACTIVE_CHANGE_FIELD_USED_TYPE, index)) { + return; + } +} + +void PermActiveStatusPtr::ActiveStatusChangeCallback(ActiveChangeResponse& activeChangeResponse) +{ + if (vm_ == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "vm is nullptr;"); + return; + } + + ani_option interopEnabled {"--interop=disable", nullptr}; + ani_options aniArgs {1, &interopEnabled}; + ani_env* env; + if (vm_->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "AttachCurrentThread failed!"); + return; + } + + ani_fn_object fnObj = reinterpret_cast(ref_); + if (fnObj == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Reinterpret_cast failed!"); + return; + } + + ani_object aniObject; + ConvertActiveChangeResponse(env, activeChangeResponse, aniObject); + + std::vector args; + args.emplace_back(aniObject); + ani_ref result; + if (!AniFunctionalObjectCall(env, fnObj, args.size(), args.data(), result)) { + return; + } + + if (vm_->DetachCurrentThread() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "DetachCurrentThread failed!"); + return; + } +} + +static bool ParseInputToRegister(const ani_string& aniType, const ani_array_ref& aniArray, + const ani_ref& aniCallback, RegisterPermActiveChangeContext* context, bool isReg) +{ + std::string type; // type: the first parameter is string + if (!AniParseString(context->env, aniType, type)) { + BusinessErrorAni::ThrowError(context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg("type", "string")); + return false; + } + + std::vector permList; // permissionList: the second parameter is Array + if (!AniParseStringArray(context->env, aniArray, permList)) { + BusinessErrorAni::ThrowError(context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg( + "permissionList", "Array")); + return false; + } + std::sort(permList.begin(), permList.end()); + + bool hasCallback = true; + if (!isReg) { + bool isUndefined = true; + if (!AniIsRefUndefined(context->env, aniCallback, isUndefined)) { + BusinessErrorAni::ThrowError( + context->env, STS_ERROR_PARAM_INVALID, GetErrorMessage(STSErrorCode::STS_ERROR_PARAM_INVALID)); + return false; + } + hasCallback = !isUndefined; + } + + ani_ref callback = nullptr; // callback: the third parameter is function + if (hasCallback) { + if (!AniParseCallback(context->env, aniCallback, callback)) { + BusinessErrorAni::ThrowError(context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg( + "callback", "Callback")); + return false; + } + } + + ani_vm* vm; + if (context->env->GetVM(&vm) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetVM failed!"); + return false; + } + + context->callbackRef = callback; + context->type = type; + context->permissionList = permList; + context->subscriber = std::make_shared(permList); + context->threadId = std::this_thread::get_id(); + + context->subscriber->SetVm(vm); + context->subscriber->SetEnv(context->env); + context->subscriber->SetCallbackRef(callback); + context->subscriber->SetThreadId(context->threadId); + + return true; +} + +static bool IsExistRegister(const RegisterPermActiveChangeContext* context) +{ + std::vector targetPermList; + context->subscriber->GetPermList(targetPermList); + std::lock_guard lock(g_mutex); + for (const auto& item : g_subScribers) { + std::vector permList; + item->subscriber->GetPermList(permList); + bool hasPermIntersection = false; + // Special cases: + // 1.Have registered full, and then register some + // 2.Have registered some, then register full + if (permList.empty() || targetPermList.empty()) { + hasPermIntersection = true; + } + for (const auto& PermItem : targetPermList) { + if (hasPermIntersection) { + break; + } + auto iter = std::find(permList.begin(), permList.end(), PermItem); + if (iter != permList.end()) { + hasPermIntersection = true; + } + } + bool isEqual = true; + if (!AniIsCallbackRefEqual(context->env, item->callbackRef, context->callbackRef, item->threadId, isEqual)) { + return true; + } + if (hasPermIntersection && isEqual) { + return true; + } + } + return false; +} + +static void RegisterPermActiveStatusCallback([[maybe_unused]] ani_env* env, + ani_string aniType, ani_array_ref aniArray, ani_ref callback) +{ + if (env == nullptr) { + BusinessErrorAni::ThrowError(env, STS_ERROR_INNER, GetErrorMessage(STSErrorCode::STS_ERROR_INNER)); + return; + } + + RegisterPermActiveChangeContext* context = new (std::nothrow) RegisterPermActiveChangeContext(); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to allocate memory for RegisterPermActiveChangeContext!"); + BusinessErrorAni::ThrowError(env, STS_ERROR_OUT_OF_MEMORY, GetErrorMessage( + STSErrorCode::STS_ERROR_OUT_OF_MEMORY)); + return; + } + context->env = env; + std::unique_ptr callbackPtr {context}; + + if (!ParseInputToRegister(aniType, aniArray, callback, context, true)) { + return; + } + + if (IsExistRegister(context)) { + ACCESSTOKEN_LOG_ERROR(LABEL, + "Subscribe failed. The current subscriber has existed or Reference_StrictEquals failed!"); + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_NOT_USE_TOGETHER, GetErrorMessage(STSErrorCode::STS_ERROR_NOT_USE_TOGETHER)); + return; + } + + int32_t result = PrivacyKit::RegisterPermActiveStatusCallback(context->subscriber); + if (result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "RegisterPermActiveStatusCallback failed, res is %{public}d", result); + int32_t stsCode = GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return; + } + + { + std::lock_guard lock(g_mutex); + if (g_subScribers.size() >= MAX_CALLBACK_SIZE) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Subscribers size has reached the max %{public}zu.", MAX_CALLBACK_SIZE); + BusinessErrorAni::ThrowError(env, STSErrorCode::STS_ERROR_REGISTERS_EXCEED_LIMITATION, + GetErrorMessage(STSErrorCode::STS_ERROR_REGISTERS_EXCEED_LIMITATION)); + return; + } + g_subScribers.emplace_back(context); + } + + callbackPtr.release(); + ACCESSTOKEN_LOG_INFO(LABEL, "RegisterPermActiveStatusCallback success!"); + return; +} + +static bool IsRefUndefined(ani_env* env, const ani_ref& ref) +{ + bool isUndef = false; + if (!AniIsRefUndefined(env, ref, isUndef)) { + return false; + } + return isUndef; +} + +static bool FindAndGetSubscriber(const RegisterPermActiveChangeContext* context, + std::vector& batchPermActiveChangeSubscribers) +{ + std::vector targetPermList = context->permissionList; + std::lock_guard lock(g_mutex); + bool callbackEqual; + ani_ref callbackRef = context->callbackRef; + bool isUndef = IsRefUndefined(context->env, context->callbackRef); + for (const auto& item : g_subScribers) { + std::vector permList; + item->subscriber->GetPermList(permList); + // targetCallback == nullptr, Unsubscribe from all callbacks under the same permList + // targetCallback != nullptr, unregister the subscriber with same permList and callback + if (isUndef) { + // batch delete currentThread callback + ACCESSTOKEN_LOG_INFO(LABEL, "Callback is nullptr."); + callbackEqual = IsCurrentThread(item->threadId); + } else { + ACCESSTOKEN_LOG_INFO(LABEL, "Compare callback."); + if (!AniIsCallbackRefEqual(context->env, item->callbackRef, callbackRef, item->threadId, callbackEqual)) { + continue; + } + } + + if (callbackEqual && (permList == targetPermList)) { + batchPermActiveChangeSubscribers.emplace_back(item); + if (!isUndef) { + return true; + } + } + } + if (!batchPermActiveChangeSubscribers.empty()) { + return true; + } + return false; +} + +static void DeleteRegisterInVector(const RegisterPermActiveChangeContext* context) +{ + std::vector targetPermList; + context->subscriber->GetPermList(targetPermList); + std::lock_guard lock(g_mutex); + auto item = g_subScribers.begin(); + while (item != g_subScribers.end()) { + bool isEqual = true; + if (!AniIsCallbackRefEqual( + context->env, (*item)->callbackRef, context->callbackRef, (*item)->threadId, isEqual)) { + continue; + } + if (!isEqual) { + continue; + } + + std::vector permList; + (*item)->subscriber->GetPermList(permList); + if (permList == targetPermList) { + delete *item; + *item = nullptr; + g_subScribers.erase(item); + return; + } else { + ++item; + } + } +} + +static void UnRegisterPermActiveStatusCallback([[maybe_unused]] ani_env* env, + ani_string aniType, ani_array_ref aniArray, ani_ref callback) +{ + if (env == nullptr) { + BusinessErrorAni::ThrowError(env, STS_ERROR_INNER, GetErrorMessage(STSErrorCode::STS_ERROR_INNER)); + return; + } + + RegisterPermActiveChangeContext* context = new (std::nothrow) RegisterPermActiveChangeContext(); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to allocate memory for RegisterPermActiveChangeContext!"); + BusinessErrorAni::ThrowError(env, STS_ERROR_OUT_OF_MEMORY, GetErrorMessage( + STSErrorCode::STS_ERROR_OUT_OF_MEMORY)); + return; + } + context->env = env; + std::unique_ptr callbackPtr {context}; + + if (!ParseInputToRegister(aniType, aniArray, callback, context, false)) { + return; + } + + std::vector batchPermActiveChangeSubscribers; + if (!FindAndGetSubscriber(context, batchPermActiveChangeSubscribers)) { + ACCESSTOKEN_LOG_ERROR(LABEL, + "Unsubscribe failed. The current subscriber does not exist or Reference_StrictEquals failed!"); + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_NOT_USE_TOGETHER, GetErrorMessage(STSErrorCode::STS_ERROR_NOT_USE_TOGETHER)); + return; + } + + for (const auto& item : batchPermActiveChangeSubscribers) { + int32_t result = PrivacyKit::UnRegisterPermActiveStatusCallback(item->subscriber); + if (result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "UnregisterPermActiveChangeCompleted failed"); + int32_t stsCode = GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return; + } + + DeleteRegisterInVector(item); + } + ACCESSTOKEN_LOG_INFO(LABEL, "UnRegisterPermActiveStatusCallback success!"); + return; +} + extern "C" { ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) { @@ -177,6 +702,24 @@ ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) return ANI_ERROR; }; + static const char *name = "L@ohos/privacyManager/privacyManager;"; + ani_namespace ns; + if (ANI_OK != env->FindNamespace(name, &ns)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindNamespace %{public}s failed.", name); + return ANI_ERROR; + } + + // namespace method input param without ani_object + std::array nsMethods = { + ani_native_function { "on", nullptr, reinterpret_cast(RegisterPermActiveStatusCallback) }, + ani_native_function { "off", nullptr, reinterpret_cast(UnRegisterPermActiveStatusCallback) }, + }; + + if (ANI_OK != env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size())) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Namespace_BindNativeFunctions %{public}s failed.", name); + return ANI_ERROR; + }; + *result = ANI_VERSION_1; return ANI_OK; } diff --git a/frameworks/js/napi/privacy/src/permission_record_manager_napi.cpp b/frameworks/js/napi/privacy/src/permission_record_manager_napi.cpp index d5f55eb757c857318481da2482064ed82d191f51..931092d6d029be8f7a7bf4529a8de20548bc9e3f 100644 --- a/frameworks/js/napi/privacy/src/permission_record_manager_napi.cpp +++ b/frameworks/js/napi/privacy/src/permission_record_manager_napi.cpp @@ -949,7 +949,7 @@ static bool ParseInputToRegister(const napi_env env, const napi_callback_info cb std::sort(permList.begin(), permList.end()); // 2: the third parameter of argv if (!ParseCallback(env, argv[2], callback)) { - ParamResolveErrorThrow(env, "callback", "AsyncCallback"); + ParamResolveErrorThrow(env, "callback", "Callback"); return false; } registerPermActiveChangeContext.env = env;