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