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