1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { BaseModel } from '../common/base/BaseModel'; 17import { Want } from '@kit.AbilityKit'; 18import { GlobalContext } from '../common/utils/globalContext'; 19import { Permission, PermissionGroup, ButtonStatus, PermissionOption } from '../common/model/definition'; 20import { Log, titleTrim, supportPermission } from '../common/utils/utils'; 21import { 22 Property, CallerAppInfo, PermissionGroupConfig, PermissionWithOption, optionAndState 23} from '../common/model/typedef'; 24import { PermissionGroupManager } from '../common/permissionGroupManager/PermissionGroupManager'; 25import rpc from '@ohos.rpc'; 26import window from '@ohos.window'; 27import display from '@ohos.display'; 28import pasteboard from '@ohos.pasteboard'; 29import common from '@ohos.app.ability.common'; 30import osAccount from '@ohos.account.osAccount'; 31import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; 32import bundleManager from '@ohos.bundle.bundleManager'; 33import bundleResourceManager from '@ohos.bundle.bundleResourceManager'; 34import Constants from '../common/utils/constant'; 35 36const BG_COLOR = '#00000000'; 37const THIS_TIME_FLAG = [ButtonStatus.THIS_TIME_ONLY, ButtonStatus.ALLOW_THIS_TIME]; 38const DEFAULT_CALLER_APP_INFO: CallerAppInfo = { 39 bundleName: '', 40 tokenId: -1, 41 reqPerms: [], 42 reqPermsState: [], 43 reqPermsDetails: [], 44 proxy: {} as rpc.RemoteObject, 45 grantResult: [], 46 groupWithPermission: new Map<PermissionGroup, Set<Permission>>() 47} 48 49export class GrantDialogModel extends BaseModel { 50 51 /** 52 * 从want解析调用方信息和权限申请情况 53 * @param want 54 * return 应用包信息 55 */ 56 public async getCallerAppInfo(want: Want): Promise<CallerAppInfo> { 57 if (!want.parameters) { 58 Log.error(`want.parameters is undefined!`); 59 return DEFAULT_CALLER_APP_INFO; 60 } 61 let bundleName: string = want.parameters['ohos.aafwk.param.callerBundleName'] as string ?? ''; 62 let tokenId: number = want.parameters['ohos.aafwk.param.callerToken'] as number ?? -1; 63 let reqPerms: Permission[] = [...(want.parameters['ohos.user.grant.permission'] as Permission[])] ?? []; 64 let reqPermsState: number[] = [...(want.parameters['ohos.user.grant.permission.state'] as number[])] ?? []; 65 let reqPermsDetails: bundleManager.ReqPermissionDetail[] = []; 66 let callback: Property = want.parameters['ohos.ability.params.callback'] as Property; 67 let proxy: rpc.RemoteObject = callback.value as rpc.RemoteObject; 68 try { 69 let userId: number = await this.getUserId(); 70 let flag: number = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; 71 let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoSync(bundleName, flag, userId); 72 reqPermsDetails = bundleInfo.reqPermissionDetails ?? []; 73 } catch (error) { 74 Log.error(`getBundleInfo faild, code: ${error.code}, message: ${error.message}.`); 75 } 76 let grantResult: number[] = this.initGrantResult(reqPermsState); 77 await this.preProcessPermission(reqPerms, reqPermsState, tokenId, grantResult); 78 let callerAppInfo: CallerAppInfo = { 79 bundleName, 80 tokenId, 81 reqPerms, 82 reqPermsState, 83 reqPermsDetails, 84 proxy, 85 grantResult, 86 groupWithPermission: this.getGroupWithPermission(reqPerms, reqPermsState) 87 } 88 return callerAppInfo; 89 } 90 91 /** 92 * 获取当前用户id 93 * return 用户id 94 */ 95 private async getUserId(): Promise<number> { 96 let userId: number = 0; 97 try { 98 const accountManager: osAccount.AccountManager = osAccount.getAccountManager(); 99 userId = await accountManager.getForegroundOsAccountLocalId(); 100 } catch (error) { 101 Log.error(`getForegroundOsAccountLocalId faild, code: ${error.code}, message: ${error.message}.`); 102 } 103 return userId; 104 } 105 106 /** 107 * 初始化授权状态列表 108 * @param reqPermsState 权限状态 109 * return 授权状态列表 110 */ 111 private initGrantResult(reqPermsState: number[]): number[] { 112 let grantResult: number[] = new Array(reqPermsState.length).fill(abilityAccessCtrl.GrantStatus.PERMISSION_DENIED); 113 reqPermsState.forEach((state, idx) => { 114 if (state === Constants.PASS_OPER) { 115 grantResult[idx] = abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; 116 } 117 }) 118 return grantResult; 119 } 120 121 /** 122 * 预处理待授权权限 123 * @param reqPerms 授权权限 124 * @param reqPermsState 权限状态 125 * @param tokenId 应用token 126 * @param grantResult 授权状态 127 * return 128 */ 129 private async preProcessPermission( 130 reqPerms: Permission[], reqPermsState: number[], tokenId: number, grantResult: number[] 131 ): Promise<void> { 132 let allGroupConfigs: PermissionGroupConfig[] = PermissionGroupManager.getInstance().getAllGroupConfigs(); 133 for (let index = 0; index < reqPerms.length; index++) { 134 if (!this.needHandlePermission(reqPerms[index], reqPermsState[index])) { 135 continue; 136 } 137 let group = PermissionGroupManager.getInstance().getGroupNameByPermission(reqPerms[index], allGroupConfigs); 138 let result = await PermissionGroupManager.getInstance().preProcessPermission( 139 reqPerms[index], group as PermissionGroup, tokenId 140 ); 141 this.preProcessState(reqPerms, reqPermsState, grantResult, result); 142 } 143 } 144 145 /** 146 * 初始化授权状态列表 147 * @param reqPerms 授权权限 148 * @param reqPermsState 权限状态 149 * @param grantResult 授权状态 150 * @param result 授权结果 151 * return 152 */ 153 private preProcessState( 154 reqPerms: Permission[], reqPermsState: number[], grantResult: number[], result: PermissionWithOption[] 155 ): void { 156 result.forEach(permissionWithOption => { 157 let needRefresh = reqPerms.find(perm => perm === permissionWithOption.permission); 158 if (!needRefresh) { 159 return; 160 } 161 for (let index = 0; index < reqPerms.length; index++) { 162 if (reqPerms[index] === permissionWithOption.permission) { 163 reqPermsState[index] = permissionWithOption.permissionOption === PermissionOption.GRANT ? 164 Constants.PASS_OPER : Constants.SETTING_OPER; 165 grantResult[index] = reqPermsState[index]; 166 } 167 } 168 }) 169 } 170 171 /** 172 * 获取待授权权限及其所属权限组 173 * @param reqPerms 权限 174 * @param reqPermsState 权限状态 175 * return key:权限组,value:待授权权限 176 */ 177 private getGroupWithPermission( 178 reqPerms: Permission[], reqPermsState: number[] 179 ): Map<PermissionGroup, Set<Permission>> { 180 if (reqPerms.length !== reqPermsState.length) { 181 return new Map<PermissionGroup, Set<Permission>>(); 182 } 183 let groupWithPermission: Map<PermissionGroup, Set<Permission>> = new Map(); 184 let allGroupConfigs: PermissionGroupConfig[] = PermissionGroupManager.getInstance().getAllGroupConfigs(); 185 for (let index = 0; index < reqPerms.length; index++) { 186 let perm = reqPerms[index]; 187 if (this.needHandlePermission(perm, reqPermsState[index])) { 188 let groupName = PermissionGroupManager.getInstance().getGroupNameByPermission(perm, allGroupConfigs); 189 if (!groupName) { 190 continue; 191 } 192 if (!groupWithPermission.has(groupName)) { 193 groupWithPermission.set(groupName, new Set([perm])); 194 } else { 195 groupWithPermission.get(groupName)?.add(perm); 196 } 197 } 198 } 199 return groupWithPermission; 200 } 201 202 /** 203 * 判断权限是否为当前设备所支持权限 且状态为待授权 204 * @param permission 权限 205 * @param state 权限初始状态 206 * return 207 */ 208 private needHandlePermission(permission: Permission, state: number): boolean { 209 let supportPermissions = supportPermission(); 210 if (supportPermissions.includes(permission) && state === Constants.DYNAMIC_OPER) { 211 return true; 212 } else { 213 return false; 214 } 215 } 216 217 /** 218 * 创建模态窗口 219 * @param context ServiceExtensionContext 220 * @param name 窗口名 221 * @param rect 窗口参数 222 * @param want 223 * return 224 */ 225 public async createWindow( 226 context: common.ServiceExtensionContext, name: string, rect: display.Rect, want: Want 227 ): Promise<void> { 228 try { 229 let configuration: window.Configuration = { 230 ctx: context, 231 name, 232 windowType: window.WindowType.TYPE_DIALOG 233 } 234 const win = await window.createWindow(configuration); 235 Log.info('createWindow end.'); 236 let callerAppInfo: CallerAppInfo = await this.getCallerAppInfo(want); 237 let property: Record<string, Object> = { 'win': win, 'callerAppInfo': callerAppInfo }; 238 let storage: LocalStorage = new LocalStorage(property); 239 await this.BindDialogTarget(context, win, want); 240 Log.info('bindDialogTarget end.'); 241 await win.moveWindowTo(rect.left, rect.top); 242 Log.info('moveWindowTo end.'); 243 await win.resize(rect.width, rect.height); 244 Log.info('resize end.'); 245 await win.loadContent('pages/dialogPlus', storage); 246 win.setWindowBackgroundColor(BG_COLOR); 247 await win.showWindow(); 248 Log.info('showWindow end.'); 249 let windowNum: number = GlobalContext.getContext().increaseAndGetWindowNum(); 250 Log.info(`global windowNum is: ${windowNum}.`); 251 } catch (error) { 252 Log.error(`window create faild, code: ${error.code}, message: ${error.message}.`); 253 } 254 } 255 256 /** 257 * 绑定模态窗口 258 * @param context ServiceExtensionContext 259 * @param win 窗口对象 260 * @param want 261 * return 262 */ 263 private async BindDialogTarget( 264 context: common.ServiceExtensionContext, win: window.Window, want: Want 265 ): Promise<void> { 266 let callback = want.parameters?.['ohos.ability.params.callback'] as Property; 267 let proxy = callback.value as rpc.RemoteObject; 268 let token = want.parameters?.['ohos.ability.params.token'] as Property; 269 win.bindDialogTarget(token.value, () => { 270 let option = new rpc.MessageOption(); 271 let data = new rpc.MessageSequence(); 272 let reply = new rpc.MessageSequence(); 273 try { 274 data.writeInterfaceToken(Constants.ACCESS_TOKEN); 275 proxy.sendMessageRequest(Constants.RESULT_CODE_1, data, reply, option); 276 } catch (err) { 277 Log.error(`write result failed: ${JSON.stringify(err)}`); 278 } finally { 279 data.reclaim(); 280 reply.reclaim(); 281 } 282 let windowNum: number = GlobalContext.getContext().decreaseAndGetWindowNum(); 283 Log.info(`global windowNum is: ${windowNum}.`); 284 win.destroyWindow(); 285 windowNum <= 0 ? context.terminateSelf() : null; 286 }); 287 } 288 289 /** 290 * 获取剪贴板信息 291 * return 剪贴板内容来源应用名 292 */ 293 public getPasteBoardInfo(): string { 294 let systemPasteboardDataSource: string = ''; 295 try { 296 let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard(); 297 let data = systemPasteboard.getDataSource(); 298 systemPasteboardDataSource = data || ''; 299 } catch (error) { 300 Log.error(`getSystemPasteboard faild, code: ${error.code}, message: ${error.message}.`); 301 } 302 Log.info(`systemPasteboard dataSource: ${systemPasteboardDataSource}.`); 303 return systemPasteboardDataSource; 304 } 305 306 /** 307 * 获取应用名 308 * @param bundleName 包名 309 * return 应用名 310 */ 311 public getAppName(bundleName: string): string { 312 let resourceFlag: bundleResourceManager.ResourceFlag = bundleResourceManager.ResourceFlag.GET_RESOURCE_INFO_ALL; 313 let appName: string = ''; 314 try { 315 let resourceInfo: bundleResourceManager.BundleResourceInfo = 316 bundleResourceManager.getBundleResourceInfo(bundleName, resourceFlag); 317 appName = resourceInfo?.label || ''; 318 } catch (error) { 319 Log.error(`getBundleResourceInfo faild, code: ${error.code}, message: ${error.message}.`); 320 appName = 'Application'; 321 } 322 Log.info(`appName: ${appName}.`); 323 return titleTrim(appName); 324 } 325 326 /** 327 * 初始化位置权限组授权信息 328 * @param callerAppInfo 调用方信息 329 * return flag: 0:不授权,1:只申请模糊权限,2:模糊升级为精确,3:模糊+精确,开启精确,4:模糊+精确,关闭精确 330 */ 331 public initLocationFlag(callerAppInfo: CallerAppInfo): number { 332 let locationFlag: number = Constants.LOCATION_NONE; 333 let hasFuzz: boolean = callerAppInfo.reqPerms.includes(Permission.APPROXIMATELY_LOCATION); 334 let hasPrecise: boolean = callerAppInfo.reqPerms.includes(Permission.LOCATION); 335 336 if (hasFuzz) { 337 locationFlag = Constants.LOCATION_FUZZY; 338 if (hasPrecise) { 339 locationFlag = Constants.LOCATION_BOTH_PRECISE; 340 let fuzzyIndex = callerAppInfo.reqPerms.indexOf(Permission.APPROXIMATELY_LOCATION); 341 if (callerAppInfo.reqPermsState[fuzzyIndex] === Constants.PASS_OPER) { 342 locationFlag = Constants.LOCATION_UPGRADE; 343 } 344 } 345 } else { 346 if (hasPrecise) { 347 locationFlag = Constants.LOCATION_UPGRADE; 348 } 349 } 350 return locationFlag; 351 } 352 353 /** 354 * 获取所有授权权限组 355 * @param callerAppInfo 调用方信息 356 * @param context 应用上下文 357 * @param appName 应用名 358 * @param locationFlag 位置权限组flag 359 * @param pasteBoardName 剪贴板信息 360 * return 361 */ 362 public getGrantGroups( 363 callerAppInfo: CallerAppInfo, 364 context: common.ServiceExtensionContext, 365 appName: string, 366 locationFlag: number, 367 pasteBoardName: string 368 ): PermissionGroupConfig[] { 369 return PermissionGroupManager.getInstance().getGroupConfigs( 370 callerAppInfo, context, appName, locationFlag, pasteBoardName 371 ); 372 } 373 374 /** 375 * 点击事件处理 376 * @param groupConfig 377 * @param callerAppInfo 调用方信息 378 * @param locationFlag 位置权限组flag 379 * @param buttonStatus 点击状态 380 * return 381 */ 382 public async clickHandle( 383 groupConfig: PermissionGroupConfig, callerAppInfo: CallerAppInfo, locationFlag: number, buttonStatus: ButtonStatus 384 ): Promise<void> { 385 let flag: number = 386 THIS_TIME_FLAG.includes(buttonStatus) ? Constants.PERMISSION_ALLOW_THIS_TIME : Constants.PERMISSION_FLAG; 387 Log.info(`clickHandle, group: ${groupConfig.groupName}, buttonStatus: ${buttonStatus}.`); 388 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); 389 let reportPermissions: Permission[] = []; 390 let permissionWithOptionList: PermissionWithOption[] = []; 391 392 for (let permission of groupConfig.permissions) { 393 let result: optionAndState = { 394 operationResult: Constants.RESULT_SUCCESS, 395 permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED 396 }; 397 let permissionOption = PermissionGroupManager.getInstance().getPermissionOption( 398 permission, groupConfig.groupName, callerAppInfo, locationFlag, buttonStatus 399 ); 400 let permissionWithOption: PermissionWithOption = { permission, permissionOption }; 401 if (permissionOption !== PermissionOption.SKIP) { 402 permissionWithOptionList.push(permissionWithOption); 403 } 404 if (permissionOption === PermissionOption.GRANT) { 405 result = await this.grantPermissionWithResult(permission, flag, callerAppInfo.tokenId, atManager); 406 } 407 if (permissionOption === PermissionOption.REVOKE) { 408 result = await this.revokePermissionWithResult(permission, flag, callerAppInfo.tokenId, atManager); 409 } 410 Log.info(`permission: ${permission}, opt: ${permissionOption}, result: ${result.operationResult}.`); 411 if (result.operationResult === Constants.RESULT_SUCCESS && permissionOption !== PermissionOption.SKIP) { 412 reportPermissions.push(permission); 413 this.writeToGrantResult(permission, callerAppInfo, result.permissionState); 414 } 415 } 416 } 417 418 /** 419 * 授予权限并返回操作结果 420 * @param permission 操作权限 421 * @param flag 授权flag 422 * @param tokenId 应用token 423 * @param atManager atManager对象 424 * return 425 */ 426 private async grantPermissionWithResult( 427 permission: Permission, flag: number, tokenId: number, atManager: abilityAccessCtrl.AtManager 428 ): Promise<optionAndState> { 429 try { 430 await atManager.grantUserGrantedPermission(tokenId, permission, flag); 431 Log.info(`grant permission success, permission: ${permission}.`); 432 return { 433 operationResult: Constants.RESULT_SUCCESS, 434 permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED 435 } 436 } catch (error) { 437 Log.error(`grant permission faild, permission: ${permission}, code: ${error.code}, message: ${error.message}.`); 438 return { 439 operationResult: Constants.RESULT_FAILURE, 440 permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED 441 } 442 } 443 } 444 445 /** 446 * 撤销权限并返回操作结果 447 * @param permission 操作权限 448 * @param flag 授权flag 449 * @param tokenId 应用token 450 * @param atManager atManager对象 451 * return 452 */ 453 private async revokePermissionWithResult( 454 permission: Permission, flag: number, tokenId: number, atManager: abilityAccessCtrl.AtManager 455 ): Promise<optionAndState> { 456 try { 457 await atManager.revokeUserGrantedPermission(tokenId, permission, flag); 458 Log.info(`revoke permission success, permission: ${permission}.`); 459 return { 460 operationResult: Constants.RESULT_SUCCESS, 461 permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED 462 } 463 } catch (error) { 464 Log.error(`revoke permission faild, permission: ${permission}, code: ${error.code}, message: ${error.message}.`); 465 return { 466 operationResult: Constants.RESULT_FAILURE, 467 permissionState: abilityAccessCtrl.GrantStatus.PERMISSION_DENIED 468 } 469 } 470 } 471 472 /** 473 * 授权或撤销成功后,将结果写入grantResult 474 * @param permission 权限 475 * @param callerAppInfo 调用方信息 476 * @param state 权限状态 477 * return 478 */ 479 public writeToGrantResult( 480 permission: Permission, callerAppInfo: CallerAppInfo, state: abilityAccessCtrl.GrantStatus 481 ): void { 482 callerAppInfo.reqPerms.forEach((reqPerm, index) => { 483 if (reqPerm === permission) { 484 callerAppInfo.grantResult[index] = state; 485 } 486 }) 487 } 488 489 /** 490 * 销毁模态窗口 491 * @param context ServiceExtensionContext 492 * @param win 窗口对象 493 * @param callerAppInfo 调用方信息 494 * return 495 */ 496 public async terminateWithResult( 497 context: common.ServiceExtensionContext, win: window.Window, callerAppInfo: CallerAppInfo 498 ): Promise<void> { 499 Log.info(`Perms: ${JSON.stringify(callerAppInfo.reqPerms)}, result: ${JSON.stringify(callerAppInfo.grantResult)}.`); 500 let option = new rpc.MessageOption(); 501 let data = new rpc.MessageSequence(); 502 let reply = new rpc.MessageSequence(); 503 let setDialogData = new rpc.MessageSequence(); 504 try { 505 data.writeInterfaceToken(Constants.ACCESS_TOKEN); 506 data.writeStringArray(callerAppInfo.reqPerms); 507 data.writeIntArray(callerAppInfo.grantResult); 508 setDialogData.writeInterfaceToken(Constants.ACCESS_TOKEN); 509 callerAppInfo.proxy.sendMessageRequest(Constants.RESULT_CODE, data, reply, option); 510 callerAppInfo.proxy.sendMessageRequest(Constants.RESULT_CODE_1, setDialogData, reply, option); 511 } catch (error) { 512 Log.error(`terminateWithResult faild, code: ${error.code}, message: ${error.message}.`); 513 } finally { 514 data.reclaim(); 515 reply.reclaim(); 516 setDialogData.reclaim(); 517 let windowNum: number = GlobalContext.getContext().decreaseAndGetWindowNum(); 518 Log.info(`global windowNum is: ${windowNum}.`); 519 win.destroyWindow(); 520 windowNum <= 0 ? context.terminateSelf() : null; 521 } 522 } 523 524}