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