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