diff --git a/frameworks/ets/ani/BUILD.gn b/frameworks/ets/ani/BUILD.gn index 213648161dc7989669c4c318d24bfc577b7cf158..f60582a2cd204d2d73a350d88337dc3dd4b56bfa 100644 --- a/frameworks/ets/ani/BUILD.gn +++ b/frameworks/ets/ani/BUILD.gn @@ -19,6 +19,7 @@ group("arkts_package") { deps += [ "accesstoken:accesstoken_ets", "common:common_ets", + "el5filekeymanager:el5filekeymanager_ets", "privacy:privacy_ets", ] } diff --git a/frameworks/ets/ani/accesstoken/BUILD.gn b/frameworks/ets/ani/accesstoken/BUILD.gn index 6fe0a656122a59623b4de891af30a7b0119da6af..cd5ebb4008a9c9690e87b44e03908908b9e73273 100644 --- a/frameworks/ets/ani/accesstoken/BUILD.gn +++ b/frameworks/ets/ani/accesstoken/BUILD.gn @@ -31,7 +31,13 @@ ohos_shared_library("accesstoken_ani") { "${access_token_path}/interfaces/innerkits/token_callback/include", "${access_token_path}/frameworks/ets/ani/common/include", ] - sources = [ "src/ability_access_ctrl.cpp" ] + sources = [ + "src/ani_ability_access_ctrl.cpp", + "src/ani_common.cpp", + "src/ani_request_global_switch_on_setting.cpp", + "src/ani_request_permission.cpp", + "src/ani_request_permission_on_setting.cpp", + ] deps = [ "${access_token_path}/frameworks/ets/ani/common:libani_common", diff --git a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets index 39ea50e65e528fc6a2120d10aed17c0ac5cccd39..1f3dca1ec07c5b2fac6b0f38bd01f17303b2203c 100644 --- a/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets +++ b/frameworks/ets/ani/accesstoken/ets/@ohos.abilityAccessCtrl.ets @@ -22,15 +22,23 @@ 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; +class AsyncCallbackWrapper { + private wrapperCallback_: AsyncCallback = (err: BusinessError | null, data: T | undefined) => {} + + constructor(callback: AsyncCallback) { + this.wrapperCallback_ = callback; + } + + invoke(err: BusinessError, data: T) : void { + this.wrapperCallback_(err, data); + } +} + export default namespace abilityAccessCtrl { loadLibrary("accesstoken_ani.z"); - export enum GrantStatus { PERMISSION_DENIED = -1, PERMISSION_GRANTED = 0 @@ -72,10 +80,15 @@ export default namespace abilityAccessCtrl { STS_ERROR_NOT_SYSTEM_APP = 202, STS_ERROR_PARAM_ILLEGAL = 401, STS_ERROR_SYSTEM_CAPABILITY_NOT_SUPPORT = 801, + }; - } ; + class PermissionStateChangeInfoInner implements PermissionStateChangeInfo { + change: PermissionStateChangeType; + tokenID: int; + permissionName: Permissions; + } function validateRequestParams(context: Context, permissionList: Array): void { - if (typeof context === "undefined" || context == null) { + 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"); @@ -89,100 +102,374 @@ export default namespace abilityAccessCtrl { throw err; } } - native function createAtManager(): AtManager; + native function createAtManager(): AtManager; interface AtManager { - checkAccessTokenANI(tokenID: int, permissionName: Permissions): int; - checkAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus; - checkAccessToken(tokenID: int, permissionName: Permissions): Promise + checkAccessTokenExecute(tokenID: int, permissionName: Permissions): int; + requestPermissionsFromUserExecute(context: Context, permissionList: Array, + callback: AsyncCallbackWrapper): void; + requestPermissionOnSettingExecute(context: Context, + permissionList: Array, callback: AsyncCallbackWrapper>): void; + requestGlobalSwitchExecute( + context: Context, type: int, callback: AsyncCallbackWrapper): void; + revokeUserGrantedPermissionExecute(tokenID: int, permissionName: Permissions, permissionFlags: int): void; + getPermissionsStatusExecute(tokenID: int, permissionList: Array): Array; + getVersionExecute(): int; + getPermissionFlagsExecute(tokenID: int, permissionName: Permissions): int; + setPermissionRequestToggleStatusExecute(permissionName: Permissions, status: int): void; + getPermissionRequestToggleStatusExecute(permissionName: Permissions): int; + RequestAppPermOnSettingExecute(tokenID: int): undefined; + onExcute(type: string, tokenIDList: Array, permissionList: Array, + callback: Callback): void; + offExcute(type: string, tokenIDList: Array, permissionList: Array, + callback?: Callback): void; + requestAppPermOnSettingExecute(tokenID: int): void; + checkAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus; verifyAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus; - - requestPermissionsFromUserExecute( - context: Context, - permissionList: Array): PermissionRequestResult; - + checkAccessToken(tokenID: int, permissionName: Permissions): Promise; + verifyAccessToken(tokenID: int, permissionName: Permissions): Promise; requestPermissionsFromUser( - context: Context, - permissionList: Array, - requestCallback: AsyncCallback - ): void; - + context: Context, permissionList: Array, + requestCallback: AsyncCallback): void; requestPermissionsFromUser( - context: Context, - permissionList: Array): Promise; + context: Context, permissionList: Array): Promise; + requestPermissionOnSetting( + context: Context, permissionList: Array): Promise>; + requestGlobalSwitch(context: Context, type: SwitchType): Promise; + grantUserGrantedPermission( + tokenID: int, + permissionName: Permissions, + permissionFlags: int, + callback: AsyncCallback): void; + + grantUserGrantedPermission( + tokenID: int, + permissionName: Permissions, + permissionFlags: int): Promise; + + revokeUserGrantedPermission(tokenID: int, permissionName: Permissions, permissionFlags: int): Promise; + revokeUserGrantedPermission( + tokenID: int, permissionName: Permissions, permissionFlags: int, callback: AsyncCallback): void; + getVersion(): Promise; + getPermissionsStatus(tokenID: int, permissionList: Array): Promise>; + getPermissionFlags(tokenID: int, permissionName: Permissions): Promise; + setPermissionRequestToggleStatus( + permissionName: Permissions, status: PermissionRequestToggleStatus): Promise; + getPermissionRequestToggleStatus(permissionName: Permissions): Promise; + requestPermissionOnApplicationSetting(tokenID: int): Promise; + on(type: 'selfPermissionStateChange', permissionList: Array, + callback: Callback): void; + on(type: 'permissionStateChange', tokenIDList: Array, permissionList: Array, + callback: Callback): void; + off(type: 'selfPermissionStateChange', permissionList: Array, + callback?: Callback): void; + off(type: 'permissionStateChange', tokenIDList: Array, permissionList: Array, + callback: Callback): void; } class AtManagerInner implements AtManager { - native checkAccessTokenANI(tokenID: int, permissionName: Permissions): int; + native checkAccessTokenExecute(tokenID: int, permissionName: Permissions): int; + native requestPermissionsFromUserExecute(context: Context, permissionList: Array, + callback: AsyncCallbackWrapper): void; + native requestPermissionOnSettingExecute(context: Context, + permissionList: Array, callback: AsyncCallbackWrapper>): void; + native requestGlobalSwitchExecute( + context: Context, type: int, callback: AsyncCallbackWrapper): void; + native grantUserGrantedPermissionExecute(tokenID: int, permissionName: Permissions, permissionFlags: int): void; + native revokeUserGrantedPermissionExecute( + tokenID: int, permissionName: Permissions, permissionFlags: int): void; + native getPermissionsStatusExecute(tokenID: int, permissionList: Array): Array; + native getVersionExecute(): int; + native getPermissionFlagsExecute(tokenID: int, permissionName: Permissions): int; + native setPermissionRequestToggleStatusExecute(permissionName: Permissions, status: int): void; + native getPermissionRequestToggleStatusExecute(permissionName: Permissions): int; + native RequestAppPermOnSettingExecute(tokenID: int): undefined; + native onExcute(type: string, tokenIDList: Array, permissionList: Array, + callback: Callback): void; + native offExcute(type: string, tokenIDList: Array, permissionList: Array, + callback?: Callback): void; + native requestAppPermOnSettingExecute(tokenID: int): void; verifyAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus { - let result = this.checkAccessTokenANI(tokenID, permissionName); - if (result == 0) { - return GrantStatus.PERMISSION_GRANTED; - } - return GrantStatus.PERMISSION_DENIED; + let result = this.checkAccessTokenExecute(tokenID, permissionName); + return result as GrantStatus; } checkAccessTokenSync(tokenID: int, permissionName: Permissions): GrantStatus { - let result = this.checkAccessTokenANI(tokenID, permissionName); - if (result == 0) { - return GrantStatus.PERMISSION_GRANTED; - } - return GrantStatus.PERMISSION_DENIED; + let result = this.checkAccessTokenExecute(tokenID, permissionName); + return result as GrantStatus; } 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: Error): void => { - reject(err as BusinessError); - }); + let p = new Promise(( + resolve: (v: GrantStatus) => void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): GrantStatus => { + return new AtManagerInner().checkAccessTokenExecute(tokenID, permissionName) as GrantStatus; }); + p1.then((e: NullishType) => { + resolve(e as GrantStatus); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); return p; } - native requestPermissionsFromUserExecute(context: Context, - permissionList: Array): PermissionRequestResult; + verifyAccessToken(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().checkAccessTokenExecute(tokenID, permissionName) as GrantStatus; + }); + p1.then((e: NullishType) => { + resolve(e as GrantStatus); + }).catch((err: Error): void => { + resolve(GrantStatus.PERMISSION_DENIED); + }); + }); + return p; + } requestPermissionsFromUser(context: Context, permissionList: Array, - callback: AsyncCallback): void { + requestCallback: 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); - }) + let callbackWrap = new AsyncCallbackWrapper(requestCallback); + taskpool.execute((): void => { + new AtManagerInner().requestPermissionsFromUserExecute(context, permissionList, callbackWrap); + }); } requestPermissionsFromUser(context: Context, permissionList: Array): Promise { validateRequestParams(context, permissionList); - let p = new Promise(( + let p: Promise = 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: Error): void => { - if ((err as BusinessError).code == STSErrorCode.STS_ERROR_PARAM_ILLEGAL) { - throw err; + let callbackWrap = new AsyncCallbackWrapper(( + err: BusinessError | null, data: PermissionRequestResult | undefined) => { + if (err?.code == 0) { + if (data !== undefined) { + resolve(data); + } + } else { + reject(err as BusinessError); } + }); + taskpool.execute((): void => { + new AtManagerInner().requestPermissionsFromUserExecute(context, permissionList, callbackWrap); + }); + }); + return p; + } + + requestPermissionOnSetting(context: Context, permissionList: Array): Promise> { + validateRequestParams(context, permissionList); + let p: Promise> = new Promise>(( + resolve: (v: Array) => void, reject: (error: BusinessError) => void) => { + let callbackWrap = new AsyncCallbackWrapper>(( + err: BusinessError | null, data: Array | undefined) => { + if (err?.code == 0) { + if (data !== undefined) { + resolve(data); + } + } else { + reject(err as BusinessError); + } + }); + taskpool.execute((): void => { + new AtManagerInner().requestPermissionOnSettingExecute(context, permissionList, callbackWrap); + }); + }); + return p; + } + + requestGlobalSwitch(context: Context, type: SwitchType): Promise { + 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; + } + let p: Promise = new Promise(( + resolve: (v: boolean) => void, reject: (error: BusinessError) => void) => { + let callbackWrap = new AsyncCallbackWrapper(( + err: BusinessError | null, data: boolean | undefined) => { + if (err?.code == 0) { + if (data !== undefined) { + resolve(data); + } + } else { + reject(err as BusinessError); + } + }); + taskpool.execute((): void => { + new AtManagerInner().requestGlobalSwitchExecute(context, type, callbackWrap); + }); + }); + return p; + } + + grantUserGrantedPermission(tokenID: int, permissionName: Permissions, permissionFlags: int, + callback: AsyncCallback): void { + let p1 = taskpool.execute((): void => { + new AtManagerInner().grantUserGrantedPermissionExecute(tokenID, permissionName, permissionFlags);}); + p1.then(() => { + callback(null, undefined); + }, (err: Error): void => { + callback(err as BusinessError, undefined); + }); + } + + grantUserGrantedPermission(tokenID: int, permissionName: Permissions, permissionFlags: int): Promise { + return new Promise( + (resolve: (v: undefined) => void, reject: (error: BusinessError) => void) : void => { + let p = taskpool.execute(() : void => { + new AtManagerInner().grantUserGrantedPermissionExecute(tokenID, permissionName, permissionFlags); + }); + p.then((e: NullishType) : void => { + resolve(undefined); + }).catch((err: Error) : void => { + reject(err as BusinessError); + }); + }); + } + + revokeUserGrantedPermission(tokenID: int, permissionName: Permissions, permissionFlags: int): Promise { + return new Promise( + (resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p = taskpool.execute((): void => { + new AtManagerInner().revokeUserGrantedPermissionExecute(tokenID, permissionName, permissionFlags); + }); + p.then((e: NullishType): void => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } + + revokeUserGrantedPermission(tokenID: int, permissionName: Permissions, + permissionFlags: int, callback: AsyncCallback): void { + let job = taskpool.execute((): void => { + new AtManagerInner().revokeUserGrantedPermissionExecute(tokenID, permissionName, permissionFlags); + }); + job.then(() => { + callback(null, undefined); + }, (err: Error): void=> { + callback(err as BusinessError, undefined); + }); + } + + getVersion(): Promise { + let p = new Promise(( + resolve: (v: int) => void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): int => { + return new AtManagerInner().getVersionExecute() as int; + }); + p1.then((e: NullishType) => { + resolve(e as int); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; + } + + getPermissionsStatus(tokenID: int, permissionList: Array): Promise> { + let p = new Promise>(( + resolve: (v: Array) => void, reject: (error: BusinessError) => void) => { + let p1 = taskpool.execute((): Array => { + return new AtManagerInner().getPermissionsStatusExecute( + tokenID, permissionList) as Array; + }); + p1.then((e: NullishType) => { + resolve(e as Array); + }).catch((err: Error): void => { reject(err as BusinessError); }); }); return p; } + + getPermissionFlags(tokenID: int, permissionName: Permissions): Promise { + let p = new Promise( + (resolve: (v: int) => void, reject: (err: BusinessError) => void): int => { + let p1 = taskpool.execute((): int => { + return new AtManagerInner().getPermissionFlagsExecute(tokenID, permissionName); + }); + p1.then((e: NullishType): void => { + resolve(e as int); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }) + return p; + } + + setPermissionRequestToggleStatus(permissionName: Permissions, status: PermissionRequestToggleStatus): Promise { + let p = new Promise( + (resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p = taskpool.execute((): void => { + new AtManagerInner().setPermissionRequestToggleStatusExecute(permissionName, status); + }); + p.then((e: NullishType): void => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; + } + + getPermissionRequestToggleStatus(permissionName: Permissions): Promise { + let p = new Promise( + (resolve: (v: PermissionRequestToggleStatus) => void, + reject: (err: BusinessError) => void): PermissionRequestToggleStatus => { + let p1 = taskpool.execute((): PermissionRequestToggleStatus => { + return new AtManagerInner().getPermissionRequestToggleStatusExecute(permissionName) as PermissionRequestToggleStatus; + }); + p1.then((e: NullishType): void => { + resolve(e as PermissionRequestToggleStatus); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; + } + + requestPermissionOnApplicationSetting(tokenID: int): Promise { + let p = new Promise( + (resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p = taskpool.execute((): void => { + new AtManagerInner().requestAppPermOnSettingExecute(tokenID); + }); + p.then((e: NullishType): void => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }) + }); + return p; + } + + on(type: 'selfPermissionStateChange', permissionList: Array, + callback: Callback): void { + new AtManagerInner().onExcute('selfPermissionStateChange', [], permissionList, callback); + } + + on(type: 'permissionStateChange', tokenIDList: Array, permissionList: Array, + callback: Callback): void { + new AtManagerInner().onExcute('permissionStateChange', tokenIDList, permissionList, callback); + } + + off(type: 'selfPermissionStateChange', permissionList: Array, + callback?: Callback): void { + new AtManagerInner().offExcute('selfPermissionStateChange', [], permissionList, callback); + } + + off(type: 'permissionStateChange', tokenIDList: Array, permissionList: Array, + callback?: Callback): void { + new AtManagerInner().offExcute('permissionStateChange', tokenIDList, permissionList, callback); + } } } diff --git a/frameworks/ets/ani/accesstoken/include/ani_ability_access_ctrl.h b/frameworks/ets/ani/accesstoken/include/ani_ability_access_ctrl.h new file mode 100644 index 0000000000000000000000000000000000000000..179bd111ac08c6ed06c0b9db5002dd0bf4a67393 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/include/ani_ability_access_ctrl.h @@ -0,0 +1,51 @@ +/* + * 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 "access_token.h" +#include "ani_error.h" +namespace OHOS { +namespace Security { +namespace AccessToken { +const int32_t PARAM_DEFAULT_VALUE = -1; +struct AtManagerAsyncContext { + AccessTokenID tokenId = 0; + std::string permissionName; + union { + uint32_t flag = 0; + uint32_t status; + }; + int32_t grantStatus = PERMISSION_DENIED; + AtmResult result; +}; + +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; +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS + +#endif // ABILITY_ACCESS_CTRL_H \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/include/ani_common.h b/frameworks/ets/ani/accesstoken/include/ani_common.h new file mode 100644 index 0000000000000000000000000000000000000000..61284c998dd8c48e5627bbae4a36138083d14ef0 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/include/ani_common.h @@ -0,0 +1,34 @@ +/* + * 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_H +#define INTERFACES_ETS_ANI_COMMON_H + +#include "ability_context.h" +#include "ani.h" +#include "ani_base_context.h" +#include "ui_content.h" +#include "ui_extension_context.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +bool ExecuteAsyncCallback(ani_env* env, ani_object callback, ani_object error, ani_object result); +OHOS::Ace::UIContent* GetUIContent(const std::shared_ptr& abilityContext, + std::shared_ptr& uiExtensionContext, bool uiAbilityFlag); +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* INTERFACES_ETS_ANI_COMMON_H */ \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/include/ani_request_global_switch_on_setting.h b/frameworks/ets/ani/accesstoken/include/ani_request_global_switch_on_setting.h new file mode 100644 index 0000000000000000000000000000000000000000..d8fb85cf10913174659041e3db097a5d708da23b --- /dev/null +++ b/frameworks/ets/ani/accesstoken/include/ani_request_global_switch_on_setting.h @@ -0,0 +1,101 @@ +/* + * 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 ANI_REQUEST_GLOBAL_SWITCHN_ON_SETTING_H +#define ANI_REQUEST_GLOBAL_SWITCHN_ON_SETTING_H + +#include "access_token.h" +#include "ani.h" +#include "ani_common.h" +#include "ani_error.h" +#include "ani_utils.h" +#ifdef EVENTHANDLER_ENABLE +#include "event_handler.h" +#include "event_queue.h" +#endif +#include "permission_grant_info.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +typedef enum { + CAMERA = 0, + MICROPHONE = 1, + LOCATION = 2, +} SwitchType; + +struct RequestGlobalSwitchAsyncContext { + virtual ~RequestGlobalSwitchAsyncContext(); + AccessTokenID tokenId = 0; + AtmResult result; + PermissionGrantInfo info; + + int32_t instanceId = -1; + bool isDynamic = true; + bool switchStatus = false; + int32_t switchType = -1; + + std::shared_ptr abilityContext = nullptr; + std::shared_ptr uiExtensionContext = nullptr; + bool uiAbilityFlag = false; + bool releaseFlag = false; + std::mutex lockReleaseFlag; + +#ifdef EVENTHANDLER_ENABLE + std::shared_ptr handler_ = + std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif + std::thread::id threadId; + ani_vm* vm = nullptr; + ani_env* env = nullptr; + ani_object callback = nullptr; + ani_ref callbackRef = nullptr; +}; + +class RequestGlobalSwitchAsyncInstanceControl { + public: + static void AddCallbackByInstanceId(std::shared_ptr& asyncContext); + static void ExecCallback(int32_t id); + static void CheckDynamicRequest( + std::shared_ptr& asyncContext, bool& isDynamic); + static void UpdateQueueData(const std::shared_ptr& asyncContext); + private: + static std::map>> instanceIdMap_; + static std::mutex instanceIdMutex_; +}; + +class SwitchOnSettingUICallback { +public: + explicit SwitchOnSettingUICallback(const std::shared_ptr& reqContext); + ~SwitchOnSettingUICallback(); + void SetSessionId(int32_t sessionId); + void ReleaseHandler(int32_t code); + void OnRelease(int32_t releaseCode); + void OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result); + void OnReceive(const OHOS::AAFwk::WantParams& request); + void OnError(int32_t code, const std::string& name, const std::string& message); + void OnRemoteReady(const std::shared_ptr& uiProxy); + void OnDestroy(); + +private: + std::shared_ptr reqContext_ = nullptr; + int32_t sessionId_ = 0; +}; + +void RequestGlobalSwitchExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_object aniContext, ani_int status, ani_object callback); +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* ANI_REQUEST_GLOBAL_SWITCHN_ON_SETTING_H */ diff --git a/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h b/frameworks/ets/ani/accesstoken/include/ani_request_permission.h similarity index 62% rename from frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h rename to frameworks/ets/ani/accesstoken/include/ani_request_permission.h index f7844ba9ff1335a041403f587b6048f1912150f6..cfcbfcf3a1188ca0da83ff9e5cc3a97a6a3f8a69 100644 --- a/frameworks/ets/ani/accesstoken/include/ability_access_ctrl.h +++ b/frameworks/ets/ani/accesstoken/include/ani_request_permission.h @@ -12,89 +12,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#ifndef ANI_REQUEST_PERMISSION_H +#define ANI_REQUEST_PERMISSION_H -#ifndef ABILITY_ACCESS_CTRL_H -#define ABILITY_ACCESS_CTRL_H - -#include -#include -#include #include - -#include "ability.h" -#include "ability_manager_client.h" +#include #include "access_token.h" #include "ani.h" +#include "ani_common.h" +#include "ani_error.h" +#include "ani_utils.h" +#ifdef EVENTHANDLER_ENABLE +#include "event_handler.h" +#include "event_queue.h" +#endif #include "permission_grant_info.h" +#include "perm_state_change_callback_customize.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 { + virtual ~RequestAsyncContext(); AccessTokenID tokenId = 0; std::string bundleName; bool needDynamicRequest = true; - int32_t result = RET_SUCCESS; + AtmResult result; 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; + std::shared_ptr abilityContext = nullptr; + std::shared_ptr uiExtensionContext = nullptr; bool uiAbilityFlag = false; bool uiExtensionFlag = false; bool uiContentFlag = false; @@ -103,6 +57,11 @@ struct RequestAsyncContext { #ifdef EVENTHANDLER_ENABLE std::shared_ptr handler_ = nullptr; #endif + std::thread::id threadId; + ani_vm* vm = nullptr; + ani_env* env = nullptr; + ani_object callback = nullptr; + ani_ref callbackRef = nullptr; }; class UIExtensionCallback { @@ -138,7 +97,7 @@ private: class RequestAsyncInstanceControl { public: - static bool AddCallbackByInstanceId(std::shared_ptr& asyncContext); + static void AddCallbackByInstanceId(std::shared_ptr& asyncContext); static void ExecCallback(int32_t id); static void CheckDynamicRequest(std::shared_ptr& asyncContext, bool& isDynamic); @@ -154,10 +113,41 @@ struct ResultCallback { std::shared_ptr data = nullptr; }; -std::map>> RequestAsyncInstanceControl::instanceIdMap_; -std::mutex RequestAsyncInstanceControl::instanceIdMutex_; +void RequestPermissionsFromUserExecute([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_object aniContext, ani_array_ref permissionList, ani_object callback); + +class RegisterPermStateChangeScopePtr : public std::enable_shared_from_this, + public PermStateChangeCallbackCustomize { +public: + explicit RegisterPermStateChangeScopePtr(const PermStateChangeScope& subscribeInfo); + ~RegisterPermStateChangeScopePtr() override; + void PermStateChangeCallback(PermStateChangeInfo& result) override; + void SetCallbackRef(const ani_ref& ref); + void SetValid(bool valid); + void SetEnv(ani_env* env); + + void SetVm(ani_vm* vm); + void SetThreadId(const std::thread::id threadId); +private: + bool valid_ = true; + std::mutex validMutex_; + ani_env* env_ = nullptr; + + ani_vm* vm_ = nullptr; + std::thread::id threadId_; + ani_ref ref_ = nullptr; +}; + +struct RegisterPermStateChangeInf { + ani_env* env = nullptr; + ani_ref callbackRef = nullptr; + int32_t errCode = RET_SUCCESS; + std::string permStateChangeType; + std::thread::id threadId; + std::shared_ptr subscriber = nullptr; + PermStateChangeScope scopeInfo; +}; } // namespace AccessToken } // namespace Security } // namespace OHOS - -#endif // ABILITY_ACCESS_CTRL_H \ No newline at end of file +#endif /* ANI_REQUEST_PERMISSION_H */ diff --git a/frameworks/ets/ani/accesstoken/include/ani_request_permission_on_setting.h b/frameworks/ets/ani/accesstoken/include/ani_request_permission_on_setting.h new file mode 100644 index 0000000000000000000000000000000000000000..5d73f58179d3378d0b2bff811cef19e706f70030 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/include/ani_request_permission_on_setting.h @@ -0,0 +1,96 @@ +/* + * 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 ANI_REQUEST_PERMISSION_ON_SETTING_H +#define ANI_REQUEST_PERMISSION_ON_SETTING_H + +#include "access_token.h" +#include "ani.h" +#include "ani_common.h" +#include "ani_error.h" +#include "ani_utils.h" +#ifdef EVENTHANDLER_ENABLE +#include "event_handler.h" +#include "event_queue.h" +#endif +#include "permission_grant_info.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +struct RequestPermOnSettingAsyncContext { + virtual ~RequestPermOnSettingAsyncContext(); + AccessTokenID tokenId = 0; + AtmResult result; + PermissionGrantInfo info; + + int32_t instanceId = -1; + bool isDynamic = true; + std::vector permissionList; + ani_object requestResult = nullptr; + std::vector stateList; + + std::shared_ptr abilityContext = nullptr; + std::shared_ptr uiExtensionContext = nullptr; + bool uiAbilityFlag = false; + bool releaseFlag = false; + std::mutex lockReleaseFlag; + +#ifdef EVENTHANDLER_ENABLE + std::shared_ptr handler_ = + std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif + std::thread::id threadId; + ani_vm* vm = nullptr; + ani_env* env = nullptr; + ani_object callback = nullptr; + ani_ref callbackRef = nullptr; +}; + +class RequestOnSettingAsyncInstanceControl { + public: + static void AddCallbackByInstanceId(std::shared_ptr& asyncContext); + static void ExecCallback(int32_t id); + static void CheckDynamicRequest( + std::shared_ptr& asyncContext, bool& isDynamic); + static void UpdateQueueData(const std::shared_ptr& asyncContext); + private: + static std::map>> instanceIdMap_; + static std::mutex instanceIdMutex_; +}; + +class PermissonOnSettingUICallback { +public: + explicit PermissonOnSettingUICallback(const std::shared_ptr& reqContext); + ~PermissonOnSettingUICallback(); + void SetSessionId(int32_t sessionId); + void ReleaseHandler(int32_t code); + void OnRelease(int32_t releaseCode); + void OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result); + void OnReceive(const OHOS::AAFwk::WantParams& request); + void OnError(int32_t code, const std::string& name, const std::string& message); + void OnRemoteReady(const std::shared_ptr& uiProxy); + void OnDestroy(); + +private: + std::shared_ptr reqContext_ = nullptr; + int32_t sessionId_ = 0; +}; + +void RequestPermissionOnSettingExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_object aniContext, ani_array_ref permissionList, ani_object callback); +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif /* ANI_REQUEST_PERMISSION_ON_SETTING_H */ diff --git a/frameworks/ets/ani/accesstoken/src/ani_ability_access_ctrl.cpp b/frameworks/ets/ani/accesstoken/src/ani_ability_access_ctrl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08dd8ab38e65e3834dd6bbd0f93a13c2621f4972 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/src/ani_ability_access_ctrl.cpp @@ -0,0 +1,888 @@ +/* + * 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_ability_access_ctrl.h" + +#include +#include +#include + +#include "access_token_error.h" +#include "accesstoken_kit.h" +#include "accesstoken_log.h" +#include "ani_request_global_switch_on_setting.h" +#include "ani_request_permission.h" +#include "ani_request_permission_on_setting.h" +#include "hisysevent.h" +#include "parameter.h" +#include "permission_list_state.h" +#include "permission_map.h" +#include "token_setproc.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniAbilityAccessCtrl" }; +constexpr int32_t VALUE_MAX_LEN = 32; +std::mutex g_lockCache; +static PermissionParamCache g_paramCache; +std::map g_cache; +static constexpr const char* PERMISSION_STATUS_CHANGE_KEY = "accesstoken.permission.change"; +} +constexpr const char* PERM_STATE_CHANGE_FIELD_TOKEN_ID = "tokenID"; +constexpr const char* PERM_STATE_CHANGE_FIELD_PERMISSION_NAME = "permissionName"; +constexpr const char* PERM_STATE_CHANGE_FIELD_CHANGE = "change"; +std::mutex g_lockForPermStateChangeRegisters; +std::vector g_permStateChangeRegisters; +static const char* REGISTER_PERMISSION_STATE_CHANGE_TYPE = "permissionStateChange"; +static const char* REGISTER_SELF_PERMISSION_STATE_CHANGE_TYPE = "selfPermissionStateChange"; + +RegisterPermStateChangeScopePtr::RegisterPermStateChangeScopePtr(const PermStateChangeScope& subscribeInfo) + : PermStateChangeCallbackCustomize(subscribeInfo) +{} + +RegisterPermStateChangeScopePtr::~RegisterPermStateChangeScopePtr() +{ + if (vm_ == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "vm is nullptr;"); + return; + } + bool isSameThread = (threadId_ == std::this_thread::get_id()); + ani_env* env = isSameThread ? env_ : GetCurrentEnv(vm_); + + if (ref_ != nullptr) { + env->GlobalReference_Delete(ref_); + ref_ = nullptr; + } + + if (!isSameThread) { + vm_->DetachCurrentThread(); + } +} + +void RegisterPermStateChangeScopePtr::SetEnv(ani_env* env) +{ + env_ = env; +} + +void RegisterPermStateChangeScopePtr::SetCallbackRef(const ani_ref& ref) +{ + ref_ = ref; +} + +void RegisterPermStateChangeScopePtr::SetValid(bool valid) +{ + std::lock_guard lock(validMutex_); + valid_ = valid; +} + +void RegisterPermStateChangeScopePtr::SetVm(ani_vm* vm) +{ + vm_ = vm; +} + +void RegisterPermStateChangeScopePtr::SetThreadId(const std::thread::id threadId) +{ + threadId_ = threadId; +} + +static bool SetStringProperty(ani_env* env, ani_object& aniObject, const char* propertyName, const std::string in) +{ + ani_string aniString = CreateAniString(env, in); + if (env->Object_SetPropertyByName_Ref(aniObject, propertyName, aniString) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetPropertyByName_Ref failed!"); + return false; + } + + return true; +} + +static void transPermStateChangeTypeToAniInt(const int32_t permStateChangeType, ani_size& index) +{ + index = static_cast(permStateChangeType); + return; +} + +static void ConvertPermStateChangeInfo(ani_env* env, const PermStateChangeInfo& result, ani_object& aniObject) +{ + // class implements from interface should use property, independent class use field + aniObject = CreateClassObject(env, "L@ohos/abilityAccessCtrl/abilityAccessCtrl/PermissionStateChangeInfoInner;"); + if (aniObject == nullptr) { + return; + } + + // set tokenId: int32_t + SetIntProperty(env, aniObject, PERM_STATE_CHANGE_FIELD_TOKEN_ID, static_cast(result.tokenID)); + + // set permissionName: Permissions + SetStringProperty(env, aniObject, PERM_STATE_CHANGE_FIELD_PERMISSION_NAME, result.permissionName); + + // set permStateChangeType: int32_t + ani_size index; + transPermStateChangeTypeToAniInt(result.permStateChangeType, index); + const char* activeStatusDes = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/PermissionStateChangeType;"; + SetEnumProperty( + env, aniObject, activeStatusDes, PERM_STATE_CHANGE_FIELD_CHANGE, static_cast(index)); +} + +void RegisterPermStateChangeScopePtr::PermStateChangeCallback(PermStateChangeInfo& PermStateChangeInfo) +{ + 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; + ConvertPermStateChangeInfo(env, PermStateChangeInfo, 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 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[VALUE_MAX_LEN] = { 0 }; + auto ret = GetParameterValue(g_paramCache.handle, value, 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->grantStatus = + AccessToken::AccessTokenKit::VerifyAccessToken(asyncContext->tokenId, asyncContext->permissionName); + iter->second.status = asyncContext->grantStatus; + iter->second.paramValue = currPara; + ACCESSTOKEN_LOG_DEBUG(LABEL, "Param changed currPara %{public}s", currPara.c_str()); + } else { + asyncContext->grantStatus = iter->second.status; + } + } else { + asyncContext->grantStatus = + AccessToken::AccessTokenKit::VerifyAccessToken(asyncContext->tokenId, asyncContext->permissionName); + g_cache[asyncContext->permissionName].status = asyncContext->grantStatus; + 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 CheckAccessTokenExecute([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_int aniTokenID, ani_string aniPermission) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return AccessToken::PermissionState::PERMISSION_DENIED; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permissionName = ParseAniString(env, static_cast(aniPermission)); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) is invalid.", + tokenID, permissionName.c_str()); + 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 = permissionName; + static uint64_t selfTokenId = GetSelfTokenID(); + if (asyncContext->tokenId != static_cast(selfTokenId)) { + asyncContext->grantStatus = AccessToken::AccessTokenKit::VerifyAccessToken(tokenID, permissionName); + return static_cast(asyncContext->grantStatus); + } + UpdatePermissionCache(asyncContext); + ACCESSTOKEN_LOG_INFO(LABEL, "CheckAccessTokenExecute result : %{public}d", asyncContext->grantStatus); + return static_cast(asyncContext->grantStatus); +} + +static void GrantUserGrantedPermissionExecute([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, + ani_int aniTokenID, ani_string aniPermission, ani_int aniFlags) +{ + if (env == nullptr) { + return; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permissionName = ParseAniString(env, static_cast(aniPermission)); + uint32_t permissionFlags = static_cast(aniFlags); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName)) || + (!BusinessErrorAni::ValidatePermissionFlagWithThrowError(env, permissionFlags))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) or flags(%{public}u)is invalid.", + tokenID, permissionName.c_str(), permissionFlags); + return; + } + + PermissionBriefDef def; + if (!GetPermissionBriefDef(permissionName, def) || def.grantMode != USER_GRANT) { + std::string errMsg = GetErrorMessage(STS_ERROR_PERMISSION_NOT_EXIST, + "The specified permission does not exist or is not a user_grant permission."); + BusinessErrorAni::ThrowError(env, STS_ERROR_PERMISSION_NOT_EXIST, errMsg); + return; + } + + int32_t res = AccessTokenKit::GrantPermission(tokenID, permissionName, permissionFlags); + if (res != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(res); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + } +} + +static void RevokeUserGrantedPermissionExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_int aniTokenID, ani_string aniPermission, ani_int aniFlags) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "RevokeUserGrantedPermission begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env null."); + return; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permissionName = ParseAniString(env, static_cast(aniPermission)); + uint32_t permissionFlags = static_cast(aniFlags); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName)) || + (!BusinessErrorAni::ValidatePermissionFlagWithThrowError(env, permissionFlags))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) or flags(%{public}u)is invalid.", + tokenID, permissionName.c_str(), permissionFlags); + return; + } + + PermissionBriefDef def; + if (!GetPermissionBriefDef(permissionName, def) || def.grantMode != USER_GRANT) { + std::string errMsg = GetErrorMessage(STS_ERROR_PERMISSION_NOT_EXIST, + "The specified permission does not exist or is not a user_grant permission."); + BusinessErrorAni::ThrowError(env, STS_ERROR_PERMISSION_NOT_EXIST, errMsg); + return; + } + + int32_t ret = AccessTokenKit::RevokePermission(tokenID, permissionName, permissionFlags); + if (ret != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(ret); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + } +} + +static ani_int GetVersionExecute([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "getVersionExecute begin."); + uint32_t version = -1; + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env null"); + return static_cast(version); + } + + int32_t result = AccessTokenKit::GetVersion(version); + if (result != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return static_cast(version); + } + return static_cast(version); +} + +static ani_ref GetPermissionsStatusExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_int aniTokenID, ani_array_ref aniPermissionList) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "GetPermissionsStatusExecute begin."); + if ((env == nullptr) || (aniPermissionList == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "aniPermissionList or env null."); + return nullptr; + } + AccessTokenID tokenID = static_cast(aniTokenID); + if (!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) is invalid.", tokenID); + return nullptr; + } + std::vector permissionList = ParseAniStringVector(env, aniPermissionList); + if (permissionList.empty()) { + BusinessErrorAni::ThrowError( + env, STS_ERROR_INNER, GetErrorMessage(STS_ERROR_INNER, "The permissionList is empty.")); + return nullptr; + } + + std::vector permList; + for (const auto& permission : permissionList) { + PermissionListState permState; + permState.permissionName = permission; + permState.state = INVALID_OPER; + permList.emplace_back(permState); + } + + int32_t result = RET_SUCCESS; + result = AccessTokenKit::GetPermissionsStatus(tokenID, permList); + if (result != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return nullptr; + } + std::vector permissionQueryResults; + for (const auto& permState : permList) { + permissionQueryResults.emplace_back(permState.state); + } + + return CreateAniArrayInt(env, permissionQueryResults); +} + +static ani_int GetPermissionFlagsExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_int aniTokenID, ani_string aniPermissionName) +{ + uint32_t flag = PERMISSION_DEFAULT_FLAG; + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null"); + return flag; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permissionName = ParseAniString(env, static_cast(aniPermissionName)); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) is invalid.", + tokenID, permissionName.c_str()); + return flag; + } + + int32_t result = AccessTokenKit::GetPermissionFlag(tokenID, permissionName, flag); + if (result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "result = %{public}d errcode = %{public}d", + result, BusinessErrorAni::GetStsErrorCode(result)); + BusinessErrorAni::ThrowError( + env, BusinessErrorAni::GetStsErrorCode(result), GetErrorMessage(BusinessErrorAni::GetStsErrorCode(result))); + } + return flag; +} + +static void SetPermissionRequestToggleStatusExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_string aniPermissionName, ani_int status) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null"); + return; + } + std::string permissionName = ParseAniString(env, static_cast(aniPermissionName)); + if (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Permission(%{public}s) is invalid.", permissionName.c_str()); + return; + } + int32_t result = AccessTokenKit::SetPermissionRequestToggleStatus(permissionName, status, 0); + if (result != RET_SUCCESS) { + BusinessErrorAni::ThrowError( + env, BusinessErrorAni::GetStsErrorCode(result), GetErrorMessage(BusinessErrorAni::GetStsErrorCode(result))); + } + return; +} + +static ani_int GetPermissionRequestToggleStatusExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_string aniPermissionName) +{ + uint32_t flag = CLOSED; + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null"); + return flag; + } + std::string permissionName = ParseAniString(env, static_cast(aniPermissionName)); + if (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permissionName)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Permission(%{public}s) is invalid.", permissionName.c_str()); + return flag; + } + + int32_t result = AccessTokenKit::GetPermissionRequestToggleStatus(permissionName, flag, 0); + if (result != RET_SUCCESS) { + BusinessErrorAni::ThrowError( + env, BusinessErrorAni::GetStsErrorCode(result), GetErrorMessage(BusinessErrorAni::GetStsErrorCode(result))); + } + return flag; +} + +static void RequestAppPermOnSettingExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_int tokenID) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null."); + return; + } + + int32_t result = AccessTokenKit::RequestAppPermOnSetting(static_cast(tokenID)); + if (result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Result = %{public}d, errcode = %{public}d.", + result, BusinessErrorAni::GetStsErrorCode(result)); + BusinessErrorAni::ThrowError(env, BusinessErrorAni::GetStsErrorCode(result), + GetErrorMessage(BusinessErrorAni::GetStsErrorCode(result))); + } +} + +static bool SetupPermissionSubscriber( + RegisterPermStateChangeInf* context, const PermStateChangeScope& scopeInfo, const ani_ref& callback) +{ + ani_vm* vm; + if (context->env->GetVM(&vm) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetVM failed!"); + return false; + } + + auto sortedTokenIDs = scopeInfo.tokenIDs; + auto sortedPermList = scopeInfo.permList; + std::sort(sortedTokenIDs.begin(), sortedTokenIDs.end()); + std::sort(sortedPermList.begin(), sortedPermList.end()); + + context->callbackRef = callback; + context->scopeInfo.tokenIDs = sortedTokenIDs; + context->scopeInfo.permList = sortedPermList; + context->subscriber = std::make_shared(scopeInfo); + context->subscriber->SetVm(vm); + context->subscriber->SetEnv(context->env); + context->subscriber->SetCallbackRef(callback); + + return true; +} + +static bool ParseInputToRegister(const ani_string& aniType, const ani_array_ref& aniId, const ani_array_ref& aniArray, + const ani_ref& aniCallback, RegisterPermStateChangeInf* context, bool isReg) +{ + std::string type = ParseAniString(context->env, aniType); + if (type.empty()) { + BusinessErrorAni::ThrowError(context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg( + "type", "permissionStateChange or selfPermissionStateChange")); + return false; + } + if ((type != REGISTER_SELF_PERMISSION_STATE_CHANGE_TYPE) && (type != REGISTER_PERMISSION_STATE_CHANGE_TYPE)) { + BusinessErrorAni::ThrowError( + context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg("type", "type is invalid")); + return false; + } + + context->permStateChangeType = type; + context->threadId = std::this_thread::get_id(); + + PermStateChangeScope scopeInfo; + std::string errMsg; + if (type == REGISTER_PERMISSION_STATE_CHANGE_TYPE) { + if (!AniParseAccessTokenIDArray(context->env, aniId, scopeInfo.tokenIDs)) { + BusinessErrorAni::ThrowError( + context->env, STS_ERROR_PARAM_INVALID, GetParamErrorMsg("tokenIDList", "Array")); + return false; + } + } else if (type == REGISTER_SELF_PERMISSION_STATE_CHANGE_TYPE) { + scopeInfo.tokenIDs = {GetSelfTokenID()}; + } + scopeInfo.permList = ParseAniStringVector(context->env, aniArray); + bool hasCallback = true; + if (!isReg) { + hasCallback = !(AniIsRefUndefined(context->env, aniCallback)); + } + + ani_ref callback = nullptr; + if (hasCallback) { + if (!AniParseCallback(context->env, aniCallback, callback)) { + BusinessErrorAni::ThrowError(context->env, STS_ERROR_PARAM_ILLEGAL, GetParamErrorMsg( + "callback", "Callback")); + return false; + } + } + + return SetupPermissionSubscriber(context, scopeInfo, callback); +} + +static bool IsExistRegister(const RegisterPermStateChangeInf* context) +{ + PermStateChangeScope targetScopeInfo; + context->subscriber->GetScope(targetScopeInfo); + std::vector targetTokenIDs = targetScopeInfo.tokenIDs; + std::vector targetPermList = targetScopeInfo.permList; + std::lock_guard lock(g_lockForPermStateChangeRegisters); + + for (const auto& item : g_permStateChangeRegisters) { + PermStateChangeScope scopeInfo; + item->subscriber->GetScope(scopeInfo); + + bool hasPermIntersection = false; + // Special cases: + // 1.Have registered full, and then register some + // 2.Have registered some, then register full + if (scopeInfo.permList.empty() || targetPermList.empty()) { + hasPermIntersection = true; + } + for (const auto& PermItem : targetPermList) { + if (hasPermIntersection) { + break; + } + auto iter = std::find(scopeInfo.permList.begin(), scopeInfo.permList.end(), PermItem); + if (iter != scopeInfo.permList.end()) { + hasPermIntersection = true; + } + } + + bool hasTokenIdIntersection = false; + + if (scopeInfo.tokenIDs.empty() || targetTokenIDs.empty()) { + hasTokenIdIntersection = true; + } + for (const auto& tokenItem : targetTokenIDs) { + if (hasTokenIdIntersection) { + break; + } + auto iter = std::find(scopeInfo.tokenIDs.begin(), scopeInfo.tokenIDs.end(), tokenItem); + if (iter != scopeInfo.tokenIDs.end()) { + hasTokenIdIntersection = true; + } + } + bool isEqual = true; + if (!AniIsCallbackRefEqual(context->env, item->callbackRef, context->callbackRef, item->threadId, isEqual)) { + return true; + } + if (hasPermIntersection && hasTokenIdIntersection && isEqual) { + return true; + } + } + return false; +} + +static void RegisterPermStateChangeCallback([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_string aniType, ani_array_ref aniId, ani_array_ref aniArray, ani_ref callback) +{ + if (env == nullptr) { + BusinessErrorAni::ThrowError(env, STS_ERROR_INNER, GetErrorMessage(STSErrorCode::STS_ERROR_INNER)); + return; + } + + RegisterPermStateChangeInf* registerPermStateChangeInf = new (std::nothrow) RegisterPermStateChangeInf(); + if (registerPermStateChangeInf == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to allocate memory for RegisterPermStateChangeInf!"); + return; + } + registerPermStateChangeInf->env = env; + std::unique_ptr callbackPtr {registerPermStateChangeInf}; + if (!ParseInputToRegister(aniType, aniId, aniArray, callback, registerPermStateChangeInf, true)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ParseInputToRegister false."); + return; + } + + if (IsExistRegister(registerPermStateChangeInf)) { + ACCESSTOKEN_LOG_ERROR(LABEL, + "Subscribe failed. The current subscriber has existed or Reference_StrictEquals failed!"); + if (registerPermStateChangeInf->permStateChangeType == REGISTER_SELF_PERMISSION_STATE_CHANGE_TYPE) { + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_NOT_USE_TOGETHER, + GetErrorMessage(STSErrorCode::STS_ERROR_NOT_USE_TOGETHER)); + } else { + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_PARAM_INVALID, GetErrorMessage(STSErrorCode::STS_ERROR_PARAM_INVALID)); + } + return; + } + + int32_t result; + if (registerPermStateChangeInf->permStateChangeType == REGISTER_PERMISSION_STATE_CHANGE_TYPE) { + result = AccessTokenKit::RegisterPermStateChangeCallback(registerPermStateChangeInf->subscriber); + } else { + result = AccessTokenKit::RegisterSelfPermStateChangeCallback(registerPermStateChangeInf->subscriber); + } + if (result != RET_SUCCESS) { + ACCESSTOKEN_LOG_ERROR(LABEL, "RegisterPermStateChangeCallback failed"); + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return; + } + + { + std::lock_guard lock(g_lockForPermStateChangeRegisters); + g_permStateChangeRegisters.emplace_back(registerPermStateChangeInf); + ACCESSTOKEN_LOG_INFO( + LABEL, "Add g_PermStateChangeRegisters.size = %{public}zu", g_permStateChangeRegisters.size()); + } + callbackPtr.release(); + return; +} + +static bool FindAndGetSubscriberInVector(RegisterPermStateChangeInf* unregisterPermStateChangeInf, + std::vector& batchPermStateChangeRegisters) +{ + ACCESSTOKEN_LOG_DEBUG(LABEL, "FindAndGetSubscriberInVector In."); + std::lock_guard lock(g_lockForPermStateChangeRegisters); + std::vector targetTokenIDs = unregisterPermStateChangeInf->scopeInfo.tokenIDs; + std::vector targetPermList = unregisterPermStateChangeInf->scopeInfo.permList; + bool callbackEqual; + ani_ref callbackRef = unregisterPermStateChangeInf->callbackRef; + bool isUndef = AniIsRefUndefined(unregisterPermStateChangeInf->env, unregisterPermStateChangeInf->callbackRef); + + for (const auto& item : g_permStateChangeRegisters) { + 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(unregisterPermStateChangeInf->env, callbackRef, + unregisterPermStateChangeInf->callbackRef, item->threadId, callbackEqual)) { + continue; + } + } + + PermStateChangeScope scopeInfo; + item->subscriber->GetScope(scopeInfo); + if (scopeInfo.tokenIDs == targetTokenIDs && scopeInfo.permList == targetPermList) { + unregisterPermStateChangeInf->subscriber = item->subscriber; + batchPermStateChangeRegisters.emplace_back(item); + } + } + if (!batchPermStateChangeRegisters.empty()) { + return true; + } + return false; +} + +static void DeleteRegisterFromVector(const RegisterPermStateChangeInf* context) +{ + std::vector targetTokenIDs = context->scopeInfo.tokenIDs; + std::vector targetPermList = context->scopeInfo.permList; + std::lock_guard lock(g_lockForPermStateChangeRegisters); + auto item = g_permStateChangeRegisters.begin(); + while (item != g_permStateChangeRegisters.end()) { + PermStateChangeScope stateChangeScope; + (*item)->subscriber->GetScope(stateChangeScope); + bool callbackEqual = true; + if (!AniIsCallbackRefEqual( + context->env, (*item)->callbackRef, context->callbackRef, (*item)->threadId, callbackEqual)) { + continue; + } + if (!callbackEqual) { + continue; + } + + if ((stateChangeScope.tokenIDs == targetTokenIDs) && (stateChangeScope.permList == targetPermList)) { + delete *item; + *item = nullptr; + g_permStateChangeRegisters.erase(item); + break; + } else { + ++item; + } + } +} + +static void UnregisterPermStateChangeCallback([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_string aniType, ani_array_ref aniId, ani_array_ref aniArray, ani_ref callback) +{ + ACCESSTOKEN_LOG_DEBUG(LABEL, "UnregisterPermStateChangeCallback In."); + if (env == nullptr) { + BusinessErrorAni::ThrowError(env, STS_ERROR_INNER, GetErrorMessage(STSErrorCode::STS_ERROR_INNER)); + return; + } + + RegisterPermStateChangeInf* context = new (std::nothrow) RegisterPermStateChangeInf(); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to allocate memory for UnRegisterPermStateChangeInf!"); + return; + } + context->env = env; + std::unique_ptr callbackPtr {context}; + + if (!ParseInputToRegister(aniType, aniId, aniArray, callback, context, false)) { + return; + } + + std::vector batchPermStateChangeRegisters; + if (!FindAndGetSubscriberInVector(context, batchPermStateChangeRegisters)) { + ACCESSTOKEN_LOG_ERROR(LABEL, + "Unsubscribe failed. The current subscriber does not exist or Reference_StrictEquals failed!"); + if (context->permStateChangeType == REGISTER_SELF_PERMISSION_STATE_CHANGE_TYPE) { + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_NOT_USE_TOGETHER, + GetErrorMessage(STSErrorCode::STS_ERROR_NOT_USE_TOGETHER)); + } else { + BusinessErrorAni::ThrowError( + env, STSErrorCode::STS_ERROR_PARAM_INVALID, GetErrorMessage(STSErrorCode::STS_ERROR_PARAM_INVALID)); + } + return; + } + for (const auto& item : batchPermStateChangeRegisters) { + PermStateChangeScope scopeInfo; + item->subscriber->GetScope(scopeInfo); + int32_t result; + if (context->permStateChangeType == REGISTER_PERMISSION_STATE_CHANGE_TYPE) { + result = AccessTokenKit::UnRegisterPermStateChangeCallback(item->subscriber); + } else { + result = AccessTokenKit::UnRegisterSelfPermStateChangeCallback(item->subscriber); + } + if (result == RET_SUCCESS) { + DeleteRegisterFromVector(item); + } else { + ACCESSTOKEN_LOG_ERROR(LABEL, "UnregisterPermStateChangeCallback failed"); + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return; + } + } + return; +} + +void InitAbilityCtrlFunction(ani_env *env) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); + return; + } + 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; + } + 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; + }; + 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; + } + std::array claMethods = { + ani_native_function { "checkAccessTokenExecute", nullptr, reinterpret_cast(CheckAccessTokenExecute) }, + ani_native_function { "requestPermissionsFromUserExecute", + nullptr, reinterpret_cast(RequestPermissionsFromUserExecute) }, + ani_native_function { "requestPermissionOnSettingExecute", + nullptr, reinterpret_cast(RequestPermissionOnSettingExecute) }, + ani_native_function {"requestGlobalSwitchExecute", + nullptr, reinterpret_cast(RequestGlobalSwitchExecute) }, + ani_native_function { "grantUserGrantedPermissionExecute", nullptr, + reinterpret_cast(GrantUserGrantedPermissionExecute) }, + ani_native_function { "revokeUserGrantedPermissionExecute", + nullptr, reinterpret_cast(RevokeUserGrantedPermissionExecute) }, + ani_native_function { "getVersionExecute", nullptr, reinterpret_cast(GetVersionExecute) }, + ani_native_function { "getPermissionsStatusExecute", + nullptr, reinterpret_cast(GetPermissionsStatusExecute) }, + ani_native_function{ "getPermissionFlagsExecute", + nullptr, reinterpret_cast(GetPermissionFlagsExecute) }, + ani_native_function{ "setPermissionRequestToggleStatusExecute", + nullptr, reinterpret_cast(SetPermissionRequestToggleStatusExecute) }, + ani_native_function{ "getPermissionRequestToggleStatusExecute", + nullptr, reinterpret_cast(GetPermissionRequestToggleStatusExecute) }, + ani_native_function{ "requestAppPermOnSettingExecute", + nullptr, reinterpret_cast(RequestAppPermOnSettingExecute) }, + ani_native_function { "onExcute", nullptr, reinterpret_cast(RegisterPermStateChangeCallback) }, + ani_native_function { "offExcute", nullptr, reinterpret_cast(UnregisterPermStateChangeCallback) }, + }; + if (ANI_OK != env->Class_BindNativeMethods(cls, claMethods.data(), claMethods.size())) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Cannot bind native methods to %{public}s", className); + return; + }; +} +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; + } + InitAbilityCtrlFunction(env); + *result = ANI_VERSION_1; + return ANI_OK; +} +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS diff --git a/frameworks/ets/ani/accesstoken/src/ani_common.cpp b/frameworks/ets/ani/accesstoken/src/ani_common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33de4a56bc8d3d9936d15b01cce15f174118e0a7 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/src/ani_common.cpp @@ -0,0 +1,74 @@ +/* + * 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_common.h" +#include "accesstoken_log.h" +#include +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniAccessTokenCommon" }; +constexpr const char* WRAPPER_CLASS_NAME = "L@ohos/abilityAccessCtrl/AsyncCallbackWrapper;"; +constexpr const char* INVOKE_METHOD_NAME = "invoke"; +} // namespace + +bool ExecuteAsyncCallback(ani_env* env, ani_object callback, ani_object error, ani_object result) +{ + if (env == nullptr || callback == nullptr || error == nullptr || result == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid paramter."); + return false; + } + ani_status status = ANI_ERROR; + ani_class clsCall {}; + + if ((status = env->FindClass(WRAPPER_CLASS_NAME, &clsCall)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass failed, error=%{public}d.", static_cast(status)); + return false; + } + ani_method method = {}; + if ((status = env->Class_FindMethod( + clsCall, INVOKE_METHOD_NAME, "L@ohos/base/BusinessError;Lstd/core/Object;:V", &method)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindMethod failed, error=%{public}d.", static_cast(status)); + return false; + } + + status = env->Object_CallMethod_Void(static_cast(callback), method, error, result); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_CallMethod_Void failed, error=%{public}d.", static_cast(status)); + return false; + } + return true; +} + +OHOS::Ace::UIContent* GetUIContent(const std::shared_ptr& abilityContext, + std::shared_ptr& uiExtensionContext, bool uiAbilityFlag) +{ + OHOS::Ace::UIContent* uiContent = nullptr; + if (uiAbilityFlag) { + if (abilityContext == nullptr) { + return nullptr; + } + uiContent = abilityContext->GetUIContent(); + } else { + if (uiExtensionContext == nullptr) { + return nullptr; + } + uiContent = uiExtensionContext->GetUIContent(); + } + return uiContent; +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/src/ani_request_global_switch_on_setting.cpp b/frameworks/ets/ani/accesstoken/src/ani_request_global_switch_on_setting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..02ced1e1890ef6be7c7a80020b9958457dbe6403 --- /dev/null +++ b/frameworks/ets/ani/accesstoken/src/ani_request_global_switch_on_setting.cpp @@ -0,0 +1,511 @@ +/* + * 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_request_global_switch_on_setting.h" +#include "accesstoken_kit.h" +#include "accesstoken_log.h" +#include "token_setproc.h" +#include "want.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +std::map>> + RequestGlobalSwitchAsyncInstanceControl::instanceIdMap_; +std::mutex RequestGlobalSwitchAsyncInstanceControl::instanceIdMutex_; +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { + LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniRequestGlobalSwitch" }; +const std::string GLOBAL_SWITCH_KEY = "ohos.user.setting.global_switch"; +const std::string GLOBAL_SWITCH_RESULT_KEY = "ohos.user.setting.global_switch.result"; +const std::string RESULT_ERROR_KEY = "ohos.user.setting.error_code"; +const std::string EXTENSION_TYPE_KEY = "ability.want.params.uiExtensionType"; +const std::string UI_EXTENSION_TYPE = "sys/commonUI"; + +// error code from dialog +constexpr int32_t REQUEST_REALDY_EXIST = 1; +constexpr int32_t GLOBAL_TYPE_IS_NOT_SUPPORT = 2; +constexpr int32_t SWITCH_IS_ALREADY_OPEN = 3; +} +RequestGlobalSwitchAsyncContext::~RequestGlobalSwitchAsyncContext() +{ + if (vm == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "VM is nullptr."); + return; + } + bool isSameThread = IsCurrentThread(threadId); + ani_env* curEnv = isSameThread ? env : GetCurrentEnv(vm); + if (curEnv == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } + + if (callbackRef != nullptr) { + curEnv->GlobalReference_Delete(callbackRef); + callbackRef = nullptr; + } +} + +static ani_status GetContext( + ani_env* env, const ani_object& aniContext, std::shared_ptr& asyncContext) +{ + auto context = OHOS::AbilityRuntime::GetStageModeContext(env, aniContext); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetStageModeContext failed"); + return ANI_ERROR; + } + asyncContext->abilityContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->abilityContext != nullptr) { + auto abilityInfo = asyncContext->abilityContext->GetApplicationInfo(); + if (abilityInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->uiAbilityFlag = true; + asyncContext->tokenId = abilityInfo->accessTokenId; + } else { + asyncContext->uiExtensionContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->uiExtensionContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertTo UIExtensionContext failed"); + return ANI_ERROR; + } + auto uiExtensionInfo = asyncContext->uiExtensionContext->GetApplicationInfo(); + if (uiExtensionInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->tokenId = uiExtensionInfo->accessTokenId; + } + return ANI_OK; +} + +static bool ParseRequestGlobalSwitch(ani_env* env, ani_object& aniContext, ani_int type, + ani_object callback, std::shared_ptr& asyncContext) +{ + ani_vm* vm; + ani_status status = env->GetVM(&vm); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetVM failed, error=%{public}d.", static_cast(status)); + return false; + } + asyncContext->vm = vm; + asyncContext->env = env; + asyncContext->callback = callback; + asyncContext->threadId = std::this_thread::get_id(); + asyncContext->switchType = static_cast(type); + + if (GetContext(env, aniContext, asyncContext) != ANI_OK) { + BusinessErrorAni::ThrowParameterTypeError(env, STS_ERROR_PARAM_ILLEGAL, + GetParamErrorMsg("context", "UIAbility or UIExtension Context")); + return false; + } + if (!AniParseCallback(env, reinterpret_cast(callback), asyncContext->callbackRef)) { + return false; + } +#ifdef EVENTHANDLER_ENABLE + asyncContext->handler_ = std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif + return true; +} + +static int32_t TransferToStsErrorCode(int32_t errCode) +{ + int32_t stsCode = STS_OK; + switch (errCode) { + case RET_SUCCESS: + stsCode = STS_OK; + break; + case REQUEST_REALDY_EXIST: + stsCode = STS_ERROR_REQUEST_IS_ALREADY_EXIST; + break; + case GLOBAL_TYPE_IS_NOT_SUPPORT: + stsCode = STS_ERROR_PARAM_INVALID; + break; + case SWITCH_IS_ALREADY_OPEN: + stsCode = STS_ERROR_GLOBAL_SWITCH_IS_ALREADY_OPEN; + break; + default: + stsCode = STS_ERROR_INNER; + break; + } + ACCESSTOKEN_LOG_INFO(LABEL, "dialog error(%{public}d) stsCode(%{public}d).", errCode, stsCode); + return stsCode; +} + +SwitchOnSettingUICallback::SwitchOnSettingUICallback( + const std::shared_ptr& reqContext) +{ + this->reqContext_ = reqContext; +} + +SwitchOnSettingUICallback::~SwitchOnSettingUICallback() +{} + +void SwitchOnSettingUICallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_ = sessionId; +} + +static void GlobalSwitchResultsCallbackUI(bool switchStatus, std::shared_ptr& data) +{ + bool isSameThread = IsCurrentThread(data->threadId); + ani_env* env = isSameThread ? data->env : GetCurrentEnv(data->vm); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } + + int32_t stsCode = TransferToStsErrorCode(data->result.errorCode); + ani_object error = BusinessErrorAni::CreateError(env, stsCode, GetErrorMessage(stsCode, data->result.errorMsg)); + ExecuteAsyncCallback( + env, reinterpret_cast(data->callbackRef), error, CreateBooleanObject(env, data->switchStatus)); + + if (!isSameThread && data->vm->DetachCurrentThread() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "DetachCurrentThread failed!"); + } +} + +static void CloseModalUIExtensionMainThread(std::shared_ptr& asyncContext, + int32_t sessionId) +{ + auto task = [asyncContext, sessionId]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + asyncContext->result.errorCode = RET_FAILED; + return; + } + uiContent->CloseModalUIExtension(sessionId); + ACCESSTOKEN_LOG_INFO(LABEL, "Close end, sessionId: %{public}d", sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CloseModalUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +void SwitchOnSettingUICallback::ReleaseHandler(int32_t code) +{ + { + std::lock_guard lock(this->reqContext_->lockReleaseFlag); + if (this->reqContext_->releaseFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "Callback has executed."); + return; + } + this->reqContext_->releaseFlag = true; + } + CloseModalUIExtensionMainThread(this->reqContext_, this->sessionId_); + if (code == -1) { + this->reqContext_->result.errorCode = code; + } + RequestGlobalSwitchAsyncInstanceControl::UpdateQueueData(this->reqContext_); + RequestGlobalSwitchAsyncInstanceControl::ExecCallback(this->reqContext_->instanceId); + GlobalSwitchResultsCallbackUI(this->reqContext_->switchStatus, this->reqContext_); +} + +/* + * when UIExtensionAbility use terminateSelfWithResult + */ +void SwitchOnSettingUICallback::OnResult(int32_t resultCode, const AAFwk::Want& result) +{ + this->reqContext_->result.errorCode = result.GetIntParam(RESULT_ERROR_KEY, 0); + this->reqContext_->switchStatus = result.GetBoolParam(GLOBAL_SWITCH_RESULT_KEY, 0); + ACCESSTOKEN_LOG_INFO(LABEL, "ResultCode is %{public}d, errorCodeis %{public}d, switchStatus=%{public}d.", + resultCode, this->reqContext_->result.errorCode, this->reqContext_->switchStatus); + ReleaseHandler(0); +} + +/* + * when UIExtensionAbility send message to UIExtensionComponent + */ +void SwitchOnSettingUICallback::OnReceive(const AAFwk::WantParams& receive) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Called!"); +} + +/* + * when UIExtensionAbility disconnect or use terminate or process die + * releaseCode is 0 when process normal exit + */ +void SwitchOnSettingUICallback::OnRelease(int32_t releaseCode) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseCode is %{public}d", releaseCode); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error + */ +void SwitchOnSettingUICallback::OnError(int32_t code, const std::string& name, const std::string& message) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Code is %{public}d, name is %{public}s, message is %{public}s", + code, name.c_str(), message.c_str()); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init, + * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy + */ +void SwitchOnSettingUICallback::OnRemoteReady(const std::shared_ptr& uiProxy) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Connect to UIExtensionAbility successfully."); +} + +/* + * when UIExtensionComponent destructed + */ +void SwitchOnSettingUICallback::OnDestroy() +{ + ACCESSTOKEN_LOG_INFO(LABEL, "UIExtensionAbility destructed."); + ReleaseHandler(-1); +} + +static void CreateUIExtensionMainThread(std::shared_ptr& asyncContext, + const AAFwk::Want& want, const Ace::ModalUIExtensionCallbacks& uiExtensionCallbacks, + const std::shared_ptr& uiExtCallback) +{ + auto task = [asyncContext, want, uiExtensionCallbacks, uiExtCallback]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to get ui content!"); + asyncContext->result.errorCode = RET_FAILED; + return; + } + + Ace::ModalUIExtensionConfig config; + config.isProhibitBack = true; + int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config); + ACCESSTOKEN_LOG_INFO(LABEL, "Create end, sessionId: %{public}d, tokenId: %{public}d.", + sessionId, asyncContext->tokenId); + if (sessionId == 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to create component, sessionId is 0."); + asyncContext->result.errorCode = RET_FAILED; + return; + } + uiExtCallback->SetSessionId(sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CreateUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +static void CreateUIExtension( + const OHOS::AAFwk::Want &want, std::shared_ptr asyncContext) +{ + auto uiExtCallback = std::make_shared(asyncContext); + Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = { + [uiExtCallback](int32_t releaseCode) { + uiExtCallback->OnRelease(releaseCode); + }, + [uiExtCallback](int32_t resultCode, const AAFwk::Want& result) { + uiExtCallback->OnResult(resultCode, result); + }, + [uiExtCallback](const AAFwk::WantParams& receive) { + uiExtCallback->OnReceive(receive); + }, + [uiExtCallback](int32_t code, const std::string& name, [[maybe_unused]] const std::string& message) { + uiExtCallback->OnError(code, name, name); + }, + [uiExtCallback](const std::shared_ptr& uiProxy) { + uiExtCallback->OnRemoteReady(uiProxy); + }, + [uiExtCallback]() { + uiExtCallback->OnDestroy(); + }, + }; + + CreateUIExtensionMainThread(asyncContext, want, uiExtensionCallbacks, uiExtCallback); +} + +static void StartUIExtension(std::shared_ptr& asyncContext) +{ + AccessTokenKit::GetPermissionManagerInfo(asyncContext->info); + ACCESSTOKEN_LOG_INFO(LABEL, "bundleName: %{public}s, permStateAbilityName: %{public}s.", + asyncContext->info.grantBundleName.c_str(), asyncContext->info.permStateAbilityName.c_str()); + OHOS::AAFwk::Want want; + want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.globalSwitchAbilityName); + want.SetParam(GLOBAL_SWITCH_KEY, asyncContext->switchType); + want.SetParam(EXTENSION_TYPE_KEY, UI_EXTENSION_TYPE); + CreateUIExtension(want, asyncContext); +} + +static void GetInstanceId(std::shared_ptr& asyncContext) +{ + auto task = [asyncContext]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + return; + } + asyncContext->instanceId = uiContent->GetInstanceId(); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:GetInstanceId"); + } else { + task(); + } +#else + task(); +#endif + ACCESSTOKEN_LOG_INFO(LABEL, "Instance id: %{public}d", asyncContext->instanceId); +} + +void RequestGlobalSwitchAsyncInstanceControl::AddCallbackByInstanceId( + std::shared_ptr& asyncContext) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "InstanceId: %{public}d", asyncContext->instanceId); + { + std::lock_guard lock(instanceIdMutex_); + auto iter = instanceIdMap_.find(asyncContext->instanceId); + // id is existed mean a pop window is showing, add context to waiting queue + if (iter != instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "InstanceId: %{public}d has existed.", asyncContext->instanceId); + instanceIdMap_[asyncContext->instanceId].emplace_back(asyncContext); + return; + } + // make sure id is in map to indicate a pop-up window is showing + instanceIdMap_[asyncContext->instanceId] = {}; + } + StartUIExtension(asyncContext); +} + +void RequestGlobalSwitchAsyncInstanceControl::UpdateQueueData( + const std::shared_ptr& reqContext) +{ + if ((reqContext->result.errorCode != RET_SUCCESS) || !(reqContext->switchStatus)) { + ACCESSTOKEN_LOG_INFO(LABEL, "The queue data does not need to be updated."); + return; + } + + { + std::lock_guard lock(instanceIdMutex_); + int32_t id = reqContext->instanceId; + auto iter = instanceIdMap_.find(id); + if (iter == instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d not existed.", id); + return; + } + int32_t targetSwitchType = reqContext->switchType; + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map size: %{public}zu.", id, iter->second.size()); + for (auto& asyncContext : iter->second) { + if (targetSwitchType == asyncContext->switchType) { + asyncContext->result.errorCode = reqContext->result.errorCode; + asyncContext->switchStatus = reqContext->switchStatus; + asyncContext->isDynamic = false; + } + } + } +} + +void RequestGlobalSwitchAsyncInstanceControl::ExecCallback(int32_t id) +{ + std::shared_ptr asyncContext = nullptr; + bool isDynamic = false; + { + std::lock_guard lock(instanceIdMutex_); + auto iter = instanceIdMap_.find(id); + if (iter == instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d not existed.", id); + return; + } + while (!iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map size: %{public}zu.", id, iter->second.size()); + asyncContext = iter->second[0]; + iter->second.erase(iter->second.begin()); + CheckDynamicRequest(asyncContext, isDynamic); + if (isDynamic) { + break; + } + } + if (iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map is empty", id); + instanceIdMap_.erase(id); + } + } + if (isDynamic) { + StartUIExtension(asyncContext); + } +} + +void RequestGlobalSwitchAsyncInstanceControl::CheckDynamicRequest( + std::shared_ptr& asyncContext, bool& isDynamic) +{ + isDynamic = asyncContext->isDynamic; + if (!isDynamic) { + ACCESSTOKEN_LOG_INFO(LABEL, "It does not need to request permission exsion"); + GlobalSwitchResultsCallbackUI(asyncContext->switchStatus, asyncContext); + return; + } +} + +void RequestGlobalSwitchExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_object aniContext, ani_int type, ani_object callback) +{ + if (env == nullptr || callback == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env or permissionList or callback is null."); + return; + } + + std::shared_ptr asyncContext = + std::make_shared(); + if (!ParseRequestGlobalSwitch(env, aniContext, type, callback, asyncContext)) { + return; + } + + ani_object result = CreateBooleanObject(env, false); + static AccessTokenID selfTokenID = static_cast(GetSelfTokenID()); + if (selfTokenID != asyncContext->tokenId) { + ACCESSTOKEN_LOG_ERROR(LABEL, "The context tokenID %{public}d is not same with selfTokenID %{public}d.", + asyncContext->tokenId, selfTokenID); + ani_object error = + BusinessErrorAni::CreateError(env, STS_ERROR_PARAM_INVALID, GetErrorMessage(STS_ERROR_PARAM_INVALID, + "The specified context does not belong to the current application.")); + ExecuteAsyncCallback(env, callback, error, result); + return; + } + GetInstanceId(asyncContext); + RequestGlobalSwitchAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); + ACCESSTOKEN_LOG_INFO(LABEL, "Start to pop ui extension dialog"); + + if (asyncContext->result.errorCode != RET_SUCCESS) { + int32_t stsCode = TransferToStsErrorCode(asyncContext->result.errorCode); + ani_object error = BusinessErrorAni::CreateError( + env, stsCode, GetErrorMessage(stsCode, asyncContext->result.errorMsg)); + ExecuteAsyncCallback(env, callback, error, result); + ACCESSTOKEN_LOG_WARN(LABEL, "Failed to pop uiextension dialog."); + } +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp b/frameworks/ets/ani/accesstoken/src/ani_request_permission.cpp similarity index 53% rename from frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp rename to frameworks/ets/ani/accesstoken/src/ani_request_permission.cpp index 9b8ca465f0e20784c07d039c1831941ee25eb858..4f71cf139ef8d5e9a5eb32606d2518fdf7e06029 100644 --- a/frameworks/ets/ani/accesstoken/src/ability_access_ctrl.cpp +++ b/frameworks/ets/ani/accesstoken/src/ani_request_permission.cpp @@ -12,30 +12,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include "ani_request_permission.h" -#include "ability_access_ctrl.h" +#include +#include +#include +#include -#include -#include - -#include "access_token.h" +#include "ability_manager_client.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::map>> RequestAsyncInstanceControl::instanceIdMap_; +std::mutex RequestAsyncInstanceControl::instanceIdMutex_; +namespace { +#define SETTER_METHOD_NAME(property) "" #property +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { + LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniRequestPermissionFromUser" }; 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"; @@ -49,18 +49,95 @@ 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"; +} +RequestAsyncContext::~RequestAsyncContext() +{ + if (vm == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "VM is nullptr."); + return; + } + bool isSameThread = IsCurrentThread(threadId); + ani_env* curEnv = isSameThread ? env : GetCurrentEnv(vm); + if (curEnv == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } -#define SETTER_METHOD_NAME(property) "" #property + if (callbackRef != nullptr) { + curEnv->GlobalReference_Delete(callbackRef); + callbackRef = nullptr; + } +} -static void UpdateGrantPermissionResultOnly( - const std::vector& permissions, const std::vector& grantResults, - std::shared_ptr& data, std::vector& newGrantResults) +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, name: %{public}s.", + static_cast(status), setterName); + return false; + } + if ((status = env->Object_SetField_Ref(object, fieldValue, value)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_SetField_Ref Fail %{public}d, name: %{public}s.", + static_cast(status), setterName); + return false; + } + 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 aniPerms = CreateAniArrayString(env, asyncContext->permissionList); + ani_ref aniAuthRes = CreateAniArrayInt(env, state); + ani_ref aniDiasShownRes = CreateAniArrayBool(env, asyncContext->dialogShownResults); + ani_ref aniErrorReasons = CreateAniArrayInt(env, asyncContext->errorReasons); + if (aniPerms == nullptr || aniAuthRes == nullptr || aniDiasShownRes == nullptr || aniErrorReasons == nullptr || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(permissions), aniPerms) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(authResults), aniAuthRes) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(dialogShownResults), aniDiasShownRes) || + !CallSetter(env, cls, aObject, SETTER_METHOD_NAME(errorReasons), aniErrorReasons)) { + aObject = nullptr; + } + DeleteReference(env, aniPerms); + DeleteReference(env, aniAuthRes); + DeleteReference(env, aniDiasShownRes); + DeleteReference(env, aniErrorReasons); + return aObject; +} + +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; + result = data->result.errorCode == AccessToken::RET_SUCCESS ? grantResults[i] : AccessToken::INVALID_OPER; } newGrantResults.emplace_back(result); } @@ -107,30 +184,16 @@ static bool IsDynamicRequest(std::shared_ptr& asyncContext) 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); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); - asyncContext->result = AccessToken::RET_FAILED; + asyncContext->result.errorCode = AccessToken::RET_FAILED; asyncContext->uiExtensionFlag = false; asyncContext->loadlock.unlock(); return; @@ -141,7 +204,7 @@ static void CreateUIExtensionMainThread(std::shared_ptr& as 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->result.errorCode = AccessToken::RET_FAILED; asyncContext->uiExtensionFlag = false; asyncContext->loadlock.unlock(); return; @@ -159,20 +222,20 @@ static void CreateUIExtensionMainThread(std::shared_ptr& as #endif } -static bool CreateServiceExtension(std::shared_ptr& asyncContext) +static void 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; + asyncContext->result.errorCode = RET_FAILED; + return; } 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; + asyncContext->result.errorCode = RET_FAILED; + return; } OHOS::AAFwk::Want want; want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.grantServiceAbilityName); @@ -195,10 +258,9 @@ static bool CreateServiceExtension(std::shared_ptr& asyncCo 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) +static void CreateUIExtension(std::shared_ptr& asyncContext) { OHOS::AAFwk::Want want; want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.grantAbilityName); @@ -221,13 +283,13 @@ static bool CreateUIExtension(std::shared_ptr& asyncContext [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); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); return; @@ -246,207 +308,30 @@ static void GetInstanceId(std::shared_ptr& asyncContext) #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->result.errorCode = RET_FAILED; } data->grantResults.assign(newGrantResults.begin(), newGrantResults.end()); - data->loadlock.unlock(); - g_loadedCond.notify_all(); + + bool isSameThread = IsCurrentThread(data->threadId); + ani_env* env = isSameThread ? data->env : GetCurrentEnv(data->vm); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(data->result.errorCode); + ani_object error = BusinessErrorAni::CreateError(env, stsCode, GetErrorMessage(stsCode, data->result.errorMsg)); + ani_object result = WrapResult(env, data); + ExecuteAsyncCallback(env, reinterpret_cast(data->callbackRef), error, result); + if (!isSameThread && data->vm->DetachCurrentThread() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "DetachCurrentThread failed!"); + } } static ani_status ConvertContext( @@ -486,107 +371,125 @@ static ani_status ConvertContext( 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) +static void RequestPermissionsFromUserProcess(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)) { + if ((asyncContext->permissionsState.empty()) && (asyncContext->result.errorCode == RET_SUCCESS)) { ACCESSTOKEN_LOG_ERROR(LABEL, "GrantResults empty"); - asyncContext->result = RET_FAILED; + asyncContext->result.errorCode = RET_FAILED; } - return false; + return; } + 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); + RequestAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); } else { - lockFlag = CreateServiceExtension(asyncContext); + CreateServiceExtension(asyncContext); } } else if (asyncContext->instanceId == -1) { ACCESSTOKEN_LOG_ERROR(LABEL, "Pop service extension dialog, instanceId is -1."); - lockFlag = CreateServiceExtension(asyncContext); + 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); + 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); + 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) +static bool ParseRequestPermissionFromUser(ani_env* env, ani_object aniContext, ani_array_ref aniPermissionList, + ani_object callback, std::shared_ptr& asyncContext) { - if (env == nullptr || permissionList == nullptr) { - ACCESSTOKEN_LOG_ERROR(LABEL, "permissionList or env null"); - return nullptr; + ani_vm* vm; + ani_status status = env->GetVM(&vm); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetVM failed, error=%{public}d.", static_cast(status)); + return false; } - std::shared_ptr asyncContext = std::make_shared(); - if (!ParseRequestPermissionFromUser(env, aniContext, permissionList, asyncContext)) { - return nullptr; + asyncContext->vm = vm; + asyncContext->env = env; + asyncContext->callback = callback; + asyncContext->threadId = std::this_thread::get_id(); + + status = ConvertContext(env, aniContext, asyncContext); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertContext failed, error=%{public}d.", static_cast(status)); + BusinessErrorAni::ThrowParameterTypeError(env, STS_ERROR_PARAM_ILLEGAL, + GetParamErrorMsg("context", "UIAbility or UIExtension Context")); + return false; + } + asyncContext->permissionList = ParseAniStringVector(env, aniPermissionList); + if (!AniParseCallback(env, reinterpret_cast(callback), asyncContext->callbackRef)) { + return false; } #ifdef EVENTHANDLER_ENABLE asyncContext->handler_ = std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); #endif - bool errTokenID = true; + return true; +} + +void RequestPermissionsFromUserExecute([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, + ani_object aniContext, ani_array_ref aniPermissionList, ani_object callback) +{ + if (env == nullptr || aniPermissionList == nullptr || callback == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Parenv or aniPermissionList or callback is null."); + return; + } + std::shared_ptr asyncContext = std::make_shared(); + if (!ParseRequestPermissionFromUser(env, aniContext, aniPermissionList, callback, asyncContext)) { + return; + } + 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; + + ani_ref nullRef = nullptr; + env->GetNull(&nullRef); + ani_object result = reinterpret_cast(nullRef); + ani_object error = BusinessErrorAni::CreateError(env, STS_ERROR_INNER, GetErrorMessage(STS_ERROR_INNER, + "The specified context does not belong to the current application.")); + ExecuteAsyncCallback(env, callback, error, result); + return; } - if (!RequestPermissionsFromUserProcess(env, asyncContext) || !errTokenID) { - return DealWithResult(env, asyncContext); + RequestPermissionsFromUserProcess(asyncContext); + if (asyncContext->needDynamicRequest) { + return; } + + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(asyncContext->result.errorCode); + ani_object error = BusinessErrorAni::CreateError( + env, stsCode, GetErrorMessage(stsCode, asyncContext->result.errorMsg)); + ani_object result = WrapResult(env, asyncContext); + ExecuteAsyncCallback(env, callback, error, result); 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); + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if (uiContent == nullptr) { ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); - asyncContext->result = RET_FAILED; + asyncContext->result.errorCode = RET_FAILED; return; } uiContent->CloseModalUIExtension(sessionId); @@ -612,10 +515,11 @@ void RequestAsyncInstanceControl::ExecCallback(int32_t id) std::lock_guard lock(instanceIdMutex_); auto iter = RequestAsyncInstanceControl::instanceIdMap_.find(id); if (iter == RequestAsyncInstanceControl::instanceIdMap_.end()) { - ACCESSTOKEN_LOG_ERROR(LABEL, "instanceIdMap_ empty"); + ACCESSTOKEN_LOG_ERROR(LABEL, "Id: %{public}d not existed.", id); return; } while (!iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map size: %{public}zu.", id, iter->second.size()); asyncContext = iter->second[0]; iter->second.erase(iter->second.begin()); CheckDynamicRequest(asyncContext, isDynamic); @@ -624,20 +528,17 @@ void RequestAsyncInstanceControl::ExecCallback(int32_t id) } } if (iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map is empty", id); RequestAsyncInstanceControl::instanceIdMap_.erase(id); } } - bool lockFlag = true; if (isDynamic) { if (asyncContext->uiExtensionFlag) { - lockFlag = CreateUIExtension(asyncContext); + CreateUIExtension(asyncContext); } else { - lockFlag = CreateServiceExtension(asyncContext); + CreateServiceExtension(asyncContext); } } - if (!lockFlag) { - asyncContext->loadlock.unlock(); - } } void RequestAsyncInstanceControl::CheckDynamicRequest( @@ -652,22 +553,23 @@ void RequestAsyncInstanceControl::CheckDynamicRequest( isDynamic = true; } -bool RequestAsyncInstanceControl::AddCallbackByInstanceId(std::shared_ptr& asyncContext) +void 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; + { + 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; + } + RequestAsyncInstanceControl::instanceIdMap_[asyncContext->instanceId] = {}; + } if (asyncContext->uiExtensionFlag) { - lockFlag = CreateUIExtension(asyncContext); + CreateUIExtension(asyncContext); } else { - lockFlag = CreateServiceExtension(asyncContext); + CreateServiceExtension(asyncContext); } - return lockFlag; + return; } UIExtensionCallback::UIExtensionCallback(const std::shared_ptr& reqContext) @@ -693,7 +595,9 @@ void UIExtensionCallback::ReleaseHandler(int32_t code) this->reqContext_->releaseFlag = true; } CloseModalUIExtensionMainThread(this->reqContext_, this->sessionId_); - this->reqContext_->result = code; + this->reqContext_->result.errorCode = code; + ACCESSTOKEN_LOG_ERROR(LABEL, "ReleaseHandler errorCode: %{public}d", + this->reqContext_->result.errorCode); RequestAsyncInstanceControl::ExecCallback(this->reqContext_->instanceId); RequestResultsHandler(this->reqContext_->permissionList, this->reqContext_->permissionsState, this->reqContext_); } @@ -743,6 +647,7 @@ void AuthorizationResult::GrantResultsCallback( if (asyncContext == nullptr) { return; } + ACCESSTOKEN_LOG_ERROR(LABEL, "GrantResultsCallback"); RequestResultsHandler(permissionList, grantResults, asyncContext); } @@ -752,184 +657,13 @@ void AuthorizationResult::WindowShownCallback() if (asyncContext == nullptr) { return; } - OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext); + OHOS::Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); if ((uiContent == nullptr) || !(asyncContext->uiContentFlag)) { return; } 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 +} // namespace AccessToken +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/frameworks/ets/ani/accesstoken/src/ani_request_permission_on_setting.cpp b/frameworks/ets/ani/accesstoken/src/ani_request_permission_on_setting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d8dc482e85454333f582f147871cb5cb5b1b22f --- /dev/null +++ b/frameworks/ets/ani/accesstoken/src/ani_request_permission_on_setting.cpp @@ -0,0 +1,602 @@ +/* + * 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_request_permission_on_setting.h" +#include "accesstoken_kit.h" +#include "accesstoken_log.h" +#include "token_setproc.h" +#include "want.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +std::map>> + RequestOnSettingAsyncInstanceControl::instanceIdMap_; +std::mutex RequestOnSettingAsyncInstanceControl::instanceIdMutex_; +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "AniRequestPermission" }; +constexpr int32_t REQUEST_REALDY_EXIST = 1; +constexpr int32_t PERM_NOT_BELONG_TO_SAME_GROUP = 2; +constexpr int32_t PERM_IS_NOT_DECLARE = 3; +constexpr int32_t ALL_PERM_GRANTED = 4; +constexpr int32_t PERM_NOT_REVOKE_BY_USER = 5; +const std::string PERMISSION_SETTING_KEY = "ohos.user.setting.permission"; +const std::string EXTENSION_TYPE_KEY = "ability.want.params.uiExtensionType"; +const std::string UI_EXTENSION_TYPE = "sys/commonUI"; +const std::string RESULT_ERROR_KEY = "ohos.user.setting.error_code"; +const std::string PERMISSION_RESULT_KEY = "ohos.user.setting.permission.result"; +} +RequestPermOnSettingAsyncContext::~RequestPermOnSettingAsyncContext() +{ + if (vm == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "VM is nullptr."); + return; + } + bool isSameThread = IsCurrentThread(threadId); + ani_env* curEnv = isSameThread ? env : GetCurrentEnv(vm); + if (curEnv == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } + + if (callbackRef != nullptr) { + curEnv->GlobalReference_Delete(callbackRef); + callbackRef = nullptr; + } +} + +static ani_status GetContext( + ani_env* env, const ani_object& aniContext, std::shared_ptr& asyncContext) +{ + auto context = OHOS::AbilityRuntime::GetStageModeContext(env, aniContext); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetStageModeContext failed"); + return ANI_ERROR; + } + asyncContext->abilityContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->abilityContext != nullptr) { + auto abilityInfo = asyncContext->abilityContext->GetApplicationInfo(); + if (abilityInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->uiAbilityFlag = true; + asyncContext->tokenId = abilityInfo->accessTokenId; + } else { + asyncContext->uiExtensionContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (asyncContext->uiExtensionContext == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ConvertTo UIExtensionContext failed"); + return ANI_ERROR; + } + auto uiExtensionInfo = asyncContext->uiExtensionContext->GetApplicationInfo(); + if (uiExtensionInfo == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetApplicationInfo failed"); + return ANI_ERROR; + } + asyncContext->tokenId = uiExtensionInfo->accessTokenId; + } + return ANI_OK; +} + +static bool ParseRequestPermissionOnSetting(ani_env* env, ani_object& aniContext, ani_array_ref& aniPermissionList, + ani_object callback, std::shared_ptr& asyncContext) +{ + ani_vm* vm; + ani_status status = env->GetVM(&vm); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetVM failed, error=%{public}d.", static_cast(status)); + return false; + } + asyncContext->vm = vm; + asyncContext->env = env; + asyncContext->callback = callback; + asyncContext->threadId = std::this_thread::get_id(); + + if (GetContext(env, aniContext, asyncContext) != ANI_OK) { + BusinessErrorAni::ThrowParameterTypeError(env, STS_ERROR_PARAM_ILLEGAL, + GetParamErrorMsg("context", "UIAbility or UIExtension Context")); + return false; + } + asyncContext->permissionList = ParseAniStringVector(env, aniPermissionList); + if (!AniParseCallback(env, reinterpret_cast(callback), asyncContext->callbackRef)) { + return false; + } +#ifdef EVENTHANDLER_ENABLE + asyncContext->handler_ = std::make_shared(AppExecFwk::EventRunner::GetMainEventRunner()); +#endif + return true; +} + +static void StateToEnumIndex(int32_t state, ani_size& enumIndex) +{ + if (state == 0) { + enumIndex = 1; + } else { + enumIndex = 0; + } +} + +static ani_object ReturnResult(ani_env* env, std::shared_ptr& asyncContext) +{ + ani_class arrayCls = nullptr; + if (env->FindClass("Lescompat/Array;", &arrayCls) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass name Lescompat/Array failed!"); + return nullptr; + } + + ani_method arrayCtor; + if (env->Class_FindMethod(arrayCls, "", "I:V", &arrayCtor) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass failed!"); + return nullptr; + } + + ani_object arrayObj; + if (env->Object_New(arrayCls, arrayCtor, &arrayObj, asyncContext->stateList.size()) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object new failed!"); + return nullptr; + } + + const char* enumDescriptor = "L@ohos/abilityAccessCtrl/abilityAccessCtrl/GrantStatus;"; + ani_enum enumType; + if (env->FindEnum(enumDescriptor, &enumType) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass name %{public}s failed!", enumDescriptor); + return nullptr; + } + + ani_size index = 0; + for (const auto& state: asyncContext->stateList) { + ani_enum_item enumItem; + ani_size enumIndex = 0; + StateToEnumIndex(state, enumIndex); + if (env->Enum_GetEnumItemByIndex(enumType, enumIndex, &enumItem) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetEnumItemByIndex value %{public}u failed!", state); + break; + } + + if (env->Object_CallMethodByName_Void(arrayObj, "$_set", "ILstd/core/Object;:V", index, enumItem) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_CallMethodByName_Void $_set failed!"); + break; + } + index++; + } + + return arrayObj; +} + +static int32_t TransferToStsErrorCode(int32_t errorCode) +{ + int32_t stsCode = STS_OK; + switch (errorCode) { + case RET_SUCCESS: + stsCode = STS_OK; + break; + case REQUEST_REALDY_EXIST: + stsCode = STS_ERROR_REQUEST_IS_ALREADY_EXIST; + break; + case PERM_NOT_BELONG_TO_SAME_GROUP: + stsCode = STS_ERROR_PARAM_INVALID; + break; + case PERM_IS_NOT_DECLARE: + stsCode = STS_ERROR_PARAM_INVALID; + break; + case ALL_PERM_GRANTED: + stsCode = STS_ERROR_ALL_PERM_GRANTED; + break; + case PERM_NOT_REVOKE_BY_USER: + stsCode = STS_ERROR_PERM_NOT_REVOKE_BY_USER; + break; + default: + stsCode = STS_ERROR_INNER; + break; + } + ACCESSTOKEN_LOG_INFO(LABEL, "Dialog error(%{public}d) stsCode(%{public}d).", errorCode, stsCode); + return stsCode; +} + +PermissonOnSettingUICallback::PermissonOnSettingUICallback( + const std::shared_ptr& reqContext) +{ + this->reqContext_ = reqContext; +} + +PermissonOnSettingUICallback::~PermissonOnSettingUICallback() +{} + +void PermissonOnSettingUICallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_ = sessionId; +} + +static void PermissionResultsCallbackUI( + const std::vector stateList, std::shared_ptr& data) +{ + bool isSameThread = IsCurrentThread(data->threadId); + ani_env* env = isSameThread ? data->env : GetCurrentEnv(data->vm); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetCurrentEnv failed."); + return; + } + + int32_t stsCode = TransferToStsErrorCode(data->result.errorCode); + ani_object error = BusinessErrorAni::CreateError(env, stsCode, GetErrorMessage(stsCode, data->result.errorMsg)); + ani_object result = ReturnResult(env, data); + ExecuteAsyncCallback(env, reinterpret_cast(data->callbackRef), error, result); + + if (!isSameThread && data->vm->DetachCurrentThread() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "DetachCurrentThread failed!"); + } +} + +static void CloseSettingModalUIExtensionMainThread(std::shared_ptr& asyncContext, + int32_t sessionId) +{ + auto task = [asyncContext, sessionId]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + asyncContext->result.errorCode = RET_FAILED; + return; + } + uiContent->CloseModalUIExtension(sessionId); + ACCESSTOKEN_LOG_INFO(LABEL, "Close end, sessionId: %{public}d", sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CloseModalUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +void PermissonOnSettingUICallback::ReleaseHandler(int32_t code) +{ + { + std::lock_guard lock(this->reqContext_->lockReleaseFlag); + if (this->reqContext_->releaseFlag) { + ACCESSTOKEN_LOG_WARN(LABEL, "Callback has executed."); + return; + } + this->reqContext_->releaseFlag = true; + } + CloseSettingModalUIExtensionMainThread(this->reqContext_, this->sessionId_); + if (code == -1) { + this->reqContext_->result.errorCode = code; + } + RequestOnSettingAsyncInstanceControl::UpdateQueueData(this->reqContext_); + RequestOnSettingAsyncInstanceControl::ExecCallback(this->reqContext_->instanceId); + PermissionResultsCallbackUI(this->reqContext_->stateList, this->reqContext_); +} + +/* + * when UIExtensionAbility use terminateSelfWithResult + */ +void PermissonOnSettingUICallback::OnResult(int32_t resultCode, const AAFwk::Want& result) +{ + this->reqContext_->result.errorCode = result.GetIntParam(RESULT_ERROR_KEY, 0); + this->reqContext_->stateList = result.GetIntArrayParam(PERMISSION_RESULT_KEY); + ACCESSTOKEN_LOG_INFO(LABEL, "ResultCode is %{public}d, errorCodeis %{public}d, listSize=%{public}zu.", + resultCode, this->reqContext_->result.errorCode, this->reqContext_->stateList.size()); + ReleaseHandler(0); +} + +/* + * when UIExtensionAbility send message to UIExtensionComponent + */ +void PermissonOnSettingUICallback::OnReceive(const AAFwk::WantParams& receive) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Called!"); +} + +/* + * when UIExtensionAbility disconnect or use terminate or process die + * releaseCode is 0 when process normal exit + */ +void PermissonOnSettingUICallback::OnRelease(int32_t releaseCode) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseCode is %{public}d", releaseCode); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error + */ +void PermissonOnSettingUICallback::OnError(int32_t code, const std::string& name, const std::string& message) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Code is %{public}d, name is %{public}s, message is %{public}s", + code, name.c_str(), message.c_str()); + + ReleaseHandler(-1); +} + +/* + * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init, + * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy + */ +void PermissonOnSettingUICallback::OnRemoteReady(const std::shared_ptr& uiProxy) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "Connect to UIExtensionAbility successfully."); +} + +/* + * when UIExtensionComponent destructed + */ +void PermissonOnSettingUICallback::OnDestroy() +{ + ACCESSTOKEN_LOG_INFO(LABEL, "UIExtensionAbility destructed."); + ReleaseHandler(-1); +} + +static void CreateSettingUIExtensionMainThread(std::shared_ptr& asyncContext, + const AAFwk::Want& want, const Ace::ModalUIExtensionCallbacks& uiExtensionCallbacks, + const std::shared_ptr& uiExtCallback) +{ + auto task = [asyncContext, want, uiExtensionCallbacks, uiExtCallback]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to get ui content!"); + asyncContext->result.errorCode = RET_FAILED; + return; + } + + Ace::ModalUIExtensionConfig config; + config.isProhibitBack = true; + int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config); + ACCESSTOKEN_LOG_INFO(LABEL, "Create end, sessionId: %{public}d, tokenId: %{public}d.", + sessionId, asyncContext->tokenId); + if (sessionId == 0) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to create component, sessionId is 0."); + asyncContext->result.errorCode = RET_FAILED; + return; + } + uiExtCallback->SetSessionId(sessionId); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:CreateUIExtensionMainThread"); + } else { + task(); + } +#else + task(); +#endif +} + +static void CreateUIExtension( + const OHOS::AAFwk::Want &want, std::shared_ptr asyncContext) +{ + auto uiExtCallback = std::make_shared(asyncContext); + Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = { + [uiExtCallback](int32_t releaseCode) { + uiExtCallback->OnRelease(releaseCode); + }, + [uiExtCallback](int32_t resultCode, const AAFwk::Want& result) { + uiExtCallback->OnResult(resultCode, result); + }, + [uiExtCallback](const AAFwk::WantParams& receive) { + uiExtCallback->OnReceive(receive); + }, + [uiExtCallback](int32_t code, const std::string& name, [[maybe_unused]] const std::string& message) { + uiExtCallback->OnError(code, name, name); + }, + [uiExtCallback](const std::shared_ptr& uiProxy) { + uiExtCallback->OnRemoteReady(uiProxy); + }, + [uiExtCallback]() { + uiExtCallback->OnDestroy(); + }, + }; + + CreateSettingUIExtensionMainThread(asyncContext, want, uiExtensionCallbacks, uiExtCallback); +} + +static void StartUIExtension(std::shared_ptr& asyncContext) +{ + AccessTokenKit::GetPermissionManagerInfo(asyncContext->info); + ACCESSTOKEN_LOG_INFO(LABEL, "bundleName: %{public}s, permStateAbilityName: %{public}s.", + asyncContext->info.grantBundleName.c_str(), asyncContext->info.permStateAbilityName.c_str()); + OHOS::AAFwk::Want want; + want.SetElementName(asyncContext->info.grantBundleName, asyncContext->info.permStateAbilityName); + want.SetParam(PERMISSION_SETTING_KEY, asyncContext->permissionList); + want.SetParam(EXTENSION_TYPE_KEY, UI_EXTENSION_TYPE); + CreateUIExtension(want, asyncContext); +} + +static void GetInstanceId(std::shared_ptr& asyncContext) +{ + auto task = [asyncContext]() { + Ace::UIContent* uiContent = GetUIContent(asyncContext->abilityContext, + asyncContext->uiExtensionContext, asyncContext->uiAbilityFlag); + if (uiContent == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get ui content failed!"); + return; + } + asyncContext->instanceId = uiContent->GetInstanceId(); + }; +#ifdef EVENTHANDLER_ENABLE + if (asyncContext->handler_ != nullptr) { + asyncContext->handler_->PostSyncTask(task, "AT:GetInstanceId"); + } else { + task(); + } +#else + task(); +#endif + ACCESSTOKEN_LOG_INFO(LABEL, "Instance id: %{public}d", asyncContext->instanceId); +} + +void RequestOnSettingAsyncInstanceControl::AddCallbackByInstanceId( + std::shared_ptr& asyncContext) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "InstanceId: %{public}d", asyncContext->instanceId); + { + std::lock_guard lock(instanceIdMutex_); + auto iter = instanceIdMap_.find(asyncContext->instanceId); + // id is existed mean a pop window is showing, add context to waiting queue + if (iter != instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "InstanceId: %{public}d has existed.", asyncContext->instanceId); + instanceIdMap_[asyncContext->instanceId].emplace_back(asyncContext); + return; + } + // make sure id is in map to indicate a pop-up window is showing + instanceIdMap_[asyncContext->instanceId] = {}; + } + StartUIExtension(asyncContext); +} + +bool static CheckPermList(std::vector permList, std::vector tmpPermList) +{ + if (permList.size() != tmpPermList.size()) { + ACCESSTOKEN_LOG_ERROR( + LABEL, "Perm list size not equal, CurrentPermList size: %{public}zu.", tmpPermList.size()); + return false; + } + + for (const auto& item : permList) { + auto iter = std::find_if(tmpPermList.begin(), tmpPermList.end(), [item](const std::string& perm) { + return item == perm; + }); + if (iter == tmpPermList.end()) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Different permission lists."); + return false; + } + } + return true; +} + +void RequestOnSettingAsyncInstanceControl::UpdateQueueData( + const std::shared_ptr& reqContext) +{ + if (reqContext->result.errorCode != RET_SUCCESS) { + ACCESSTOKEN_LOG_INFO(LABEL, "The queue data does not need to be updated."); + return; + } + for (const int32_t item : reqContext->stateList) { + if (item != PERMISSION_GRANTED) { + ACCESSTOKEN_LOG_INFO(LABEL, "The queue data does not need to be updated"); + return; + } + } + + { + std::lock_guard lock(instanceIdMutex_); + int32_t id = reqContext->instanceId; + auto iter = instanceIdMap_.find(id); + if (iter == instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d not existed.", id); + return; + } + std::vector permList = reqContext->permissionList; + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map size: %{public}zu.", id, iter->second.size()); + for (auto& asyncContext : iter->second) { + std::vector tmpPermList = asyncContext->permissionList; + if (CheckPermList(permList, tmpPermList)) { + asyncContext->result.errorCode = reqContext->result.errorCode; + asyncContext->stateList = reqContext->stateList; + asyncContext->isDynamic = false; + } + } + } +} + +void RequestOnSettingAsyncInstanceControl::ExecCallback(int32_t id) +{ + std::shared_ptr asyncContext = nullptr; + bool isDynamic = false; + { + std::lock_guard lock(instanceIdMutex_); + auto iter = instanceIdMap_.find(id); + if (iter == instanceIdMap_.end()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d not existed.", id); + return; + } + while (!iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map size: %{public}zu.", id, iter->second.size()); + asyncContext = iter->second[0]; + iter->second.erase(iter->second.begin()); + CheckDynamicRequest(asyncContext, isDynamic); + if (isDynamic) { + break; + } + } + if (iter->second.empty()) { + ACCESSTOKEN_LOG_INFO(LABEL, "Id: %{public}d, map is empty", id); + instanceIdMap_.erase(id); + } + } + if (isDynamic) { + StartUIExtension(asyncContext); + } +} + +void RequestOnSettingAsyncInstanceControl::CheckDynamicRequest( + std::shared_ptr& asyncContext, bool& isDynamic) +{ + isDynamic = asyncContext->isDynamic; + if (!isDynamic) { + ACCESSTOKEN_LOG_INFO(LABEL, "It does not need to request permission exsion"); + PermissionResultsCallbackUI(asyncContext->stateList, asyncContext); + return; + } +} + +void RequestPermissionOnSettingExecute([[maybe_unused]] ani_env* env, + [[maybe_unused]] ani_object object, ani_object aniContext, ani_array_ref permissionList, ani_object callback) +{ + if (env == nullptr || permissionList == nullptr || callback == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "env or permissionList or callback is null."); + return; + } + + std::shared_ptr asyncContext = + std::make_shared(); + if (!ParseRequestPermissionOnSetting(env, aniContext, permissionList, callback, asyncContext)) { + return; + } + + ani_ref nullRef = nullptr; + env->GetNull(&nullRef); + ani_object result = reinterpret_cast(nullRef); + static AccessTokenID selfTokenID = static_cast(GetSelfTokenID()); + if (selfTokenID != asyncContext->tokenId) { + ACCESSTOKEN_LOG_ERROR(LABEL, "The context tokenID %{public}d is not same with selfTokenID %{public}d.", + asyncContext->tokenId, selfTokenID); + ani_object error = + BusinessErrorAni::CreateError(env, STS_ERROR_PARAM_INVALID, GetErrorMessage(STS_ERROR_PARAM_INVALID, + "The specified context does not belong to the current application.")); + ExecuteAsyncCallback(env, callback, error, result); + return; + } + GetInstanceId(asyncContext); + RequestOnSettingAsyncInstanceControl::AddCallbackByInstanceId(asyncContext); + ACCESSTOKEN_LOG_INFO(LABEL, "Start to pop ui extension dialog"); + + if (asyncContext->result.errorCode != RET_SUCCESS) { + int32_t stsCode = TransferToStsErrorCode(asyncContext->result.errorCode); + ani_object error = BusinessErrorAni::CreateError( + env, stsCode, GetErrorMessage(stsCode, asyncContext->result.errorMsg)); + ExecuteAsyncCallback(env, callback, error, result); + ACCESSTOKEN_LOG_WARN(LABEL, "Failed to pop uiextension dialog."); + } +} +} // 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 index 1f19f85aeab918dc9833d313c98dd60a4e4df6fb..6ebcfa5145f1dfed04d00d08c7051ba53cf8e385 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/ets/permissions.ets b/frameworks/ets/ani/common/ets/permissions.ets index c06103b9640f0ebf38d4c86f9e106cb2d5c2d8e5..e0a300419aa58dbff4d96fb2c160b462ab36e5d2 100644 --- a/frameworks/ets/ani/common/ets/permissions.ets +++ b/frameworks/ets/ani/common/ets/permissions.ets @@ -12,25 +12,4 @@ * 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 index ba08345a99963339afd0fba9f88a734e81d99a4a..a1ce477b782c41e9417ccf569636f1861d628f97 100644 --- a/frameworks/ets/ani/common/ets/security/PermissionRequestResult.ets +++ b/frameworks/ets/ani/common/ets/security/PermissionRequestResult.ets @@ -12,126 +12,9 @@ * 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 - */ + authResults: Array = new Array(); 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; + 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 index 2af6c5ca3c0e4cb5da6613ffb85bc7d9e07ac41b..e47fe113372111c792d696a2f5c4493d246eada9 100644 --- a/frameworks/ets/ani/common/include/ani_error.h +++ b/frameworks/ets/ani/common/include/ani_error.h @@ -13,11 +13,12 @@ * 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 +#include "access_token.h" namespace OHOS { namespace Security { @@ -42,23 +43,26 @@ typedef enum { 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_PERM_NOT_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); + +struct AtmResult { + int32_t errorCode = 0; + std::string errorMsg = ""; +}; + +std::string GetParamErrorMsg(const std::string& param, const std::string& errMsg); +std::string GetErrorMessage(int32_t errCode, const std::string& extendMsg = ""); 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 void ThrowParameterTypeError(ani_env* env, int32_t err, const std::string& errMsg); + static void ThrowError(ani_env* env, int32_t err, const std::string& errMsg = ""); static int32_t GetStsErrorCode(int32_t errCode); + static bool ValidateTokenIDdWithThrowError(ani_env* env, AccessTokenID tokenID); + static bool ValidatePermissionWithThrowError(ani_env* env, const std::string& permission); + static bool ValidatePermissionFlagWithThrowError(ani_env* env, uint32_t flag); private: static void ThrowError(ani_env* env, ani_object err); @@ -66,4 +70,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..8c7b6222b85bf9d7adcdf83dc2e3cd75be4fdc56 --- /dev/null +++ b/frameworks/ets/ani/common/include/ani_utils.h @@ -0,0 +1,84 @@ +/* + * 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 std::string& namespaceDescriptor, ani_namespace& out); +bool AniFindClass(ani_env* env, const std::string& classDescriptor, ani_class& out); +bool AniClassFindMethod( + ani_env* env, const ani_class& aniClass, const std::string& methodDescriptor, + const std::string& signature, ani_method& out); +bool AniClassFindField(ani_env* env, const ani_class& aniClass, const std::string& fieldName, ani_field& out); + +bool AniParseCallback(ani_env* env, const ani_ref& ani_callback, ani_ref& out); +bool AniIsRefUndefined(ani_env* env, const ani_ref& ref); +bool AniParseUint32(ani_env* env, const ani_int& aniInt, uint32_t& out); +bool AniParseAccessTokenIDArray(ani_env* env, const ani_array_ref& array, std::vector& out); + +bool GetBoolProperty(ani_env* env, const ani_object& object, const std::string& property, bool& value); +bool GetIntProperty(ani_env* env, const ani_object& object, const std::string& property, int32_t& value); +bool GetLongProperty(ani_env* env, const ani_object& object, const std::string& property, int64_t& value); +bool GetStringProperty(ani_env* env, const ani_object& object, const std::string& property, std::string& value); +bool GetEnumProperty(ani_env* env, const ani_object& object, const std::string& property, int32_t& value); +bool GetStringVecProperty( + ani_env* env, const ani_object& object, const std::string& property, std::vector& value); + +bool SetBoolProperty(ani_env* env, ani_object& object, const std::string& property, bool in); +bool SetIntProperty(ani_env* env, ani_object& object, const std::string& property, int32_t in); +bool SetLongProperty(ani_env* env, ani_object& object, const std::string& property, int64_t in); +bool SetRefProperty(ani_env* env, ani_object& object, const std::string& property, const ani_ref& in); +bool SetStringProperty(ani_env* env, ani_object& aniObject, const std::string& property, const std::string& in); +bool SetEnumProperty( + ani_env* env, ani_object& aniObject, const std::string& enumDescription, + const std::string& property, uint32_t value); +bool SetOptionalIntProperty(ani_env* env, ani_object& aniObject, const std::string& property, int32_t 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); + +// ani to naitive +std::string ParseAniString(ani_env* env, ani_string aniStr); +std::vector ParseAniStringVector(ani_env* env, const ani_array_ref& aniStrArr); + +// native to ani +ani_string CreateAniString(ani_env *env, const std::string& str); +ani_object CreateIntObject(ani_env *env, int32_t value); +ani_object CreateBooleanObject(ani_env *env, bool value); +ani_object CreateClassObject(ani_env* env, const std::string& classDescriptor); +ani_object CreateArrayObject(ani_env* env, uint32_t length); + +ani_ref CreateAniArrayBool(ani_env* env, const std::vector& cArray); +ani_ref CreateAniArrayInt(ani_env* env, const std::vector& cArray); +ani_ref CreateAniArrayString(ani_env* env, const std::vector& cArray); +ani_env* GetCurrentEnv(ani_vm* vm); + +// delete ref of GlobalReference_Create +void DeleteReference(ani_env* env, ani_ref& ref); +} // 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 7e8a2ffb7839280716b47d9c3c2547d967433e85..23b9b9719d7153e93f0a2bc840385489f0a9e09e 100644 --- a/frameworks/ets/ani/common/src/ani_error.cpp +++ b/frameworks/ets/ani/common/src/ani_error.cpp @@ -18,6 +18,7 @@ #include "access_token_error.h" #include "accesstoken_log.h" +#include "data_validator.h" namespace OHOS { namespace Security { @@ -25,21 +26,15 @@ 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_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." }, @@ -50,18 +45,17 @@ static const std::unordered_map g_errorStringMap = { { 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, + { STS_ERROR_PERM_NOT_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) +void BusinessErrorAni::ThrowError(ani_env* env, int32_t err, const std::string& errMsg) { if (env == nullptr) { return; } - ani_object error = CreateError(env, err, msg); + ani_object error = CreateError(env, err, errMsg); ThrowError(env, error); } @@ -116,97 +110,31 @@ ani_object BusinessErrorAni::CreateError(ani_env* env, ani_int code, const std:: return obj; } -std::string GetParamErrorMsg(const std::string& param, const std::string& type) +std::string GetParamErrorMsg(const std::string& param, const std::string& errMsg) { - std::string msg = "Parameter Error. The type of \"" + param + "\" must be " + type + "."; + std::string msg = "Parameter Error. The type of \"" + param + "\" must be " + errMsg + "."; return msg; } -std::string GetErrorMessage(uint32_t errCode) +std::string GetErrorMessage(int32_t errCode, const std::string& extendMsg) { auto iter = g_errorStringMap.find(errCode); if (iter != g_errorStringMap.end()) { - return iter->second; + return iter->second + (extendMsg.empty() ? "" : ("" + extendMsg)); } - std::string errMsg = "Unknown error, errCode + " + std::to_string(errCode) + "."; + 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) +void BusinessErrorAni::ThrowParameterTypeError(ani_env* env, int32_t err, const std::string& errMsg) { if (env == nullptr) { return; } - ani_object error = CreateCommonError(env, err, parameter, type); + ani_object error = CreateError(env, err, errMsg); 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) { @@ -264,6 +192,37 @@ int32_t BusinessErrorAni::GetStsErrorCode(int32_t errCode) ACCESSTOKEN_LOG_DEBUG(LABEL, "GetStsErrorCode nativeCode(%{public}d) stsCode(%{public}d).", errCode, stsCode); return stsCode; } + +bool BusinessErrorAni::ValidateTokenIDdWithThrowError(ani_env* env, AccessTokenID tokenID) +{ + if (!DataValidator::IsTokenIDValid(tokenID)) { + std::string errMsg = GetErrorMessage(STS_ERROR_PARAM_INVALID, "The tokenID is 0."); + BusinessErrorAni::ThrowError(env, STS_ERROR_PARAM_INVALID, errMsg); + return false; + } + return true; +} + +bool BusinessErrorAni::ValidatePermissionWithThrowError(ani_env* env, const std::string& permission) +{ + if (!DataValidator::IsPermissionNameValid(permission)) { + std::string errMsg = GetErrorMessage( + STS_ERROR_PARAM_INVALID, "The permissionName is empty or exceeds 256 characters."); + BusinessErrorAni::ThrowError(env, STS_ERROR_PARAM_INVALID, errMsg); + return false; + } + return true; +} + +bool BusinessErrorAni::ValidatePermissionFlagWithThrowError(ani_env* env, uint32_t flag) +{ + if (!DataValidator::IsPermissionFlagValid(flag)) { + std::string errMsg = GetErrorMessage(STS_ERROR_PARAM_INVALID, "The permissionFlags is invalid."); + BusinessErrorAni::ThrowError(env, STS_ERROR_PARAM_INVALID, errMsg); + return false; + } + return true; +} } // namespace AccessToken } // namespace Security } // namespace OHOS 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..53842ea3a2ecd619b70382639c6fefcf36eb5407 --- /dev/null +++ b/frameworks/ets/ani/common/src/ani_utils.cpp @@ -0,0 +1,631 @@ +/* + * 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 std::string& namespaceDescriptor, ani_namespace& out) +{ + if (env->FindNamespace(namespaceDescriptor.c_str(), &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindNamespace failed!"); + return false; + } + return true; +} + +bool AniFindClass(ani_env* env, const std::string& classDescriptor, ani_class& out) +{ + if (env->FindClass(classDescriptor.c_str(), &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass(%{public}s) failed.", classDescriptor.c_str()); + return false; + } + return true; +} + +bool AniClassFindMethod( + ani_env* env, const ani_class& aniClass, const std::string& methodDescriptor, + const std::string& signature, ani_method& out) +{ + if (env->Class_FindMethod(aniClass, methodDescriptor.c_str(), signature.c_str(), &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 AniParseUint32(ani_env* env, const ani_ref& aniInt, uint32_t& out) +{ + ani_int tmp; + ani_status status; + if ((status = env->Object_CallMethodByName_Int( + static_cast(aniInt), "unboxed", nullptr, &tmp)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_CallMethodByName_Int failed! %{public}d", status); + return false; + } + + out = static_cast(tmp); + return true; +} + +bool AniParseAccessTokenIDArray(ani_env* env, const ani_array_ref& array, std::vector& out) +{ + ani_size size; + if (env->Array_GetLength(array, &size) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_GetLength failed!"); + return false; + } + + for (ani_size i = 0; i < size; ++i) { + ani_ref elementRef; + if (env->Array_Get_Ref(array, i, &elementRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_Get_Ref failed at index %{public}zu!", i); + return false; + } + uint32_t value; + if (!AniParseUint32(env, elementRef, value)) { + return false; + } + if (value == 0) { + return false; + } + out.emplace_back(value); + } + return true; +} + +std::vector ParseAniStringVector(ani_env* env, const ani_array_ref& aniStrArr) +{ + std::vector out; + ani_size size = 0; + if (env->Array_GetLength(aniStrArr, &size) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_GetLength failed!"); + return out; + } + + for (ani_size i = 0; i < size; ++i) { + ani_ref aniRef; + if (env->Array_Get_Ref(aniStrArr, i, &aniRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Array_Get_Ref failed!"); + return out; + } + + std::string stdStr = ParseAniString(env, static_cast(aniRef)); + if (!stdStr.empty()) { + out.emplace_back(stdStr); + } + } + return out; +} + +bool AniParseCallback(ani_env* env, const ani_ref& aniCallback, ani_ref& out) +{ + if (env->GlobalReference_Create(aniCallback, &out) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GlobalReference_Create failed!"); + return false; + } + return true; +} + +bool AniIsRefUndefined(ani_env* env, const ani_ref& ref) +{ + ani_boolean isUnd; + if (env->Reference_IsUndefined(ref, &isUnd) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Reference_IsUndefined failed!"); + return false; + } + return isUnd ? true : false; +} + +ani_string CreateAniString(ani_env* env, const std::string& str) +{ + ani_string aniStr = {}; + if (env->String_NewUTF8(str.c_str(), str.size(), &aniStr) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "String_NewUTF8 failed!"); + return aniStr; + } + return aniStr; +} + +bool IsCurrentThread(std::thread::id threadId) +{ + return threadId == std::this_thread::get_id(); +} + +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; +} + +std::string ParseAniString(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; +} + +ani_ref CreateAniArrayString(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, "FindClass String failed."); + return nullptr; + } + ani_ref undefinedRef = nullptr; + if (ANI_OK != env->GetUndefined(&undefinedRef)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "GetUndefined failed."); + return nullptr; + } + if (env->Array_New_Ref(aStringcls, length, undefinedRef, &aArrayRef) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "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, "GlobalReference_Create failed."); + return nullptr; + } + return aRef; +} + +ani_ref CreateAniArrayInt(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, "Array_New_Int 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, "GlobalReference_Create failed."); + return nullptr; + } + return aRef; +} + +ani_ref CreateAniArrayBool(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, "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, "GlobalReference_Create failed."); + return nullptr; + } + return aRef; +} + +void DeleteReference(ani_env* env, ani_ref& ref) +{ + if (ref != nullptr) { + env->GlobalReference_Delete(ref); + ref = nullptr; + } +} + +ani_object CreateBooleanObject(ani_env *env, bool value) +{ + ani_class persionCls; + ani_status status = ANI_ERROR; + if ((status = env->FindClass("Lstd/core/Boolean;", &persionCls)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_method personInfoCtor; + if ((status = env->Class_FindMethod(persionCls, "", "Z:V", &personInfoCtor)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindMethod fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_object personInfoObj; + if ((status = env->Object_New(persionCls, personInfoCtor, &personInfoObj, value)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + return personInfoObj; +} + +ani_object CreateIntObject(ani_env *env, int32_t value) +{ + ani_class persionCls; + ani_status status = ANI_ERROR; + if ((status = env->FindClass("Lstd/core/Int;", &persionCls)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_method aniMethod; + if ((status = env->Class_FindMethod(persionCls, "", "I:V", &aniMethod)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindMethod fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_object aniObject; + if ((status = env->Object_New(persionCls, aniMethod, &aniObject, value)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + return aniObject; +} + +ani_object CreateClassObject(ani_env* env, const std::string& classDescriptor) +{ + ani_class aniClass; + ani_status status = ANI_ERROR; + if ((status = env->FindClass(classDescriptor.c_str(), &aniClass)) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindClass fail, status : %{public}d.", static_cast(status)); + return nullptr; + } + ani_method aniMethod; + status = env->Class_FindMethod(aniClass, "", ":V", &aniMethod); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Class_FindMethod failed, status: %{public}d!", status); + return nullptr; + } + ani_object out; + status = env->Object_New(aniClass, aniMethod, &out); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New failed, status %{public}d!", status); + return nullptr; + } + return out; +} + +ani_object CreateArrayObject(ani_env* env, uint32_t length) +{ + ani_class aniClass; + if (!AniFindClass(env, "Lescompat/Array;", aniClass)) { + return nullptr; + } + ani_method aniMethod; + ani_status status = env->Class_FindMethod(aniClass, "", "I:V", &aniMethod); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindMethod failed, status: %{public}d!", status); + return nullptr; + } + ani_object out; + status = env->Object_New(aniClass, aniMethod, &out, length); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object_New failed(size: %{public}d), status %{public}d!", length, status); + return nullptr; + } + return out; +} + +bool GetBoolProperty(ani_env* env, const ani_object& object, const std::string& property, bool& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + if (AniIsRefUndefined(env, ref)) { + return true; + } + + ani_boolean boolValue; + if (env->Object_CallMethodByName_Boolean(static_cast(ref), "unboxed", nullptr, &boolValue) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get bool value of property(%{public}s) failed.", property.c_str()); + return false; + } + value = static_cast(boolValue); + return true; +} + +bool GetIntProperty(ani_env* env, const ani_object& object, const std::string& property, int32_t& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d.", + property.c_str(), static_cast(status)); + return false; + } + if (AniIsRefUndefined(env, ref)) { + ACCESSTOKEN_LOG_INFO(LABEL, "Property(%{public}s) is undefned!", property.c_str()); + return true; + } + + ani_int intValue; + status = env->Object_CallMethodByName_Int(static_cast(ref), "unboxed", nullptr, &intValue); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get int value of property(%{public}s) failed, status: %{public}d.", + property.c_str(), static_cast(status)); + return false; + } + value = static_cast(intValue); + return true; +} + +bool GetLongProperty(ani_env* env, const ani_object& object, const std::string& property, int64_t& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + if (AniIsRefUndefined(env, ref)) { + return true; + } + + ani_long longValue; + if (env->Object_CallMethodByName_Long(static_cast(ref), "unboxed", nullptr, &longValue) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get int64 value of property(%{public}s) failed.", property.c_str()); + return false; + } + value = static_cast(longValue); + return true; +} + +bool GetStringProperty(ani_env* env, const ani_object& object, const std::string& property, std::string& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + if (AniIsRefUndefined(env, ref)) { + return true; + } + value = ParseAniString(env, static_cast(ref)); + return true; +} + +bool GetEnumProperty(ani_env* env, const ani_object& object, const std::string& property, int32_t& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + if (AniIsRefUndefined(env, ref)) { + return true; + } + ani_enum_item aniEnum = static_cast(ref); + ani_int aniInt; + if (env->EnumItem_GetValue_Int(aniEnum, &aniInt) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get enum value of property(%{public}s) failed.", property.c_str()); + return false; + } + value = static_cast(aniInt); + return true; +} + +bool GetStringVecProperty( + ani_env* env, const ani_object& object, const std::string& property, std::vector& value) +{ + ani_ref ref; + ani_status status = env->Object_GetPropertyByName_Ref(object, property.c_str(), &ref); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Get property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + if (AniIsRefUndefined(env, ref)) { + return true; + } + ani_array_ref anirefArray = static_cast(ref); + value = ParseAniStringVector(env, anirefArray); + return true; +} + +bool SetBoolProperty(ani_env* env, ani_object& object, const std::string& property, bool in) +{ + if ((env == nullptr) || (object == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_status status = env->Object_SetPropertyByName_Boolean(object, property.c_str(), static_cast(in)); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + return true; +} + +bool SetIntProperty(ani_env* env, ani_object& object, const std::string& property, int32_t in) +{ + if ((env == nullptr) || (object == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_status status = env->Object_SetPropertyByName_Int(object, property.c_str(), static_cast(in)); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + return true; +} + +bool SetLongProperty(ani_env* env, ani_object& aniObject, const std::string& property, int64_t in) +{ + if ((env == nullptr) || (aniObject == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_status status = env->Object_SetPropertyByName_Long(aniObject, property.c_str(), static_cast(in)); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + return true; +} + +bool SetRefProperty(ani_env* env, ani_object& aniObject, const std::string& property, const ani_ref& in) +{ + if ((env == nullptr) || (aniObject == nullptr) || (in == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_status status = env->Object_SetPropertyByName_Ref(aniObject, property.c_str(), in); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set property(%{public}s) failed, status: %{public}d!", + property.c_str(), status); + return false; + } + return true; +} + +bool SetOptionalIntProperty(ani_env* env, ani_object& aniObject, const std::string& property, int32_t in) +{ + if ((env == nullptr) || (aniObject == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_object intObject = CreateIntObject(env, in); + if (intObject == nullptr) { + return false; + } + + if (!SetRefProperty(env, aniObject, property.c_str(), intObject)) { + return false; + } + + return true; +} + +bool SetStringProperty(ani_env* env, ani_object& aniObject, const std::string& property, const std::string& in) +{ + if ((env == nullptr) || (aniObject == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_ref aniString = static_cast(CreateAniString(env, in)); + if (!SetRefProperty(env, aniObject, property.c_str(), aniString)) { + return false; + } + return true; +} + +bool SetEnumProperty(ani_env* env, ani_object& aniObject, + const std::string& enumDescription, const std::string& property, uint32_t value) +{ + if ((env == nullptr) || (aniObject == nullptr)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Input param is nullptr, property(%{public}s).", property.c_str()); + return false; + } + ani_enum aniEnum; + if (env->FindEnum(enumDescription.c_str(), &aniEnum) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindEnum failed!"); + return false; + } + ani_enum_item aniEnumItem; + if (env->Enum_GetEnumItemByIndex(aniEnum, static_cast(value), &aniEnumItem) != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Enum_GetEnumItemByIndex failed!"); + return false; + } + if (!SetRefProperty(env, aniObject, property.c_str(), aniEnumItem)) { + return false; + } + return true; +} + +ani_env* GetCurrentEnv(ani_vm* vm) +{ + if (vm == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Vm is nullptr."); + return nullptr; + } + ani_env* env = nullptr; + ani_option interopEnabled {"--interop=enable", nullptr}; + ani_options aniArgs {1, &interopEnabled}; + if (vm->AttachCurrentThread(&aniArgs, ANI_VERSION_1, &env) != ANI_OK) { + if (vm->GetEnv(ANI_VERSION_1, &env) != ANI_OK) { + return nullptr; + } + } + return env; +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS diff --git a/frameworks/ets/ani/el5filekeymanager/BUILD.gn b/frameworks/ets/ani/el5filekeymanager/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..c8c1e69eda0743a01d097559d022c480b71ab3d7 --- /dev/null +++ b/frameworks/ets/ani/el5filekeymanager/BUILD.gn @@ -0,0 +1,79 @@ +# 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("el5filekeymanager_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", + "${access_token_path}/frameworks/ets/ani/privacy/include", + "./include", + "${access_token_path}/interfaces/inner_api/el5filekeymanager/include", + ] + sources = [ "src/screen_lock_file_manager.cpp" ] + + deps = [ + "${access_token_path}/frameworks/ets/ani/common:libani_common", + "${access_token_path}/interfaces/inner_api/el5filekeymanager:el5_filekey_manager_sdk", + "${access_token_path}/interfaces/innerkits/privacy:libprivacy_sdk", + ] + + external_deps = [ + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_single", + "napi:ace_napi", + "runtime_core:ani", + "runtime_core:libarkruntime", + ] + + part_name = "access_token" + subsystem_name = "security" +} + +generate_static_abc("screen_lock_file_manager") { + base_url = "./ets" + files = [ "./ets/@ohos.ability.screenLockFileManager.ets" ] + + is_boot_abc = "True" + device_dst_file = "/system/framework/screen_lock_file_manager.abc" +} + +ohos_prebuilt_etc("screen_lock_file_manager_etc") { + source = "$target_out_dir/screen_lock_file_manager.abc" + deps = [ ":screen_lock_file_manager" ] + module_install_dir = "framework" + + part_name = "access_token" + subsystem_name = "security" +} + +group("el5filekeymanager_ets") { + deps = [ + ":el5filekeymanager_ani", + ":screen_lock_file_manager_etc", + ] +} diff --git a/frameworks/ets/ani/el5filekeymanager/ets/@ohos.ability.screenLockFileManager.ets b/frameworks/ets/ani/el5filekeymanager/ets/@ohos.ability.screenLockFileManager.ets new file mode 100644 index 0000000000000000000000000000000000000000..fd2bd21919db97a7e38267fc7bd6b544b05a8fc4 --- /dev/null +++ b/frameworks/ets/ani/el5filekeymanager/ets/@ohos.ability.screenLockFileManager.ets @@ -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. + */ +export namespace screenLockFileManager { + loadLibrary("el5filekeymanager_ani.z"); + + export enum DataType { + MEDIA_DATA = 0x00000001, + ALL_DATA = 0xffffffff + } + + export enum AccessStatus { + ACCESS_DENIED = -1, + ACCESS_GRANTED = 0 + } + + export enum ReleaseStatus { + RELEASE_DENIED = -1, + RELEASE_GRANTED = 0 + } + + export enum KeyStatus { + KEY_NOT_EXIST = -2, + KEY_RELEASED = -1, + KEY_EXIST = 0 + } + + native function acquireAccessSync(): int; + native function acquireAccessExecute(dataType: int): int; + native function releaseAccessSync(): int; + native function releaseAccessExecute(dataType: int): int; + native function queryAppKeyStateSync(): int; + native function queryAppKeyStateExecute(dataType: int): int; + + export function acquireAccess(): AccessStatus { + let result: int = acquireAccessSync(); + return result as AccessStatus; + } + export function acquireAccess(dataType: DataType): AccessStatus { + let result: int = acquireAccessExecute(dataType); + return result as AccessStatus; + } + + export function releaseAccess(): ReleaseStatus { + let result: int = releaseAccessSync(); + return result as ReleaseStatus; + } + export function releaseAccess(dataType: DataType): ReleaseStatus { + let result: int = releaseAccessExecute(dataType); + return result as ReleaseStatus; + } + + export function queryAppKeyState(): KeyStatus { + let result: int = queryAppKeyStateSync(); + return result as KeyStatus; + } + export function queryAppKeyState(dataType: DataType): KeyStatus { + let result: int = queryAppKeyStateExecute(dataType); + return result as KeyStatus; + } +} \ No newline at end of file diff --git a/frameworks/ets/ani/el5filekeymanager/include/screen_lock_file_manager.h b/frameworks/ets/ani/el5filekeymanager/include/screen_lock_file_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..64425a40dcf6db445102f6487a4274b1012eef53 --- /dev/null +++ b/frameworks/ets/ani/el5filekeymanager/include/screen_lock_file_manager.h @@ -0,0 +1,40 @@ +/* + * 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 SCREEN_LOCK_FILE_MANAGER_H +#define SCREEN_LOCK_FILE_MANAGER_H + +namespace OHOS { +namespace Security { +namespace AccessToken { +enum AccessStatus { + ACCESS_DENIED = -1, + ACCESS_GRANTED = 0 +}; + +enum ReleaseStatus { + RELEASE_DENIED = -1, + RELEASE_GRANTED = 0 +}; + +enum KeyStatus { + KEY_NOT_EXIST = -2, + KEY_RELEASED = -1, + KEY_EXIST = 0 +}; +} // namespace AccessToken +} // namespace Security +} // namespace OHOS +#endif //SCREEN_LOCK_FILE_MANAGER_H diff --git a/frameworks/ets/ani/el5filekeymanager/src/screen_lock_file_manager.cpp b/frameworks/ets/ani/el5filekeymanager/src/screen_lock_file_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..086717a5b832e653b9e58690075785d1d5b9c127 --- /dev/null +++ b/frameworks/ets/ani/el5filekeymanager/src/screen_lock_file_manager.cpp @@ -0,0 +1,267 @@ +/* + * 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 "screen_lock_file_manager.h" + +#include +#include + +#include "accesstoken_log.h" +#include "ani_error.h" +#include "ani_utils.h" +#include "data_lock_type.h" +#include "el5_filekey_manager_kit.h" +#include "el5_filekey_manager_error.h" + +namespace OHOS { +namespace Security { +namespace AccessToken { +namespace { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD005A08, "AniScreenLockFileManager" }; +const std::unordered_map EL5ErrMsgMap { + {EFM_ERR_NO_PERMISSION, "Permission denied."}, + {EFM_ERR_NOT_SYSTEM_APP, "Not system app."}, + {EFM_ERR_INVALID_PARAMETER, "Parameter error."}, + {EFM_ERR_SYSTEMCAP_NOT_SUPPORT, "The specified SystemCapability name was not found."}, + {EFM_ERR_INVALID_DATATYPE, "Invalid DataType."}, + {EFM_ERR_REMOTE_CONNECTION, "The system ability work abnormally."}, + {EFM_ERR_FIND_ACCESS_FAILED, "The application is not enabled the data protection under lock screen."}, + {EFM_ERR_ACCESS_RELEASED, "File access is denied."}, + {EFM_ERR_RELEASE_ACCESS_FAILED, "File access was not acquired."}, +}; +} + +std::string GetErrorMessage(uint32_t errCode, const std::string& extendMsg) +{ + auto iter = EL5ErrMsgMap.find(errCode); + if (iter != EL5ErrMsgMap.end()) { + return iter->second + (extendMsg.empty() ? "" : ("" + extendMsg)); + } + std::string errMsg = "Unknown error, errCode " + std::to_string(errCode) + "."; + return errMsg; +} + +bool CheckDataType(ani_env* env, int32_t dataLockType) +{ + if ((static_cast(dataLockType) != DataLockType::MEDIA_DATA) && + (static_cast(dataLockType) != DataLockType::ALL_DATA)) { + BusinessErrorAni::ThrowError( + env, EFM_ERR_INVALID_DATATYPE, GetErrorMessage(EFM_ERR_INVALID_DATATYPE)); + return false; + } + return true; +} + +static ani_int AcquireAccessSync([[maybe_unused]] ani_env* env) +{ + int32_t flag = AccessStatus::ACCESS_DENIED; + ACCESSTOKEN_LOG_INFO(LABEL, "AcquireAccessSync begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(DataLockType::DEFAULT_DATA); + auto retCode = El5FilekeyManagerKit::AcquireAccess(static_cast(dataLockType)); + if (retCode != EFM_SUCCESS) { + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + return flag; + } else { + return static_cast(retCode); + } +} + +static ani_int AcquireAccessExecute([[maybe_unused]] ani_env* env, ani_int datatype) +{ + int32_t flag = AccessStatus::ACCESS_DENIED; + ACCESSTOKEN_LOG_INFO(LABEL, "AcquireAccessExcute begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(datatype); + if (!CheckDataType(env, dataLockType)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid DataType."); + return flag; + } + + auto retCode = El5FilekeyManagerKit::AcquireAccess(static_cast(dataLockType)); + if (retCode != EFM_SUCCESS) { + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + return flag; + } else { + return static_cast(retCode); + } +} + +static ani_int ReleaseAccessSync([[maybe_unused]] ani_env* env) +{ + int32_t flag = ReleaseStatus::RELEASE_DENIED; + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseAccessSync begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(DataLockType::DEFAULT_DATA); + auto retCode = El5FilekeyManagerKit::ReleaseAccess(static_cast(dataLockType)); + if (retCode != EFM_SUCCESS) { + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + return flag; + } else { + return static_cast(retCode); + } +} + +static ani_int ReleaseAccessExecute([[maybe_unused]] ani_env* env, ani_int datatype) +{ + int32_t flag = ReleaseStatus::RELEASE_DENIED; + ACCESSTOKEN_LOG_INFO(LABEL, "ReleaseAccessExecute begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(datatype); + if (!CheckDataType(env, dataLockType)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid DataType."); + return flag; + } + + auto retCode = El5FilekeyManagerKit::ReleaseAccess(static_cast(dataLockType)); + if (retCode != EFM_SUCCESS) { + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + return flag; + } else { + return static_cast(retCode); + } +} + +static ani_int QueryAppKeyStateSync([[maybe_unused]] ani_env* env) +{ + int32_t flag = KeyStatus::KEY_RELEASED; + ACCESSTOKEN_LOG_INFO(LABEL, "QueryAppKeyStateSync begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(DataLockType::DEFAULT_DATA); + auto retCode = El5FilekeyManagerKit::QueryAppKeyState(static_cast(dataLockType)); + switch (retCode) { + case EFM_SUCCESS: + retCode = KEY_EXIST; + break; + case EFM_ERR_ACCESS_RELEASED: + retCode = KEY_RELEASED; + break; + case EFM_ERR_FIND_ACCESS_FAILED: + retCode = KEY_NOT_EXIST; + break; + default: + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + retCode = KEY_RELEASED; + break; + } + return static_cast(retCode); +} + +static ani_int QueryAppKeyStateExecute([[maybe_unused]] ani_env* env, ani_int datatype) +{ + int32_t flag = KEY_RELEASED; + ACCESSTOKEN_LOG_INFO(LABEL, "QueryAppKeyStateExecute begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return flag; + } + + int32_t dataLockType = static_cast(datatype); + if (!CheckDataType(env, dataLockType)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Invalid DataType."); + return flag; + } + + auto retCode = El5FilekeyManagerKit::QueryAppKeyState(static_cast(dataLockType)); + switch (retCode) { + case EFM_SUCCESS: + retCode = KEY_EXIST; + break; + case EFM_ERR_ACCESS_RELEASED: + retCode = KEY_RELEASED; + break; + case EFM_ERR_FIND_ACCESS_FAILED: + retCode = KEY_NOT_EXIST; + break; + default: + BusinessErrorAni::ThrowError(env, retCode, GetErrorMessage(retCode)); + retCode = KEY_RELEASED; + break; + } + return static_cast(retCode); +} + +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ANI_Constructor begin"); + 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; + } + + if (env->ResetError() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ResetError failed."); + } + + ani_namespace ns; + if (ANI_OK != env->FindNamespace("L@ohos/ability/screenLockFileManager/screenLockFileManager;", &ns)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindNamespace screenLockFileManager failed."); + return ANI_NOT_FOUND; + } + + // namespace method input param without ani_object + std::array nsMethods = { + ani_native_function { "acquireAccessSync", nullptr, reinterpret_cast(AcquireAccessSync) }, + ani_native_function{ "acquireAccessExecute", nullptr, reinterpret_cast(AcquireAccessExecute)}, + ani_native_function{ "releaseAccessSync", nullptr, reinterpret_cast(ReleaseAccessSync)}, + ani_native_function{ "releaseAccessExecute", nullptr, reinterpret_cast(ReleaseAccessExecute)}, + ani_native_function{ "queryAppKeyStateSync", nullptr, reinterpret_cast(QueryAppKeyStateSync)}, + ani_native_function { "queryAppKeyStateExecute", nullptr, reinterpret_cast(QueryAppKeyStateExecute) }, + }; + ani_status status = env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size()); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Namespace_BindNativeFunctions failed status : %{public}d.", status); + } + if (env->ResetError() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ResetError failed."); + } + + *result = ANI_VERSION_1; + return ANI_OK; +} +} +} // namespace AccessToken +} // namespace Security +} // namespace OHOS diff --git a/frameworks/ets/ani/privacy/BUILD.gn b/frameworks/ets/ani/privacy/BUILD.gn index ee46f37940ec2360338db727b0dfbe4846c7cce1..9c44be7822822ad8c4ed0a9b791b8d188823517c 100644 --- a/frameworks/ets/ani/privacy/BUILD.gn +++ b/frameworks/ets/ani/privacy/BUILD.gn @@ -29,10 +29,12 @@ 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" ] deps = [ + "${access_token_path}/frameworks/common:accesstoken_common_cxx", "${access_token_path}/frameworks/ets/ani/common:libani_common", "${access_token_path}/interfaces/innerkits/privacy:libprivacy_sdk", ] diff --git a/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets index 1d66837118c638ebb814266bb8cd17d9d04aa27b..edbe97fea60fb637478ecf2b3ecf0e65468c827c 100644 --- a/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets +++ b/frameworks/ets/ani/privacy/ets/@ohos.privacyManager.ets @@ -25,64 +25,134 @@ export { Permissions } export default namespace privacyManager { loadLibrary("privacy_ani.z"); - enum PermissionUsedType { - NORMAL_TYPE = 0, - PICKER_TYPE = 1, - SECURITY_COMPONENT_TYPE = 2 - }; + native function addPermissionUsedRecordExecute( + tokenID: int, permissionName: Permissions, successCount: int, failCount: int, + options: AddPermissionUsedRecordOptions): void; - 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, - }; + native function startUsingPermissionExecute( + tokenID: int, permissionName: Permissions, pid: int, usedType: int): void; + native function stopUsingPermissionExecute(tokenID: int, permissionName: Permissions, pid: int): void; + native function getPermissionUsedRecordExecute(request: PermissionUsedRequest): PermissionUsedResponse; + native function getPermissionUsedTypeInfosExecute( + tokenId: int, permissionName: Permissions): Array; + native function setPermissionUsedRecordToggleStatusExecute(status: boolean): void; + native function getPermissionUsedRecordToggleStatusExecute(): boolean; + native function onExecute( + type: string, permissionList: Array, callback: Callback): void; + native function offExecute( + type: string, permissionList: Array, callback?: Callback): void; - interface AddPermissionUsedRecordOptions { - usedType?: PermissionUsedType; + export 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: AddPermissionUsedRecordOptions = { usedType: usedTypeTemp }; + let p = new Promise((resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + addPermissionUsedRecordExecute( + tokenID, permissionName, successCount, failCount, optionsLocal); + }); + p1.then(() => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; } - class AddPermissionUsedRecordOptionsInner implements AddPermissionUsedRecordOptions { - usedType: PermissionUsedType|undefined; + export 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 options: AddPermissionUsedRecordOptions = { usedType: PermissionUsedType.NORMAL_TYPE }; + let p = taskpool.execute((): void => { + addPermissionUsedRecordExecute(tokenID, permissionName, + successCount, failCount, options); + }); + p.then(() => { + callback(null, undefined); + },(err: Error): void=> { + callback(err as BusinessError, undefined); + }); } - class PrivacyManagerInner { - native addPermissionUsedRecordSync(tokenID: int, permissionName: Permissions, successCount: int, failCount: int, - options: AddPermissionUsedRecordOptionsInner): void; + export function stopUsingPermission(tokenID: int, permissionName: Permissions): 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 p = new Promise((resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + return stopUsingPermissionExecute(tokenID, permissionName, -1) }); + p1.then(() => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; } - interface UsedRecordDetail { - status: number; - lockScreenStatus?: number; - timestamp: number; - count?: number; - accessDuration: number; - usedType?: PermissionUsedType; + export function stopUsingPermission( + tokenID: int, permissionName: Permissions, 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 p = taskpool.execute((): void => { + return stopUsingPermissionExecute(tokenID, permissionName, -1) }); + p.then(() => { + callback(null, undefined); + },(err: Error): void=> { + callback(err as BusinessError, undefined); + }); } - interface PermissionUsedTypeInfo { - tokenId: number; - permissionName: Permissions; - usedType: PermissionUsedType; + export function stopUsingPermission(tokenID: int, permissionName: Permissions, pid?: int): 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 optionalPid: int = pid ?? -1; + let p = new Promise((resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + return stopUsingPermissionExecute(tokenID, permissionName, optionalPid) }); + p1.then(() => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; } - function addPermissionUsedRecord(tokenID: int, permissionName: Permissions, successCount: int, - failCount: int, options?: AddPermissionUsedRecordOptions): Promise { + export function startUsingPermission(tokenID: int, permissionName: Permissions): 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) - }); + return startUsingPermissionExecute(tokenID, permissionName, + -1, PermissionUsedType.NORMAL_TYPE) }); p1.then(() => { resolve(undefined); }).catch((err: Error): void => { @@ -92,23 +162,264 @@ export default namespace privacyManager { return p; } - function addPermissionUsedRecord(tokenID: int, permissionName: Permissions, successCount: int, - failCount: int, callback: AsyncCallback): void { + export function startUsingPermission( + tokenID: int, permissionName: Permissions, 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: Error): void=> { + let p = taskpool.execute((): void => { + return startUsingPermissionExecute(tokenID, permissionName, + -1, PermissionUsedType.NORMAL_TYPE) }); + p.then(() => { + callback(null, undefined); + },(err: Error): void => { callback(err as BusinessError, undefined); }) } -} \ No newline at end of file + + export function startUsingPermission(tokenID: int, permissionName: Permissions, pid?: int, + usedType?: PermissionUsedType): 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 optionalPid: int = pid ?? -1; + let optionalUsedType = usedType ?? PermissionUsedType.NORMAL_TYPE; + let p = new Promise((resolve: (v: undefined) => void, reject: (error: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + return startUsingPermissionExecute(tokenID, permissionName, + optionalPid, optionalUsedType) }); + p1.then(() => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + return p; + } + + export function getPermissionUsedRecord(request: PermissionUsedRequest): Promise { + return new Promise(( + resolve: (data: PermissionUsedResponse) => void, + reject: (error: BusinessError) => void): PermissionUsedResponse => { + let p1 = taskpool.execute((): PermissionUsedResponse => { + return getPermissionUsedRecordExecute(request); + }); + p1.then((e: NullishType):void => { + resolve(e as PermissionUsedResponse); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } + + export function getPermissionUsedRecord(request: PermissionUsedRequest, callback: AsyncCallback): void { + let p1 = taskpool.execute((): PermissionUsedResponse => { + return getPermissionUsedRecordExecute(request); + }); + p1.then((ret: NullishType): void => { + callback(null, ret as PermissionUsedResponse); + }).catch((err: Error): void => { + callback(err as BusinessError, undefined); + }) + } + + export function getPermissionUsedTypeInfos( + tokenId?: int, permissionName?: Permissions): Promise> { + let tokenIdIn = tokenId ?? 0; + let permissionNameIn = permissionName ?? ""; + return new Promise> (( + resolve: (v: Array) => void, + reject: (err: BusinessError) => void): Array => { + let p1 = taskpool.execute((): Array => { + return getPermissionUsedTypeInfosExecute(tokenIdIn, permissionNameIn); + }); + p1.then((e: NullishType): void => { + resolve(e as Array); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } + + export function setPermissionUsedRecordToggleStatus(status: boolean): Promise { + return new Promise ( + (resolve: (v: undefined) => void, reject: (err: BusinessError) => void): void => { + let p1 = taskpool.execute((): void => { + setPermissionUsedRecordToggleStatusExecute(status); + }); + p1.then((e: NullishType): void => { + resolve(undefined); + }).catch((err: Error): void => { + reject(err as BusinessError); + }); + }); + } + + export function getPermissionUsedRecordToggleStatus(): Promise{ + return new Promise ( + (resolve: (v: boolean) => void, reject: (err: BusinessError) => void): void => { + let p1 = taskpool.execute((): boolean => { + return getPermissionUsedRecordToggleStatusExecute(); + }); + p1.then((e: NullishType): void => { + resolve(e as boolean); + }).catch((err: Error): void =>{ + reject(err as BusinessError); + }); + }); + } + + export function on( + type: string, permissionList: Array, callback: Callback): void { + onExecute(type, permissionList, callback); + } + + export function off( + type: string, permissionList: Array, callback?: Callback): void { + offExecute(type, permissionList, callback); + } + + export enum PermissionUsedType { + NORMAL_TYPE = 0, + PICKER_TYPE = 1, + SECURITY_COMPONENT_TYPE = 2 + }; + + export enum PermissionActiveStatus { + PERM_INACTIVE = 0, + PERM_ACTIVE_IN_FOREGROUND = 1, + PERM_ACTIVE_IN_BACKGROUND = 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, + }; + + export enum PermissionUsageFlag { + FLAG_PERMISSION_USAGE_SUMMARY = 0, + FLAG_PERMISSION_USAGE_DETAIL = 1 + }; + + export interface AddPermissionUsedRecordOptions { + usedType?: PermissionUsedType; + } + + export interface PermissionUsedTypeInfo { + tokenId: int; + permissionName: Permissions; + usedType: PermissionUsedType; + } + + class PermissionUsedTypeInfoInner implements PermissionUsedTypeInfo { + tokenId: int; + permissionName: Permissions; + usedType: PermissionUsedType; + } + + export 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; + } + + export interface PermissionUsedRequest { + tokenId?: int; + isRemote?: boolean; + deviceId?: string; + bundleName?: string; + permissionNames?: Array; + beginTime?: long; + endTime?: long; + flag: PermissionUsageFlag; + } + + export interface UsedRecordDetail { + status: int; + lockScreenStatus?: int; + timestamp: long; + count?: int; + accessDuration: long; + usedType?: PermissionUsedType; + } + + class UsedRecordDetailInner implements UsedRecordDetail { + status: int; + lockScreenStatus?: int; + timestamp: long; + count?: int; + accessDuration: long; + usedType?: PermissionUsedType; + } + + interface PermissionUsedRecord { + permissionName: Permissions; + accessCount: int; + rejectCount: int; + lastAccessTime: long; + lastRejectTime: long; + lastAccessDuration: long; + accessRecords: Array; + rejectRecords: Array; + } + + class PermissionUsedRecordInner implements PermissionUsedRecord { + permissionName: Permissions; + accessCount: int; + rejectCount: int; + lastAccessTime: long; + lastRejectTime: long; + lastAccessDuration: long; + accessRecords: Array; + rejectRecords: Array; + } + + export interface BundleUsedRecord{ + tokenId: int; + isRemote: boolean; + deviceId: string; + bundleName: string; + permissionRecords: Array; + } + + class BundleUsedRecordInner implements BundleUsedRecord { + tokenId: int; + isRemote: boolean; + deviceId: string; + bundleName: string; + permissionRecords: Array; + } + + export interface PermissionUsedResponse { + beginTime: long; + endTime: long; + bundleRecords: Array; + } + + class PermissionUsedResponseInner implements PermissionUsedResponse { + beginTime: long; + endTime: long; + bundleRecords: Array; + } +} 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 37f362595276d16d5abc0efa935ebcc320a1165e..9aadcd341fff766e683074aff25c02963a1415ba 100644 --- a/frameworks/ets/ani/privacy/src/privacy_manager.cpp +++ b/frameworks/ets/ani/privacy/src/privacy_manager.cpp @@ -13,18 +13,31 @@ * 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; +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) { @@ -77,103 +90,847 @@ static int32_t GetStsErrorCode(int32_t errCode) return stsCode; } -static void AddPermissionUsedRecordSync(ani_env* env, const AddPermParamInfo& info) +static void AddPermissionUsedRecordExecute([[maybe_unused]] ani_env* env, + ani_int aniTokenID, ani_string aniPermission, ani_int successCount, ani_int failCount, ani_object options) { + if (env == nullptr) { + return; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permission = ParseAniString(env, aniPermission); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permission))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) is invalid.", + tokenID, permission.c_str()); + return; + } + + int32_t usedType = 0; + if (!GetEnumProperty(env, options, "usedType", usedType)) { + return; + } + AddPermParamInfo info; + info.tokenId = static_cast(tokenID); + info.permissionName = permission; + info.successCount = successCount; + info.failCount = failCount; + info.type = static_cast(usedType); 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) +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 = isSameThread ? env_ : GetCurrentEnv(vm_); + + 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 ani_object ConvertActiveChangeResponse(ani_env* env, const ActiveChangeResponse& result) +{ + ani_object aniObject = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/ActiveChangeResponseInner;"); + if (aniObject == nullptr) { + return nullptr; + } + + // set callingTokenID?: int optional parameter callingTokenID need box as a object + SetIntProperty(env, aniObject, ACTIVE_CHANGE_FIELD_CALLING_TOKEN_ID, static_cast(result.callingTokenID)); + + // set tokenID: int + SetIntProperty(env, aniObject, ACTIVE_CHANGE_FIELD_TOKEN_ID, static_cast(result.tokenID)); + + // set permissionName: string + SetStringProperty(env, aniObject, ACTIVE_CHANGE_FIELD_PERMISSION_NAME, result.permissionName); + + // set deviceId: string + SetStringProperty(env, aniObject, ACTIVE_CHANGE_FIELD_DEVICE_ID, result.deviceId); + + // set activeStatus: PermissionActiveStatus + const char* activeStatusDes = "L@ohos/privacyManager/privacyManager/PermissionActiveStatus;"; + SetEnumProperty( + env, aniObject, activeStatusDes, ACTIVE_CHANGE_FIELD_ACTIVE_STATUS, static_cast(result.type)); + + // set usedType?: PermissionUsedType + const char* permUsedTypeDes = "L@ohos/privacyManager/privacyManager/PermissionUsedType;"; + SetEnumProperty( + env, aniObject, permUsedTypeDes, ACTIVE_CHANGE_FIELD_USED_TYPE, static_cast(result.usedType)); + return aniObject; +} + +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); + if (aniObject == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Convert object is null."); + return; + } + 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 = ParseAniString(context->env, static_cast(aniType)); + std::vector permList = ParseAniStringVector(context->env, aniArray); + std::sort(permList.begin(), permList.end()); + + bool hasCallback = true; + if (!isReg) { + hasCallback = !AniIsRefUndefined(context->env, aniCallback); + } + + ani_ref callback = nullptr; // callback: the third parameter is function + if (hasCallback) { + if (!AniParseCallback(context->env, aniCallback, callback)) { + BusinessErrorAni::ThrowParameterTypeError( + 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) { - 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); + + RegisterPermActiveChangeContext* context = new (std::nothrow) RegisterPermActiveChangeContext(); + if (context == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Failed to allocate memory for RegisterPermActiveChangeContext!"); 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); + context->env = env; + std::unique_ptr callbackPtr {context}; + + if (!ParseInputToRegister(aniType, aniArray, callback, context, true)) { 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); + + if (IsExistRegister(context)) { + std::string errMsg = GetErrorMessage( + STS_ERROR_NOT_USE_TOGETHER, "The API reuses the same input. The subscriber already exists."); + BusinessErrorAni::ThrowError( + env, STS_ERROR_NOT_USE_TOGETHER, GetErrorMessage(STS_ERROR_NOT_USE_TOGETHER, errMsg)); 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); + + 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; } - 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"); + + { + 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); } - AddPermParamInfo info; - info.tokenId = static_cast(tokenID); - info.permissionName = outputPermissionName; - info.successCount = successCount; - info.failCount = failCount; - info.type = static_cast(usedType); - AddPermissionUsedRecordSync(env, info); + + callbackPtr.release(); + ACCESSTOKEN_LOG_INFO(LABEL, "RegisterPermActiveStatusCallback success!"); + return; } -extern "C" { -ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) +static bool FindAndGetSubscriber(const RegisterPermActiveChangeContext* context, + std::vector& batchPermActiveChangeSubscribers) { - if (vm == nullptr || result == nullptr) { - ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr vm or result"); - return ANI_INVALID_ARGS; + std::vector targetPermList = context->permissionList; + std::lock_guard lock(g_mutex); + bool callbackEqual; + ani_ref callbackRef = context->callbackRef; + bool isUndef = AniIsRefUndefined(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; + } + } } - 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; + return !batchPermActiveChangeSubscribers.empty(); +} + +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) { + return; + } + + RegisterPermActiveChangeContext* context = new (std::nothrow) RegisterPermActiveChangeContext(); + if (context == nullptr) { + return; + } + context->env = env; + std::unique_ptr callbackPtr {context}; + if (!ParseInputToRegister(aniType, aniArray, callback, context, false)) { + return; } + std::vector batchPermActiveChangeSubscribers; + if (!FindAndGetSubscriber(context, batchPermActiveChangeSubscribers)) { + std::string errMsg = GetErrorMessage( + STS_ERROR_NOT_USE_TOGETHER, "The API is not used in pair with 'on'. The subscriber does not exist."); + BusinessErrorAni::ThrowError( + env, STS_ERROR_NOT_USE_TOGETHER, GetErrorMessage(STS_ERROR_NOT_USE_TOGETHER, errMsg)); + return; + } + + for (const auto& item : batchPermActiveChangeSubscribers) { + int32_t result = PrivacyKit::UnRegisterPermActiveStatusCallback(item->subscriber); + if (result == RET_SUCCESS) { + DeleteRegisterInVector(item); + } else { + ACCESSTOKEN_LOG_ERROR(LABEL, "UnregisterPermActiveChangeCompleted failed"); + int32_t stsCode = GetStsErrorCode(result); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + } + } + ACCESSTOKEN_LOG_INFO(LABEL, "UnRegisterPermActiveStatusCallback success!"); + return; +} + +static void StopUsingPermissionExecute( + [[maybe_unused]] ani_env* env, ani_int aniTokenID, ani_string aniPermission, ani_int pid) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "StopUsingPermissionExecute begin."); if (env == nullptr) { - ACCESSTOKEN_LOG_ERROR(LABEL, "nullptr env"); - return ANI_NOT_FOUND; + ACCESSTOKEN_LOG_ERROR(LABEL, "env null"); + return; + } + + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permission = ParseAniString(env, aniPermission); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permission))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) is invalid.", + tokenID, permission.c_str()); + return; } - 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; + ACCESSTOKEN_LOG_INFO(LABEL, "PermissionName : %{public}s, tokenID : %{public}u, pid : %{public}d", + permission.c_str(), tokenID, pid); + + auto retCode = PrivacyKit::StopUsingPermission(tokenID, permission, pid); + if (retCode != RET_SUCCESS) { + int32_t stsCode = GetStsErrorCode(retCode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); } +} - std::array methods = { - ani_native_function { "addPermissionUsedRecordSync", - "ILstd/core/String;IIL@ohos/privacyManager/privacyManager/AddPermissionUsedRecordOptionsInner;:V", - reinterpret_cast(AddPermissionUsedRecord) }, - }; +static void StartUsingPermissionExecute([[maybe_unused]] ani_env* env, + ani_int aniTokenID, ani_string aniPermission, ani_int pid, PermissionUsedType usedType) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "StartUsingPermissionExecute begin."); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env null"); + return; + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permission = ParseAniString(env, aniPermission); + if ((!BusinessErrorAni::ValidateTokenIDdWithThrowError(env, tokenID)) || + (!BusinessErrorAni::ValidatePermissionWithThrowError(env, permission))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "TokenId(%{public}u) or Permission(%{public}s) is invalid.", + tokenID, permission.c_str()); + return; + } + + ACCESSTOKEN_LOG_INFO(LABEL, "PermissionName : %{public}s, tokenID : %{public}u, pid : %{public}d", + permission.c_str(), tokenID, pid); - 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; + auto retCode = PrivacyKit::StartUsingPermission(tokenID, permission, pid, usedType); + if (retCode != RET_SUCCESS) { + int32_t stsCode = GetStsErrorCode(retCode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + } +} + +static ani_object ConvertSingleUsedRecordDetail(ani_env* env, const UsedRecordDetail& record) +{ + ani_object arrayObj = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/UsedRecordDetailInner;"); + if (arrayObj == nullptr) { + return nullptr; + } + if (!SetIntProperty(env, arrayObj, "status", record.status)) { + return nullptr; + } + if (!SetOptionalIntProperty(env, arrayObj, "lockScreenStatus", record.lockScreenStatus)) { + return nullptr; + } + if (!SetLongProperty(env, arrayObj, "timestamp", record.timestamp)) { + return nullptr; + } + if (!SetLongProperty(env, arrayObj, "accessDuration", record.accessDuration)) { + return nullptr; + } + if (!SetOptionalIntProperty(env, arrayObj, "count", record.count)) { + return nullptr; + } + if (!SetEnumProperty(env, arrayObj, "L@ohos/privacyManager/privacyManager/PermissionUsedType;", "usedType", + static_cast(record.type))) { + return nullptr; + } + return arrayObj; +} + +static ani_object ConvertUsedRecordDetails(ani_env* env, const std::vector& details) +{ + ani_object arrayObj = CreateArrayObject(env, details.size()); + if (arrayObj == nullptr) { + return nullptr; + } + ani_size index = 0; + for (const auto& record: details) { + ani_ref aniRecord = ConvertSingleUsedRecordDetail(env, record); + if (aniRecord == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Null aniRecord."); + break; + } + ani_status status = env->Object_CallMethodByName_Void( + arrayObj, "$_set", "ILstd/core/Object;:V", index, aniRecord); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "status : %{public}d", status); + break; + } + ++index; + } + return arrayObj; +} + +static ani_object ConvertSinglePermissionRecord(ani_env* env, const PermissionUsedRecord& record) +{ + ani_object arrayObj = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/PermissionUsedRecordInner;"); + if (arrayObj == nullptr) { + return nullptr; + } + if (!SetStringProperty(env, arrayObj, "permissionName", record.permissionName)) { + return nullptr; + } + if (!SetIntProperty(env, arrayObj, "accessCount", record.accessCount)) { + return nullptr; + } + if (!SetIntProperty(env, arrayObj, "rejectCount", record.rejectCount)) { + return nullptr; + } + if (!SetLongProperty(env, arrayObj, "lastAccessTime", record.lastAccessTime)) { + return nullptr; + } + if (!SetLongProperty(env, arrayObj, "lastRejectTime", record.lastRejectTime)) { + return nullptr; + } + if (!SetLongProperty(env, arrayObj, "lastAccessDuration", record.lastAccessDuration)) { + return nullptr; + } + if (!SetRefProperty(env, arrayObj, "accessRecords", ConvertUsedRecordDetails(env, record.accessRecords))) { + return nullptr; + } + if (!SetRefProperty(env, arrayObj, "rejectRecords", ConvertUsedRecordDetails(env, record.rejectRecords))) { + return nullptr; + } + return arrayObj; +} + +static ani_ref ConvertPermissionRecords(ani_env* env, const std::vector& permRecords) +{ + ani_object arrayObj = CreateArrayObject(env, permRecords.size()); + if (arrayObj == nullptr) { + return nullptr; + } + ani_size index = 0; + for (const auto& record: permRecords) { + ani_ref aniRecord = ConvertSinglePermissionRecord(env, record); + if (aniRecord == nullptr) { + break; + } + ani_status status = env->Object_CallMethodByName_Void( + arrayObj, "$_set", "ILstd/core/Object;:V", index, aniRecord); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR( + LABEL, "Set permission record fail, status: %{public}d.", static_cast(status)); + break; + } + ++index; + } + return arrayObj; +} + +static ani_object ConvertBundleUsedRecord(ani_env* env, const BundleUsedRecord& record) +{ + ani_object aniRecord = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/BundleUsedRecordInner;"); + if (aniRecord == nullptr) { + return nullptr; + } + if (!SetIntProperty(env, aniRecord, "tokenId", static_cast(record.tokenId))) { + return nullptr; + } + if (!SetBoolProperty(env, aniRecord, "isRemote", record.isRemote)) { + return nullptr; + } + + if (!SetStringProperty(env, aniRecord, "deviceId", record.deviceId)) { + return nullptr; + } + + if (!SetStringProperty(env, aniRecord, "bundleName", record.bundleName)) { + return nullptr; + } + + if (!SetRefProperty(env, aniRecord, "permissionRecords", ConvertPermissionRecords(env, record.permissionRecords))) { + return nullptr; + } + return aniRecord; +} + +static ani_object ConvertBundleUsedRecords(ani_env* env, const std::vector& bundleRecord) +{ + ani_object arrayObj = CreateArrayObject(env, bundleRecord.size()); + if (arrayObj == nullptr) { + return nullptr; + } + ani_size index = 0; + for (const auto& record : bundleRecord) { + ani_ref aniRecord = ConvertBundleUsedRecord(env, record); + if (aniRecord == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "aniRecord is null."); + continue; + } + ani_status status = env->Object_CallMethodByName_Void( + arrayObj, "$_set", "ILstd/core/Object;:V", index, aniRecord); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set bundle record fail, status: %{public}d.", static_cast(status)); + continue; + } + ++index; + } + return arrayObj; +} + +static ani_object ProcessRecordResult(ani_env* env, const PermissionUsedResult& result) +{ + ani_object aObject = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/PermissionUsedResponseInner;"); + if (aObject == nullptr) { + return nullptr; + } + if (!SetLongProperty(env, aObject, "beginTime", result.beginTimeMillis)) { + return nullptr; + } + if (!SetLongProperty(env, aObject, "endTime", result.endTimeMillis)) { + return nullptr; + } + + if (!SetRefProperty(env, aObject, "bundleRecords", ConvertBundleUsedRecords(env, result.bundleRecords))) { + return nullptr; + } + return aObject; +} + +static bool ParseRequest(ani_env* env, const ani_object& aniRequest, PermissionUsedRequest& request) +{ + ani_class requestClass; + if (ANI_OK != env->FindClass("L@ohos/privacyManager/privacyManager/PermissionUsedRequest;", &requestClass)) { + return false; + } + ani_boolean isRequestObject = false; + if (ANI_OK != env->Object_InstanceOf(static_cast(aniRequest), requestClass, &isRequestObject)) { + return false; + } + if (!isRequestObject) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Object is not request type."); + return false; + } + + int32_t value; + if (!GetIntProperty(env, aniRequest, "tokenId", value)) { + return false; + } + request.tokenId = static_cast(value); + + if (!GetBoolProperty(env, aniRequest, "isRemote", request.isRemote)) { + return false; + } + + if (!GetStringProperty(env, aniRequest, "deviceId", request.deviceId)) { + return false; + } + + if (!GetStringProperty(env, aniRequest, "bundleName", request.bundleName)) { + return false; + } + + if (!GetLongProperty(env, aniRequest, "beginTime", request.beginTimeMillis)) { + return false; + } + + if (!GetLongProperty(env, aniRequest, "endTime", request.endTimeMillis)) { + return false; + } + + if (!GetStringVecProperty(env, aniRequest, "permissionNames", request.permissionList)) { + return false; + } + + int32_t flag; + if (!GetEnumProperty(env, aniRequest, "flag", flag)) { + return false; + } + request.flag = static_cast(flag); + return true; +} + +static ani_object GetPermissionUsedRecordExecute([[maybe_unused]] ani_env* env, ani_object aniRequest) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "GetPermissionUsedRecordExecute Call"); + if (env == nullptr || aniRequest == nullptr) { + return nullptr; + } + + PermissionUsedRequest request; + if (!ParseRequest(env, aniRequest, request)) { + return nullptr; + } + + PermissionUsedResult retResult; + int32_t errcode = PrivacyKit::GetPermissionUsedRecords(request, retResult); + if (errcode != ANI_OK) { + int32_t stsCode = GetStsErrorCode(errcode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + return nullptr; + } + return ProcessRecordResult(env, retResult); +} + +static ani_object ConvertPermissionUsedTypeInfo(ani_env *env, const PermissionUsedTypeInfo& info) +{ + ani_object aObject = CreateClassObject(env, "L@ohos/privacyManager/privacyManager/PermissionUsedTypeInfoInner;"); + if (aObject == nullptr) { + return nullptr; + } + if (!SetIntProperty(env, aObject, "tokenId", info.tokenId)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "tokenId set fail."); + return nullptr; + } + if (!SetStringProperty(env, aObject, "permissionName", info.permissionName)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "PermissionName set fail."); + return nullptr; + } + const char* permUsedTypeDes = "L@ohos/privacyManager/privacyManager/PermissionUsedType;"; + if (!SetEnumProperty(env, aObject, permUsedTypeDes, "usedType", static_cast(info.type))) { + ACCESSTOKEN_LOG_ERROR(LABEL, "UsedType set fail."); + return nullptr; + } + return aObject; +} + +static ani_ref ConvertPermissionUsedTypeInfos(ani_env* env, const std::vector& infos) +{ + ani_object arrayObj = CreateArrayObject(env, infos.size()); + if (arrayObj == nullptr) { + return nullptr; + } + ani_size index = 0; + for (const auto& type : infos) { + ani_ref aniType = ConvertPermissionUsedTypeInfo(env, type); + if (aniType == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "aniType is null."); + continue; + } + ani_status status = env->Object_CallMethodByName_Void( + arrayObj, "$_set", "ILstd/core/Object;:V", index, aniType); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Set type fail, status: %{public}d.", static_cast(status)); + continue; + } + ++index; + } + return arrayObj; +} + +static ani_ref GetPermissionUsedTypeInfosExecute([[maybe_unused]] ani_env* env, + ani_int aniTokenID, ani_string aniPermission) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null."); + } + AccessTokenID tokenID = static_cast(aniTokenID); + std::string permission = ParseAniString(env, static_cast(aniPermission)); + std::vector typeInfos; + int32_t retCode = PrivacyKit::GetPermissionUsedTypeInfos(tokenID, permission, typeInfos); + if (retCode != RET_SUCCESS) { + BusinessErrorAni::ThrowError(env, GetStsErrorCode(retCode), + GetErrorMessage(GetStsErrorCode(retCode))); + return nullptr; + } + return ConvertPermissionUsedTypeInfos(env, typeInfos); +} + +static void SetPermissionUsedRecordToggleStatusExecute([[maybe_unused]] ani_env* env, ani_boolean status) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null."); + return; + } + int32_t userID = 0; + int32_t retCode = PrivacyKit::SetPermissionUsedRecordToggleStatus(userID, status); + if (retCode != RET_SUCCESS) { + int32_t stsCode = BusinessErrorAni::GetStsErrorCode(retCode); + BusinessErrorAni::ThrowError(env, stsCode, GetErrorMessage(stsCode)); + } +} + +static ani_boolean GetPermissionUsedRecordToggleStatusExecute([[maybe_unused]] ani_env* env) +{ + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null."); + return false; + } + int32_t userID = 0; + bool isToggleStatus = false; + int32_t retCode = PrivacyKit::GetPermissionUsedRecordToggleStatus(userID, isToggleStatus); + if (retCode != RET_SUCCESS) { + BusinessErrorAni::ThrowError(env, GetStsErrorCode(retCode), GetErrorMessage(GetStsErrorCode(retCode))); + return false; + } + return isToggleStatus; +} + +void InitPrivacyFunction(ani_env *env) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "InitPrivacyFunction call"); + if (env == nullptr) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Env is null."); + return; + } + if (env->ResetError() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ResetError failed."); + } + + ani_namespace ns; + if (ANI_OK != env->FindNamespace("L@ohos/privacyManager/privacyManager;", &ns)) { + ACCESSTOKEN_LOG_ERROR(LABEL, "FindNamespace privacyManager failed."); + return; + } + + // namespace method input param without ani_object + std::array nsMethods = { + ani_native_function {"addPermissionUsedRecordExecute", nullptr, + reinterpret_cast(AddPermissionUsedRecordExecute)}, + ani_native_function {"startUsingPermissionExecute", nullptr, + reinterpret_cast(StartUsingPermissionExecute)}, + ani_native_function {"stopUsingPermissionExecute", nullptr, + reinterpret_cast(StopUsingPermissionExecute)}, + ani_native_function {"getPermissionUsedRecordExecute", nullptr, + reinterpret_cast(GetPermissionUsedRecordExecute)}, + ani_native_function {"getPermissionUsedTypeInfosExecute", nullptr, + reinterpret_cast(GetPermissionUsedTypeInfosExecute)}, + ani_native_function {"setPermissionUsedRecordToggleStatusExecute", + nullptr, reinterpret_cast(SetPermissionUsedRecordToggleStatusExecute)}, + ani_native_function {"getPermissionUsedRecordToggleStatusExecute", + nullptr, reinterpret_cast(GetPermissionUsedRecordToggleStatusExecute)}, + ani_native_function {"onExecute", nullptr, reinterpret_cast(RegisterPermActiveStatusCallback)}, + ani_native_function {"offExecute", nullptr, reinterpret_cast(UnRegisterPermActiveStatusCallback)}, }; + ani_status status = env->Namespace_BindNativeFunctions(ns, nsMethods.data(), nsMethods.size()); + if (status != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "Namespace_BindNativeFunctions failed status : %{public}d.", status); + } + if (env->ResetError() != ANI_OK) { + ACCESSTOKEN_LOG_ERROR(LABEL, "ResetError failed."); + } + ACCESSTOKEN_LOG_INFO(LABEL, "InitPrivacyFunction end"); +} +extern "C" { +ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result) +{ + ACCESSTOKEN_LOG_INFO(LABEL, "ANI_Constructor begin"); + 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; + } + InitPrivacyFunction(env); *result = ANI_VERSION_1; + ACCESSTOKEN_LOG_INFO(LABEL, "ANI_Constructor end"); return ANI_OK; } }