1/* 2 * Copyright (c) 2024 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 */ 15import bundleManager from '@ohos.bundle.bundleManager'; 16import Constants from '../common/utils/constant'; 17import common from '@ohos.app.ability.common'; 18import { BusinessError } from '@ohos.base'; 19import { 20 checkPermissionGroup, 21 getGroupIdByPermission, getPermissionLabel, Log, 22 PermissionDialogException, 23 PermissionDialogReturn, 24 verifyAccessToken } from '../common/utils/utils'; 25import { AppInfo, CallerBundleInfo, 26 CheckboxInfo, 27 MediaDocObj, 28 WantInfo } from '../common/model/typedef'; 29import { GlobalContext } from '../common/utils/globalContext'; 30import { Permission, PermissionGroup } from '../common/model/definition'; 31import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; 32import { groups, showSubPermissionsGroup } from '../common/model/permissionGroup'; 33import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 34import { CustomContentDialog } from '@ohos.arkui.advanced.Dialog' 35 36let accessTokenId: number = 0; 37let session: UIExtensionContentSession; 38let results: Map<string, number> = new Map(); 39let terminating: boolean = false; 40 41export function ReportDialogResult(wantPermissions: Permissions[]) { 42 let resultArray: number[] = []; 43 Log.info('processed result:' + JSON.stringify(results)); 44 for (const permission of wantPermissions) { 45 // check if corresponding result is set 46 if (results.has(permission)) { 47 resultArray.push(results.get(permission)!); 48 } else { 49 Log.error(permission + ' is not processed!'); 50 resultArray.push(Constants.ERR_PERMISSION_GRANT_EXCEPTION); 51 } 52 } 53 Log.info('terminating session with return array ' + JSON.stringify(resultArray)); 54 PermissionDialogReturn(resultArray, session); 55} 56 57export function getCallerBundleInfo(want: WantInfo): CallerBundleInfo { 58 let bundleName: string = want.parameters['ohos.aafwk.param.callerBundleName'] ?? ''; 59 let uId: number = want.parameters['ohos.aafwk.param.callerUid'] ?? 0; 60 let token: number = want.parameters['ohos.aafwk.param.callerToken'] ?? 0; 61 let permissions: Permissions[] = want.parameters['ohos.user.setting.permission'] ?? []; 62 let globSwitch: number = want.parameters['ohos.user.setting.global_switch'] ?? -1; 63 64 const callerBundle: CallerBundleInfo = { 65 bundleName: bundleName, 66 uId: uId, 67 permissionGroup: permissions, 68 token: token, 69 globSwitch: globSwitch 70 } 71 72 return callerBundle; 73} 74 75@Entry({ useSharedStorage: true }) 76@Component 77struct PermissionStateSheetDialog { 78 private context = this.getUIContext().getHostContext() as common.ServiceExtensionContext; 79 @LocalStorageLink('want') want: Want | null = null; 80 @LocalStorageLink('session') session: UIExtensionContentSession = {} as UIExtensionContentSession; 81 @State applicationInfo: AppInfo | undefined = undefined; 82 @State folderStatus: boolean[] = [false, false, false]; 83 @State reqUserPermissions: Permission[] = []; 84 @State bundleName: string = ''; 85 @State isGranted: number = Constants.PERMISSION_ALLOW; 86 @State groupName: ResourceStr = ''; 87 @State currentGroup: PermissionGroup = PermissionGroup.OTHER; 88 89 dialogController: CustomDialogController | null = new CustomDialogController({ 90 builder: CustomContentDialog({ 91 contentBuilder: () => { 92 this.buildDialog(); 93 }, 94 contentAreaPadding: {left: 0, right: 0} 95 }), 96 offset: { dx: 0, dy: `-${GlobalContext.load('avoidAreaHeight') as string}` }, // unit included in globalContext 97 alignment: DialogAlignment.Bottom, 98 customStyle: false, 99 isModal: true, 100 width: '100%', 101 autoCancel: false, 102 cornerRadius: {topLeft: 32, topRight: 32, bottomLeft: 0, bottomRight: 0}, 103 cancel: () => { 104 ReportDialogResult(this.reqUserPermissions); 105 this.context.terminateSelf(); 106 this.dialogController?.close(); 107 } 108 }); 109 110 @Builder 111 buildDialog() { 112 mediaDocumentItem({ 113 bundleName: this.bundleName, 114 backTitle: this.groupName, 115 permissions: this.reqUserPermissions, 116 status: this.isGranted, 117 tokenId: this.applicationInfo?.tokenId, 118 applicationInfo: this.applicationInfo, 119 currentGroup: this.currentGroup 120 }) 121 } 122 123 build() { 124 125 } 126 127 async aboutToAppear() { 128 session = this.session; 129 await this.GetAppInfo(); 130 131 if (!this.applicationInfo) { 132 this.context.terminateSelf(); 133 } 134 135 if (this.dialogController !== null && terminating == false) { 136 this.dialogController.open(); 137 } 138 } 139 140 async getSpecifiedApplication(bundleName: string): Promise<AppInfo | undefined> { 141 const flag = 142 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION | 143 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION; 144 let promise = new Promise<AppInfo | undefined>(async (resolve, reject) => { 145 try { 146 let info = await this.getBundleInfo(bundleName, flag); 147 resolve(info); 148 } catch (e) { 149 resolve(undefined); 150 } 151 }) 152 return promise; 153 } 154 155 async getBundleInfo(bundleName: string, flag: number): Promise<AppInfo | undefined> { 156 return new Promise<AppInfo | undefined>(async (resolve, reject) => { 157 let info: AppInfo | undefined = undefined; 158 let permissionGrantMap: Map<string, number> = new Map(); 159 let bundleInfo = await bundleManager.getBundleInfo(bundleName, flag); 160 let appPermissions = bundleInfo.reqPermissionDetails; 161 let granted = bundleInfo.permissionGrantStates; 162 info = new AppInfo(bundleInfo.name, bundleInfo.targetVersion, 163 bundleInfo.appInfo.accessTokenId, '', bundleInfo.appInfo.iconId, bundleInfo.appInfo.iconResource, 164 '', bundleInfo.appInfo.labelId, bundleInfo.appInfo.labelResource, [], [], '', '', '' 165 ); 166 resolve(info); 167 // check if all permissions passed from want has been claimed by app config 168 for (const wantPermission of this.reqUserPermissions) { 169 if (terminating) { 170 continue; 171 } 172 let idx = appPermissions.findIndex((item) => { 173 return item.name == wantPermission; 174 }); 175 if (idx == -1) { 176 Log.error('permission ' + wantPermission + ' not claimed by ' + this.bundleName + ' in module.json'); 177 PermissionDialogException(Constants.ERR_PERMISSIONS_NOT_IN_CONFIG, session); 178 terminating = true; 179 this.context.terminateSelf(); 180 return; 181 } 182 permissionGrantMap.set(appPermissions[idx].name, granted[idx]); 183 } 184 // check if all permissions are already granted 185 let permissionsAllGranted = true; 186 for (const key of permissionGrantMap.keys()) { 187 if (permissionGrantMap.get(key) === -1) { // -1 means not granted 188 permissionsAllGranted = false; 189 } 190 } 191 // check if all permissions are already granted 192 if (permissionsAllGranted) { 193 Log.error('terminating : ' + terminating + ' requested permissions are all already granted'); 194 if (terminating == false) { 195 PermissionDialogException(Constants.ERR_PERMISSIONS_ALL_GRANTED, session); 196 terminating = true; 197 this.context.terminateSelf(); 198 } 199 } 200 }) 201 } 202 203 aboutToDisappear() { 204 Log.info('permission dialog about to disappear'); 205 this.dialogController = null; 206 } 207 208 async GetAppInfo() { 209 let callerBundle: CallerBundleInfo = getCallerBundleInfo(this.want as Object as WantInfo); 210 this.bundleName = callerBundle.bundleName; 211 this.reqUserPermissions = callerBundle.permissionGroup as Permission[]; // user permission is passed from wantInfo 212 // check if wanted permissions are in the same permission group 213 let groupName = checkPermissionGroup(this.reqUserPermissions); 214 if (groupName == null) { 215 terminating = true; 216 PermissionDialogException(Constants.ERR_PERMISSIONS_NOT_SAME_GROUP, session); 217 this.context.terminateSelf(); 218 } 219 this.currentGroup = groupName!; 220 // check app infos, check if permissions are claimed , check if permissions are granted 221 this.applicationInfo = await this.getSpecifiedApplication(callerBundle.bundleName); 222 if (this.applicationInfo === undefined) { 223 Log.error('application info is undefined'); 224 } 225 // get application detailed info 226 await this.initApplicationInfo(this.applicationInfo!); 227 // initialize permission states 228 await this.initialPermissions(); 229 // store application info after initialization is done 230 GlobalContext.store('applicationInfo', this.applicationInfo); 231 } 232 233 async initApplicationInfo(info: AppInfo) { 234 Log.info('labelResource: ' + JSON.stringify(info.labelResource)); 235 let resourceManager = this.context.createBundleContext(info.bundleName).resourceManager; 236 237 if (info.labelResource.id !== 0) { 238 info.label = await this.context.resourceManager.getStringValue(info.labelResource); 239 } else { 240 info.label = await resourceManager.getStringValue(info.labelId); 241 } 242 243 try { 244 if (info.iconResource.id !== 0) { 245 let iconDescriptor = this.context.resourceManager.getDrawableDescriptor(info.iconResource); 246 info.icon = iconDescriptor?.getPixelMap(); 247 } else { 248 let iconDescriptor = resourceManager.getDrawableDescriptor(info.iconId); 249 info.icon = iconDescriptor?.getPixelMap(); 250 } 251 } catch (error) { 252 Log.error(`getDrawableDescriptor failed, error code: ${error.code}, message: ${error.message}.`); 253 } 254 255 if (!info.icon) { 256 info.icon = $r('app.media.icon'); 257 } 258 259 let groupIds: number[] = []; 260 for (let i = 0; i < this.reqUserPermissions.length; i++) { 261 let groupId = getGroupIdByPermission(this.reqUserPermissions[i]); 262 if (groupIds.indexOf(groupId) == -1) { 263 groupIds.push(groupId); 264 } 265 } 266 info.permissions = this.reqUserPermissions; 267 info.groupId = groupIds; 268 } 269 270 async getStatus(groupReqPermissions: Permissions[], group: string) { 271 if (group === 'LOCATION') { 272 try { 273 let acManager = abilityAccessCtrl.createAtManager(); 274 let fuzzyState = acManager.verifyAccessTokenSync( 275 this.applicationInfo?.tokenId, Permission.APPROXIMATELY_LOCATION 276 ); 277 fuzzyState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ? 278 this.isGranted = Constants.PERMISSION_ALLOWED_ONLY_DURING_USE : null; 279 let backgroundState = 280 acManager.verifyAccessTokenSync(this.applicationInfo?.tokenId, Permission.LOCATION_IN_BACKGROUND); 281 backgroundState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ? 282 this.isGranted = Constants.PERMISSION_ALLOW : null; 283 await acManager.getPermissionFlags(this.applicationInfo?.tokenId, Permission.APPROXIMATELY_LOCATION ) 284 .then(flag => { 285 flag === Constants.PERMISSION_ALLOW_THIS_TIME ? this.isGranted = Constants.PERMISSION_ONLY_THIS_TIME : null; 286 }) 287 } catch (err) { 288 Log.error('change location status error: ' + JSON.stringify(err)); 289 } 290 GlobalContext.store('locationStatus', this.isGranted); 291 return true; 292 } 293 for (let i = 0; i < groupReqPermissions.length; i++) { 294 let permission = groupReqPermissions[i]; 295 let res = await verifyAccessToken(this.applicationInfo!.tokenId, permission); 296 if (res != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 297 this.isGranted = Constants.PERMISSION_BAN; 298 } 299 if (group === 'FOLDER' && res === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 300 switch (permission) { 301 case Permission.READ_WRITE_DOWNLOAD_DIRECTORY: 302 this.folderStatus[0] = true; 303 break; 304 case Permission.READ_WRITE_DESKTOP_DIRECTORY: 305 this.folderStatus[1] = true; 306 break; 307 case Permission.READ_WRITE_DOCUMENTS_DIRECTORY: 308 this.folderStatus[2] = true; 309 break; 310 } 311 } 312 } 313 return true; 314 } 315 316 /** 317 * Initialize permission status information and group permission information 318 */ 319 async initialPermissions() { 320 if (this.bundleName && !this.applicationInfo?.groupId.length) { 321 await this.initApplicationInfo(this.applicationInfo!); 322 } 323 let reqPermissions = this.applicationInfo!.permissions; 324 let reqGroupIds = this.applicationInfo!.groupId; 325 326 for (let i = 0; i < reqGroupIds.length; i++) { 327 let id = reqGroupIds[i]; 328 let groupName = groups[id].groupName; 329 let group = groups[id].name; 330 let groupReqPermissions: Permissions[] = []; 331 332 for (let j = 0; j < reqPermissions.length; j++) { 333 let permission = reqPermissions[j]; 334 if (groups[id].permissions.indexOf(permission) != -1) { 335 groupReqPermissions.push(permission); 336 this.groupName = groupName; 337 } 338 } 339 this.isGranted = group === 'LOCATION' ? Constants.PERMISSION_BAN : Constants.PERMISSION_ALLOW; 340 this.folderStatus = [false, false, false]; 341 await this.getStatus(groupReqPermissions, group); 342 343 GlobalContext.store('folderStatus', this.folderStatus); 344 } 345 } 346} 347 348@CustomDialog 349struct mediaDocumentItem { 350 private context = this.getUIContext().getHostContext() as common.UIAbilityContext; 351 private backTitle: ResourceStr = ''; 352 private bundleName: string = ''; 353 private permissions: Permission[] = []; 354 private status: number = 0; 355 private tokenId: number = 0; 356 private controller: CustomDialogController; 357 @State hidden: boolean = false; 358 @State currentGroup: PermissionGroup = PermissionGroup.OTHER; 359 @State applicationInfo: AppInfo = GlobalContext.load('applicationInfo'); 360 @State folderStatus: boolean[] = []; 361 @State mediaDocListItem: MediaDocObj[] = []; // Permission information array 362 @State selected: number = 0; 363 @State accurateIsOn: boolean = true; 364 @State api: number = 0; 365 @State isRisk: boolean = false; // Whether it is a risky application 366 @State noForeground: boolean = false; 367 @State isTouch: number = -1; 368 @State isCheck: string = ''; 369 @State reason: string = ''; 370 @State label: string = ''; 371 @State version: string = ''; 372 @State permissionLabels: Array<ResourceStr> = []; 373 @LocalStorageLink('bundleInfo') bundleInfo: Want = {} as Want; 374 375 /** 376 * Grant permissions to the app 377 * @param {Number} accessTokenId 378 * @param {String} permission permission name 379 */ 380 async grantUserGrantedPermission(accessTokenId: number, permission: Permissions) { 381 Log.info('granting permission ' + permission); 382 try { 383 await abilityAccessCtrl.createAtManager() 384 .grantUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG); 385 Log.info('grantUserGrantedPermission ' + permission + ' success.'); 386 results.set(permission, Constants.PERMISSION_DIALOG_SUCCESS); 387 } catch (e) { 388 results.set(permission, Constants.ERR_PERMISSION_GRANT_EXCEPTION); 389 Log.error('grantUserGrantedPermission failed. Cause: ' + JSON.stringify(e)); 390 } 391 } 392 393 /** 394 * Deauthorize the app 395 * @param {Number} accessTokenId 396 * @param {String} permission permission name 397 */ 398 async revokeUserGrantedPermission(accessTokenId: number, permission: Permissions, flag: number) { 399 abilityAccessCtrl.createAtManager().revokeUserGrantedPermission(accessTokenId, permission, flag) 400 .then(() => { 401 Log.error('revokeUserGrantedPermission ' + permission + ' success'); 402 }) 403 .catch((error: BusinessError) => { 404 Log.error('revokeUserGrantedPermission failed. Cause: ' + JSON.stringify(error)); 405 }) 406 } 407 408 onPageShow(): void { 409 this.refreshStatus(); 410 } 411 412 async refreshStatus() { 413 Log.info('Refresh permission status'); 414 let isGranted = this.currentGroup === 'LOCATION' ? Constants.PERMISSION_BAN : Constants.PERMISSION_ALLOW; 415 let folderStatus = [false, false, false]; 416 let atManager = abilityAccessCtrl.createAtManager(); 417 for (let i = 0; i < this.permissions.length; i++) { 418 let permission = this.permissions[i]; 419 if (this.currentGroup === 'LOCATION') { 420 continue; 421 } 422 let res = atManager.verifyAccessTokenSync(this.tokenId, permission); 423 if (res != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 424 isGranted = Constants.PERMISSION_BAN; 425 } 426 if (this.currentGroup === 'FOLDER' && res === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 427 switch (permission) { 428 case Permission.READ_WRITE_DOWNLOAD_DIRECTORY: 429 folderStatus[0] = true; 430 break; 431 case Permission.READ_WRITE_DESKTOP_DIRECTORY: 432 folderStatus[1] = true; 433 break; 434 case Permission.READ_WRITE_DOCUMENTS_DIRECTORY: 435 folderStatus[2] = true; 436 break; 437 } 438 results.set(permission, Constants.PERMISSION_DIALOG_SUCCESS); 439 } 440 } 441 this.folderStatus = folderStatus; 442 await this.refreshSelected(isGranted); 443 } 444 445 async refreshSelected(isGranted: number) { 446 this.selected = isGranted; 447 if (this.currentGroup === 'PASTEBOARD') { 448 try { 449 let acManager = abilityAccessCtrl.createAtManager(); 450 acManager.getPermissionFlags(this.tokenId, Permission.READ_PASTEBOARD).then(flag => { 451 flag === Constants.PERMISSION_ALLOW_THIS_TIME ? this.selected = Constants.PERMISSION_ONLY_THIS_TIME : null; 452 }); 453 } catch (err) { 454 Log.error('getPermissionFlags error: ' + JSON.stringify(err)); 455 } 456 } else if (this.currentGroup === 'LOCATION') { 457 try { 458 let acManager = abilityAccessCtrl.createAtManager(); 459 let fuzzyState = acManager.verifyAccessTokenSync(this.tokenId, Permission.APPROXIMATELY_LOCATION); 460 fuzzyState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ? 461 this.selected = Constants.PERMISSION_ALLOWED_ONLY_DURING_USE : null; 462 let accurateStatus = acManager.verifyAccessTokenSync(this.tokenId, Permission.LOCATION); 463 this.accurateIsOn = (accurateStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) ? true : false; 464 let backgroundState = acManager.verifyAccessTokenSync(this.tokenId, Permission.LOCATION_IN_BACKGROUND); 465 backgroundState === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED ? 466 this.selected = Constants.PERMISSION_ALLOW : null; 467 acManager.getPermissionFlags(this.tokenId, Permission.APPROXIMATELY_LOCATION).then(flag => { 468 flag === Constants.PERMISSION_ALLOW_THIS_TIME ? this.selected = Constants.PERMISSION_ONLY_THIS_TIME : null; 469 }) 470 } catch (err) { 471 Log.error('change location status error: ' + JSON.stringify(err)); 472 } 473 } else { 474 await this.checkPermissionFlag(); 475 } 476 } 477 478 async checkPermissionFlag() { 479 try { 480 for (const permission of this.permissions) { 481 let acManager = abilityAccessCtrl.createAtManager(); 482 let flag = await acManager.getPermissionFlags(this.tokenId, permission); 483 Log.error('permission Flag is ' + flag); 484 if (flag === Constants.PERMISSION_DEFAULT && terminating == false) { 485 PermissionDialogException(Constants.ERR_PERMISSIONS_FLAG_DEFAULT, session); 486 terminating = true; 487 this.context.terminateSelf(); 488 return; 489 } 490 } 491 } catch (err) { 492 Log.error('getPermissionFlags error: ' + JSON.stringify(err)); 493 } 494 } 495 496 getCheckboxInfo(permission: Permissions): CheckboxInfo { 497 switch (permission) { 498 case Permission.READ_WRITE_DOWNLOAD_DIRECTORY: 499 return new CheckboxInfo($r('app.string.Download_folder'), 0); 500 case Permission.READ_WRITE_DESKTOP_DIRECTORY: 501 return new CheckboxInfo($r('app.string.Desktop_folder'), 1); 502 case Permission.READ_WRITE_DOCUMENTS_DIRECTORY: 503 return new CheckboxInfo($r('app.string.Document_folder'), 2); 504 default: 505 return new CheckboxInfo($r('app.string.Download_folder'), 0); 506 } 507 } 508 509 getMediaDocList() { 510 Log.info('current group ' + this.currentGroup); 511 Log.info('current permissions ' + this.permissions); 512 513 if (this.currentGroup == 'PASTEBOARD') { 514 this.mediaDocListItem.push( 515 new MediaDocObj($r('app.string.per_use_query'), this.permissions, Constants.PERMISSION_ONLY_THIS_TIME) 516 ); 517 this.mediaDocListItem.push( 518 new MediaDocObj($r('app.string.always_allow'), this.permissions, Constants.PERMISSION_ALLOW) 519 ); 520 } else if (this.currentGroup == 'LOCATION') { 521 this.selected = GlobalContext.load('locationStatus'); 522 this.mediaDocListItem.push( 523 new MediaDocObj($r('app.string.per_inquiry'), this.permissions, Constants.PERMISSION_ONLY_THIS_TIME) 524 ); 525 if (this.permissions.includes(Permission.LOCATION_IN_BACKGROUND)) { 526 this.mediaDocListItem.push( 527 new MediaDocObj($r('app.string.always_allow'), this.permissions, Constants.PERMISSION_ALLOW) 528 ); 529 } 530 if (this.permissions.includes(Permission.APPROXIMATELY_LOCATION)) { 531 this.mediaDocListItem.push( 532 new MediaDocObj( 533 $r('app.string.allowed_only_during_use'), 534 [Permission.APPROXIMATELY_LOCATION], 535 Constants.PERMISSION_ALLOWED_ONLY_DURING_USE 536 ) 537 ); 538 } else { 539 this.noForeground = true; 540 } 541 } else { 542 this.mediaDocListItem.push( 543 new MediaDocObj($r('app.string.allow'), this.permissions, Constants.PERMISSION_ALLOW) 544 ); 545 } 546 this.mediaDocListItem.push( 547 new MediaDocObj($r('app.string.ban'), this.permissions, Constants.PERMISSION_BAN) 548 ); 549 } 550 551 async getReason() { 552 this.label = this.applicationInfo.label; 553 if (showSubPermissionsGroup.indexOf(this.currentGroup) != -1) { 554 this.permissions.forEach((permission, idx) => { 555 if (idx > 0) { 556 this.permissionLabels.push($r('app.string.and')); 557 } 558 let label = getPermissionLabel(permission); 559 this.permissionLabels.push(label); 560 }); 561 } 562 let hasReason = false; 563 let info = await 564 bundleManager.getBundleInfo(this.bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_REQUESTED_PERMISSION) 565 for (const permission of this.permissions) { 566 for (const reqPermissionDetail of info.reqPermissionDetails) { 567 if (reqPermissionDetail.name != permission) { 568 continue; 569 } 570 Log.info('reqPermissionDetail: ' + JSON.stringify(reqPermissionDetail)); 571 let context = this.context.createModuleContext(this.bundleName, reqPermissionDetail.moduleName); 572 let reason = await context.resourceManager.getStringValue(reqPermissionDetail.reasonId); 573 if (reason !== undefined && !hasReason) { 574 this.reason = reason.slice(Constants.START_SUBSCRIPT, Constants.END_SUBSCRIPT); 575 hasReason = true; 576 } 577 } 578 } 579 } 580 581 aboutToAppear() { 582 this.refreshStatus(); 583 this.getMediaDocList(); 584 this.getReason(); 585 this.setState(); 586 } 587 588 setState() { 589 this.hidden = false; 590 this.selected = this.status; 591 bundleManager.getBundleInfo(this.bundleName, bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION) 592 .then(async res => { 593 this.api = res.targetVersion; 594 this.version = res.versionName; 595 accessTokenId = res.appInfo.accessTokenId; 596 let acManager = abilityAccessCtrl.createAtManager(); 597 let accurateStatus = acManager.verifyAccessTokenSync(res.appInfo.accessTokenId, Permission.LOCATION); 598 this.accurateIsOn = (accurateStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) ? true : false; 599 try { 600 let getFlagPermission = 601 this.currentGroup === 'LOCATION' ? Permission.APPROXIMATELY_LOCATION : this.permissions[0]; 602 let flag = await acManager.getPermissionFlags(res.appInfo.accessTokenId, getFlagPermission); 603 Log.info(`getPermissionFlags success, data->${JSON.stringify(flag)}`); 604 this.isRisk = (flag == Constants.PERMISSION_POLICY_FIXED) ? true : false; 605 if (flag === Constants.PERMISSION_ALLOW_THIS_TIME) { 606 this.selected = Constants.PERMISSION_ONLY_THIS_TIME; 607 } 608 } catch (err) { 609 Log.error('acManager.getPermissionFlags failed. Cause: ' + JSON.stringify(err)); 610 } 611 }).catch((error: BusinessError) => { 612 Log.error('bundle.getBundleInfo failed. Cause: ' + JSON.stringify(error)); 613 }); 614 } 615 616 async grantFolderPermission(permission: Permissions) { 617 if (this.folderStatus[this.getCheckboxInfo(permission).index]) { 618 this.revokeUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG); 619 this.folderStatus[this.getCheckboxInfo(permission).index] = false; 620 results.set(permission, Constants.ERR_PERMISSION_GRANT_EXCEPTION); 621 } else { 622 await this.grantUserGrantedPermission(accessTokenId, permission); 623 this.folderStatus[this.getCheckboxInfo(permission).index] = true; 624 } 625 // check if every permission has been granted 626 let allGranted = true; 627 for (const permission of this.permissions) { 628 if (!results.has(permission)) { 629 Log.info('permission ' + permission + ' not granted, continue'); 630 return; 631 } 632 if (results.get(permission) !== Constants.PERMISSION_DIALOG_SUCCESS) { 633 allGranted = false; 634 } 635 } 636 // if all granted , return status 637 if (allGranted) { 638 ReportDialogResult(this.permissions); 639 } 640 } 641 642 async grantOtherPermissions(item: MediaDocObj) { 643 this.selected = item.index; 644 for (const permission of item.permissions) { 645 Log.info('item click index:' + item.index); 646 if (item.index === Constants.PERMISSION_ALLOW) { 647 if (permission !== Permission.LOCATION) { 648 await this.grantUserGrantedPermission(accessTokenId, permission); 649 } 650 } else if (item.index === Constants.PERMISSION_BAN) { 651 if (permission == Permission.LOCATION && this.accurateIsOn) { 652 this.revokeUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG); 653 this.accurateIsOn = false; 654 } else { 655 this.revokeUserGrantedPermission(accessTokenId, permission, Constants.PERMISSION_FLAG); 656 } 657 } else if (item.index === Constants.PERMISSION_ONLY_THIS_TIME) { 658 if (permission !== Permission.LOCATION) { 659 await this.revokeUserGrantedPermission( 660 accessTokenId, permission, Constants.PERMISSION_ALLOW_THIS_TIME 661 ); 662 } 663 } else if (item.index === Constants.PERMISSION_ALLOWED_ONLY_DURING_USE) { 664 await this.grantUserGrantedPermission(accessTokenId, permission); 665 await this.revokeUserGrantedPermission( 666 accessTokenId, Permission.LOCATION_IN_BACKGROUND, Constants.PERMISSION_FLAG 667 ); 668 } 669 } 670 ReportDialogResult(this.permissions); 671 return true; 672 } 673 674 build() { 675 Column() { 676 Row() { 677 Column() { 678 Row() { 679 Text(this.backTitle) 680 .align(Alignment.Start) 681 .fontColor($r('sys.color.font_primary')) 682 .maxLines(Constants.MAXIMUM_HEADER_LINES) 683 .textOverflow({ overflow: TextOverflow.Ellipsis }) 684 .fontSize(Constants.TEXT_BIG_FONT_SIZE) 685 .flexGrow(Constants.FLEX_GROW) 686 .fontWeight(FontWeight.Bold) 687 .padding({ left: Constants.PADDING_24, top: Constants.PADDING_20, bottom: Constants.PADDING_20 }) 688 .width('80%') 689 }.width(Constants.FULL_WIDTH) 690 } 691 .alignItems(HorizontalAlign.Start) 692 .width(Constants.FULL_WIDTH) 693 } 694 Row() { 695 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 696 Image(this.applicationInfo.icon) 697 .width(Constants.TERTIARY_IMAGE_WIDTH) 698 .height(Constants.TERTIARY_IMAGE_HEIGHT) 699 .margin({ left: Constants.TERTIARY_IMAGE_MARGIN_LEFT, right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 700 Column() { 701 Row() { 702 Text(this.label) 703 .maxLines(Constants.MAXIMUM_HEADER_LINES) 704 .textOverflow({ overflow: TextOverflow.Ellipsis }) 705 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 706 .fontColor($r('sys.color.font_primary')) 707 .fontWeight(FontWeight.Bold) 708 .textAlign(TextAlign.Start) 709 } 710 .width(Constants.TERTIARY_HALF_WIDTH) 711 .margin({ bottom: Constants.TERTIARY_LABEL_MARGIN_BOTTOM }) 712 713 Row() { 714 Text($r('app.string.version')) 715 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 716 .fontColor($r('sys.color.font_secondary')) 717 .textAlign(TextAlign.Start) 718 Text(this.version) 719 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 720 .fontColor($r('sys.color.font_secondary')) 721 .textAlign(TextAlign.Start) 722 } 723 .width(Constants.TERTIARY_HALF_WIDTH) 724 } 725 }.margin({ left: Constants.TERTIARY_MARGIN_LEFT }) 726 } 727 if (this.isRisk) { 728 Row() { 729 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 730 Row() { 731 Image($r('app.media.ic_public_fail')) 732 .fillColor($r('sys.color.icon_secondary')) 733 .width(Constants.TERTIARY_RADIO_IMAGE_WIDTH) 734 .height(Constants.TERTIARY_RADIO_IMAGE_HEIGHT) 735 .margin({ right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 736 Text($r('app.string.risk_warning')) 737 .fontColor($r('sys.color.font_primary')) 738 .fontSize($r('sys.float.ohos_id_text_size_body1')) 739 .fontWeight(FontWeight.Regular) 740 }.margin({ left: Constants.DEFAULT_PADDING_START, right: Constants.DEFAULT_PADDING_END }) 741 } 742 }.backgroundColor($r('sys.color.interactive_click')) 743 .borderRadius($r('sys.float.ohos_id_corner_radius_default_l')) 744 .padding({ top: Constants.DEFAULT_PADDING_TOP, bottom: Constants.DEFAULT_PADDING_BOTTOM }) 745 .margin({ left: Constants.DEFAULT_MARGIN_START, right: Constants.DEFAULT_MARGIN_END }) 746 } 747 Row() { 748 Text() { 749 Span(this.backTitle) 750 Span($r('app.string.access_permission')) 751 } 752 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 753 .fontColor($r('sys.color.icon_secondary')) 754 .fontWeight(FontWeight.Medium) 755 .textAlign(TextAlign.Start) 756 .lineHeight(Constants.SUBTITLE_LINE_HEIGHT) 757 }.width(Constants.FULL_WIDTH) 758 .constraintSize({ minHeight: Constants.SUBTITLE_MIN_HEIGHT }) 759 .padding({ top: Constants.SUBTITLE_PADDING_TOP, bottom: Constants.SUBTITLE_PADDING_BOTTOM, 760 left: Constants.TERTIARY_TEXT_MARGIN_LEFT, right: Constants.TERTIARY_IMAGE_MARGIN_RIGHT }) 761 762 Column() { 763 List() { 764 if (this.currentGroup === 'FOLDER') { 765 ForEach(this.permissions, (permission: Permissions) => { 766 ListItem() { 767 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 768 Row() { 769 Text(this.getCheckboxInfo(permission).label) 770 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 771 .fontColor($r('sys.color.font_primary')) 772 .fontWeight(FontWeight.Medium) 773 .flexGrow(Constants.FLEX_GROW) 774 Checkbox() 775 .select(this.folderStatus[this.getCheckboxInfo(permission).index]) 776 .hitTestBehavior(HitTestMode.None) 777 } 778 .width(Constants.FULL_WIDTH) 779 .height(Constants.LISTITEM_ROW_HEIGHT) 780 .onClick(async () => { 781 await this.grantFolderPermission(permission) 782 }) 783 } 784 } 785 .padding({ 786 left: $r('sys.float.ohos_id_card_margin_start'), 787 right: $r('sys.float.ohos_id_card_margin_end') 788 }) 789 .borderRadius($r('sys.float.ohos_id_corner_radius_default_l')) 790 .margin({ top: Constants.TERTIARY_LISTITEM_MARGIN_TOP }) 791 .linearGradient((this.isCheck === permission) ? { 792 angle: 90, 793 direction: GradientDirection.Right, 794 colors: [['#DCEAF9', 0.0], ['#FAFAFA', 1.0]] 795 } : { 796 angle: 90, 797 direction: GradientDirection.Right, 798 colors: [] 799 }) 800 .onTouch(event => { 801 if (event === undefined) { 802 return; 803 } 804 if (event.type === TouchType.Down) { 805 this.isCheck = permission; 806 } 807 if (event.type === TouchType.Up) { 808 this.isCheck = ''; 809 } 810 }) 811 }, (permission: Permissions) => JSON.stringify(permission)) 812 } else { 813 ForEach(this.mediaDocListItem, (item: MediaDocObj) => { 814 ListItem() { 815 Column() { 816 Row() { 817 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 818 Row() { 819 Text(item.name) 820 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 821 .fontColor($r('sys.color.font_primary')) 822 .fontWeight(FontWeight.Medium) 823 .flexGrow(Constants.FLEX_GROW) 824 Radio({ value: 'Radio', group: 'radioGroup' }) 825 .checked(item.index === this.selected) 826 .hitTestBehavior(HitTestMode.None) 827 .height(Constants.SHAPE_DIA) 828 .width(Constants.SHAPE_DIA) 829 } 830 .width(Constants.FULL_WIDTH) 831 .height(Constants.LISTITEM_ROW_HEIGHT) 832 .onClick(async () => { 833 await this.grantOtherPermissions(item); 834 }) 835 } 836 } 837 } 838 } 839 .padding({ 840 left: $r('sys.float.ohos_id_card_margin_start'), 841 right: $r('sys.float.ohos_id_card_margin_end') 842 }) 843 .borderRadius($r('sys.float.ohos_id_corner_radius_default_l')) 844 .linearGradient((this.isTouch === item.index) ? { 845 angle: 90, 846 direction: GradientDirection.Right, 847 colors: [['#DCEAF9', 0.0], ['#FAFAFA', 1.0]] 848 } : { 849 angle: 90, 850 direction: GradientDirection.Right, 851 colors: [] 852 }) 853 .onTouch(event => { 854 if (event === undefined) { 855 return; 856 } 857 if (event.type === TouchType.Down) { 858 this.isTouch = item.index; 859 } 860 if (event.type === TouchType.Up) { 861 this.isTouch = -1; 862 } 863 }) 864 .margin({ top: Constants.TERTIARY_LISTITEM_MARGIN_TOP }) 865 }, (item: MediaDocObj) => JSON.stringify(item)) 866 } 867 } 868 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 869 .backgroundColor($r('sys.color.comp_background_list_card')) 870 .padding(Constants.LIST_PADDING_TOP) 871 .divider({ 872 strokeWidth: Constants.DIVIDER, 873 color: $r('sys.color.comp_divider'), 874 startMargin: Constants.DEFAULT_MARGIN_START, 875 endMargin: Constants.DEFAULT_MARGIN_END 876 }) 877 878 if (this.permissions.includes(Permission.LOCATION)) { 879 Column() { 880 Row() { 881 Text($r('app.string.precise_location')) 882 .fontSize(Constants.TEXT_MIDDLE_FONT_SIZE) 883 .fontColor($r('sys.color.font_primary')) 884 .fontWeight(FontWeight.Medium) 885 .flexGrow(Constants.FLEX_GROW) 886 Toggle({ type: ToggleType.Switch, isOn: this.accurateIsOn }) 887 .selectedColor($r('sys.color.icon_emphasize')) 888 .switchPointColor($r('sys.color.comp_background_primary_contrary')) 889 .onChange((isOn: boolean) => { 890 let acManager = abilityAccessCtrl.createAtManager() 891 if (isOn) { 892 acManager.grantUserGrantedPermission(accessTokenId, Permission.LOCATION, Constants.PERMISSION_FLAG) 893 .then(() => { 894 this.accurateIsOn = true 895 }) 896 } else { 897 acManager.revokeUserGrantedPermission(accessTokenId, Permission.LOCATION, Constants.PERMISSION_FLAG) 898 .then(() => { 899 this.accurateIsOn = false 900 }) 901 } 902 }) 903 .padding({ right: 0 }) 904 .enabled(this.selected !== Constants.PERMISSION_BAN) 905 }.width(Constants.FULL_WIDTH) 906 .height(Constants.LISTITEM_ROW_HEIGHT) 907 }.margin({ top: Constants.LOCATION_MARGIN_TOP, bottom: Constants.LOCATION_MARGIN_BOTTOM }) 908 .padding({ 909 left: Constants.DEFAULT_PADDING_START, 910 right: Constants.DEFAULT_PADDING_END, 911 top: Constants.TERTIARY_LIST_PADDING_TOP, 912 bottom: Constants.TERTIARY_LIST_PADDING_BOTTOM 913 }) 914 .borderRadius($r('sys.float.ohos_id_corner_radius_card')) 915 .backgroundColor($r('sys.color.comp_background_list_card')) 916 917 Row() { 918 Text($r('app.string.get_the_exact_position')) 919 .fontSize(Constants.TEXT_SMALL_FONT_SIZE) 920 .fontColor($r('sys.color.font_secondary')) 921 .lineHeight(Constants.TEXT_SMALL_LINE_HEIGHT) 922 }.width(Constants.FULL_WIDTH) 923 .padding({ 924 left: Constants.DEFAULT_PADDING_START, 925 right: Constants.DEFAULT_PADDING_END, 926 }) 927 } 928 } 929 .padding({ 930 left: Constants.LIST_PADDING_LEFT, 931 right: Constants.LIST_PADDING_LEFT 932 }) 933 .width(Constants.FULL_WIDTH) 934 .enabled(!this.isRisk && !this.noForeground) 935 .opacity((!this.isRisk && !this.noForeground) ? 1 : $r('sys.float.ohos_id_alpha_disabled')) 936 } 937 .visibility(this.hidden ? Visibility.None : Visibility.Visible) 938 .width(Constants.FULL_WIDTH) 939 } 940} 941