• 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 Constants from '../../common/constant';
17import { HiLog } from '../../common/HiLog';
18import { common, Want } from '@kit.AbilityKit';
19import ViewAbilityService from '../../rpc/ViewAbility/service/ViewAbilityService';
20import { DecryptState, OpenDlpFileManager } from './OpenDlpFileManager';
21import { FileParseType } from '../../bean/data/FileParseType';
22import OpenDlpFileData from '../data/OpenDlpFileData';
23import FileUtils from '../../common/FileUtils/FileUtils';
24import ErrorManager from '../handler/ErrorHandler';
25import { emitter } from '@kit.BasicServicesKit';
26import StartSandboxHandler from '../handler/StartSandboxHandler';
27
28const TAG = 'OpeningDialogManager';
29
30interface DecryptingState {
31  uri: string,
32  dialogTimeout: boolean, // 弹框是否超过250ms,拉弹框之前状态设置未false,超时回调设置为true
33  needStartAbility: boolean, // 弹框超时后,是否需要拉沙箱
34  hasDecrypted: boolean, // 用于判断是否是用户主动取消“正在打开弹框”场景
35  needShowDialog: boolean, // 用于判断是否需要“正在打开”弹框
36  isZip: boolean, // 用于zip格式,用户取消“正在打开”弹框判断
37}
38
39export default class OpeningDialogManager {
40  private static instance: OpeningDialogManager;
41  // 是否正在弹框
42  private _showDialogState: boolean = false;
43  // SEA拉起UEA后,是否注册回调
44  private _hasCallback: boolean = false;
45  // 是否正在处理弹框逻辑
46  private _isChargeDecrypting: boolean = false;
47  private _decryptingMap: Map<string, DecryptingState>;
48  private _isTerminalSelf: boolean = false;
49  private _isWaitingShowToast: boolean = false;
50  private checkShowDialogStateEvent = async () => {
51    HiLog.info(TAG, 'receive CHECK_SHOW_DIALOG_STATE');
52    this.checkShowDialogState();
53  };
54
55  private constructor() {
56    this._decryptingMap = new Map<string, DecryptingState>();
57    emitter.on(Constants.CHECK_SHOW_DIALOG_STATE, this.checkShowDialogStateEvent);
58  }
59
60  public unRegisterEmitEvent() {
61    HiLog.debug(TAG, 'unRegisterEmitEvent');
62    emitter.off(Constants.CHECK_SHOW_DIALOG_STATE, this.checkShowDialogStateEvent);
63  }
64
65  private printAllDecryptingMap(): void {
66    HiLog.debug(TAG, `printAllDecryptingMap ${this._decryptingMap.size}`);
67    this._decryptingMap.forEach((value, key) => {
68      HiLog.debug(TAG, `key ${FileUtils.getFileNameByUri(key)}, uri ${FileUtils.getFileNameByUri(value.uri)},
69      dialogTimeout ${value.dialogTimeout}, needStartAbility ${value.needStartAbility},
70      hasDecrypted ${value.hasDecrypted}, needShowDialog ${value.needShowDialog}, isZip ${value.isZip}`);
71    });
72  }
73
74  static getInstance(): OpeningDialogManager {
75    if (!OpeningDialogManager.instance) {
76      OpeningDialogManager.instance = new OpeningDialogManager();
77    }
78    return OpeningDialogManager.instance;
79  }
80
81  public setIsWaitingShowToast(value: boolean) {
82    this._isWaitingShowToast = value;
83  }
84
85  public getIsWaitingShowToast(): boolean {
86    return this._isWaitingShowToast;
87  }
88
89  public getShowDialogState(): boolean {
90    HiLog.debug(TAG, `getShowDialogState ${this._showDialogState}`);
91    return this._showDialogState;
92  }
93
94  public getHasCallback(): boolean {
95    HiLog.debug(TAG, `getHasCallback ${this._hasCallback}`);
96    return this._hasCallback;
97  }
98
99  public setIsTerminalSelf(value: boolean) {
100    HiLog.debug(TAG, `setIsTerminalSelf ${value}`);
101    this._isTerminalSelf = value;
102  }
103
104  public getIsTerminalSelf(): boolean {
105    HiLog.debug(TAG, `getIsTerminalSelf ${this._isTerminalSelf}`);
106    return this._isTerminalSelf;
107  }
108
109  public getIsDecryptingByRequestId(requestId: string): boolean {
110    this.printAllDecryptingMap();
111    const isDecrypting = this._decryptingMap.has(requestId);
112    HiLog.info(TAG, `getIsDecryptingByRequestId requestId ${requestId} isDecrypting ${isDecrypting}`);
113    return isDecrypting;
114  }
115
116  public getIsDecrypting(): boolean {
117    this.printAllDecryptingMap();
118    const isDecrypting = this._decryptingMap.size > 0;
119    HiLog.info(TAG, `getIsDecrypting isDecrypting ${isDecrypting}`);
120    return isDecrypting;
121  }
122
123  public setIsChargeDecrypting(value: boolean) {
124    HiLog.info(TAG, `setIsChargeDecrypting ${value}`);
125    this._isChargeDecrypting = value;
126  }
127
128  public getIsChargeDecrypting(): boolean {
129    HiLog.info(TAG, `getIsChargeDecrypting ${this._isChargeDecrypting}`);
130    return this._isChargeDecrypting;
131  }
132
133  public deleteRequestId(requestId: string) {
134    this.printAllDecryptingMap();
135    HiLog.info(TAG, `deleteRequestId requestId ${requestId}`);
136    this._decryptingMap.delete(requestId);
137  }
138
139  // 判断是否需要show toast,这次不需要就等timeout的回调
140  public getCanShowToast(): boolean {
141    this.printAllDecryptingMap();
142    const requestId = this.getDecryptingRequestIdByToast();
143    if (!requestId) {
144      HiLog.error(TAG, 'no getDecryptingRequestIdByToast');
145      return true;
146    }
147    const decryptingState = this._decryptingMap.get(requestId);
148    const dialogTimeout = decryptingState?.dialogTimeout ?? true;
149    const needShowDialog = decryptingState?.needShowDialog ?? true;
150    HiLog.info(TAG, `getCanShowToast dialogTimeout ${dialogTimeout} needShowDialog ${needShowDialog}
151    showDialogState ${this._showDialogState}`);
152    if (!needShowDialog || dialogTimeout) {
153      return true;
154    }
155    if (!dialogTimeout && this._showDialogState) {
156      return false;
157    }
158    return true;
159  }
160
161  // 判断是否需要拉起沙箱,这次不需要就等timeout的回调
162  public getNeedStartAbility(requestId: string): boolean {
163    this.printAllDecryptingMap();
164    HiLog.info(TAG, `OpeningDialogManager getNeedStartAbility requestId ${requestId}`);
165    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
166    if (!decryptingState) {
167      HiLog.error(TAG, 'getNeedStartAbility not isDecrypting');
168      return false;
169    }
170    const timeout = decryptingState.dialogTimeout;
171    HiLog.info(TAG, `OpeningDialogManager getNeedStartAbility requestId ${requestId}, timeout ${timeout},
172    showDialogState ${this._showDialogState}`);
173    if (!this._showDialogState || timeout) {
174      HiLog.info(TAG, 'can startAbility');
175      return true;
176    }
177    decryptingState.needStartAbility = true;
178    this.printAllDecryptingMap();
179    return false;
180  }
181
182  // 判断是否可以拉起沙箱,即用户是否手动终止弹框
183  public getCanStartAbility(requestId: string): boolean {
184    this.printAllDecryptingMap();
185    const isDecrypting = this._decryptingMap.has(requestId);
186    HiLog.info(TAG, `getCanStartAbility requestId ${requestId}, isDecrypting ${isDecrypting}`);
187    return isDecrypting;
188  }
189
190  // 弹框消失的回调,需要判断是否是用户主动终止解密流程
191  public async dialogDisappear(requestId: string): Promise<void> {
192    this.printAllDecryptingMap();
193    HiLog.info(TAG, `OpeningDialogManager dialogDisappear requestId ${requestId}`);
194    this._showDialogState = false;
195    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
196    if (!decryptingState) {
197      HiLog.info(TAG, 'dialogDisappear uri is not decrypting');
198      return;
199    }
200    const hasDecrypted = decryptingState.hasDecrypted;
201    const isZip = decryptingState.isZip;
202    HiLog.info(TAG, `dialogDisappear hasDecrypted ${hasDecrypted} isZip ${isZip}`);
203    if (!hasDecrypted || (!hasDecrypted && isZip)) { // 如果正在解密且不需要拉弹框,是用户主动终止解密流程
204      HiLog.info(TAG, 'user close opening dialog');
205      await OpenDlpFileManager.getInstance().deleteStatus(decryptingState.uri);
206      this._decryptingMap.delete(requestId);
207      this._isChargeDecrypting = false;
208      this.printAllDecryptingMap();
209    }
210  }
211
212  // 弹框超过250ms的回调
213  public async dialogTimeout(requestId: string): Promise<void> {
214    this.printAllDecryptingMap();
215    HiLog.info(TAG, `OpeningDialogManager dialogTimeout requestId ${requestId}`);
216    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
217    if (!decryptingState) {
218      HiLog.info(TAG, 'dialogTimeout uri is not decrypting');
219      await ErrorManager.getInstance().startHandleError(requestId);
220      return;
221    }
222    decryptingState.dialogTimeout = true;
223    await ErrorManager.getInstance().startHandleError(requestId);
224    this.printAllDecryptingMap();
225    if (decryptingState.needStartAbility) { // 弹框超时,且需要拉起沙箱,就去拉沙箱
226      const startSandboxRet = await StartSandboxHandler.getInstance().startSandbox();
227      if (startSandboxRet.errcode !== Constants.ERR_CODE_SUCCESS) {
228        HiLog.error(TAG, 'OpeningDialogManager startSandbox error');
229      }
230      this._decryptingMap.delete(requestId);
231    }
232  }
233
234  private async showDialog(requestId: string): Promise<void> {
235    this.printAllDecryptingMap();
236    this._showDialogState = await ViewAbilityService.getInstance().showDialog(true, requestId);
237    HiLog.info(TAG, `OpeningDialogManager showDialog requestId: ${requestId}, state: ${this._showDialogState}`);
238  }
239
240  private async hideDialog(): Promise<void> {
241    HiLog.info(TAG, 'OpeningDialogManager hideDialog start');
242    await ViewAbilityService.getInstance().showDialog(false);
243  }
244
245  private getDecryptingRequestId(): string | undefined {
246    this.printAllDecryptingMap();
247    for (const entry of this._decryptingMap) {
248      if (!entry[1].needStartAbility && !entry[1].hasDecrypted) {
249        HiLog.info(TAG, `getDecryptingRequestId found requestId ${entry[0]}`);
250        return entry[0];
251      }
252    }
253    HiLog.info(TAG, 'getDecryptingRequestId not found');
254    return undefined;
255  }
256
257  private getDecryptingRequestIdByToast(): string | undefined {
258    this.printAllDecryptingMap();
259    for (const entry of this._decryptingMap) {
260      if (entry[1].hasDecrypted) {
261        HiLog.info(TAG, `getDecryptingRequestIdByToast found requestId ${entry[0]}`);
262        return entry[0];
263      }
264    }
265    HiLog.info(TAG, 'getDecryptingRequestIdByToast not found');
266    return undefined;
267  }
268
269
270  // 根据解密状态,校验是否需要拉起弹框
271  public async checkShowDialogState(): Promise<void> {
272    this.printAllDecryptingMap();
273    this._hasCallback = true;
274    const isDecrypting = this._decryptingMap.size > 0;
275    const requestId = this.getDecryptingRequestId();
276    HiLog.info(TAG, `OpeningDialogManager checkShowDialogState showDialogState ${this._showDialogState},
277    isDecrypting ${isDecrypting}, requestId ${requestId}, isChargeDecrypting ${this._isChargeDecrypting}`);
278
279    if (!isDecrypting && !this._isChargeDecrypting) { // 没在处理弹框逻辑或解密逻辑,需要取消弹框
280      await this.hideDialog();
281      await ViewAbilityService.getInstance().sendDisconnectMsgWithTimeout();
282      return;
283    }
284
285    if (!requestId) {
286      HiLog.info(TAG, 'OpeningDialogManager checkShowDialogState getDecryptingRequestId not found');
287      return;
288    }
289    if (isDecrypting && !this._showDialogState) { // 正在解密,且没拉起弹框,需要拉起弹框
290      this._showDialogState = true;
291      await this.showDialog(requestId);
292      return;
293    }
294  }
295
296  // openDLPFile前调用,或者zip格式提前调用
297  public async showOpeningDialog(uri: string, requestId: string, needShowDialog: boolean,
298    isZip?: boolean): Promise<void> {
299    this.printAllDecryptingMap();
300    HiLog.info(TAG, 'OpeningDialogManager showOpeningDialog');
301    if (this._decryptingMap.has(requestId)) {
302      HiLog.info(TAG, 'OpeningDialogManager showOpeningDialog is decrypting');
303      return;
304    }
305    const decryptingState: DecryptingState = {
306      uri: uri,
307      dialogTimeout: false,
308      needStartAbility: false,
309      hasDecrypted: false,
310      needShowDialog: needShowDialog,
311      isZip: isZip ?? false,
312    }
313    this._decryptingMap.set(requestId, decryptingState);
314    this.printAllDecryptingMap();
315    if (this._showDialogState) {
316      HiLog.info(TAG, 'is showing OpeningDialog');
317      return;
318    }
319    if (!needShowDialog) {
320      HiLog.info(TAG, 'no need showDialog');
321      return;
322    }
323    await this.showDialog(requestId);
324  }
325
326  // 根据大小不需要弹框,但是调用penDLPFile接口超过500ms都没返回,拉起“正在打开”弹框
327  public async showOpeningDialogByTimeout(requestId: string): Promise<void> {
328    const viewContext = AppStorage.get('viewContext') as common.ServiceExtensionContext;
329    if (!viewContext) {
330      HiLog.error(TAG, 'showOpeningDialogByTimeout viewContext null');
331      return;
332    }
333    this.printAllDecryptingMap();
334    HiLog.info(TAG, 'OpeningDialogManager showOpeningDialogByTimeout');
335    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
336    if (!decryptingState) {
337      HiLog.error(TAG, 'showOpeningDialogByTimeout not isDecrypting');
338      return;
339    }
340    decryptingState.needShowDialog = true;
341    this.printAllDecryptingMap();
342    if (this._showDialogState) {
343      HiLog.info(TAG, 'showOpeningDialogByTimeout is showing OpeningDialog');
344      return;
345    }
346    await this.loadOpeningDialogUIExtAbility(viewContext);
347    await this.showDialog(requestId);
348  }
349
350  // openDLPFile结束后调用
351  public async hideOpeningDialog(requestId: string): Promise<void> {
352    this.printAllDecryptingMap();
353    HiLog.info(TAG, `OpeningDialogManager hideOpeningDialog requestId ${requestId}`);
354    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
355    if (!decryptingState) {
356      HiLog.info(TAG, 'hideOpeningDialog uri is not decrypting');
357      return;
358    }
359    decryptingState.hasDecrypted = true;
360    this.printAllDecryptingMap();
361    HiLog.info(TAG, `hideOpeningDialog showDialogState ${this._showDialogState}`);
362    if (!this._showDialogState) {
363      return;
364    }
365    await this.hideDialog();
366  }
367
368  // 拉沙箱之前调用
369  public setNeedStartAbility(requestId: string): void {
370    this.printAllDecryptingMap();
371    HiLog.info(TAG, `OpeningDialogManager setNeedStartAbility requestId ${requestId}`);
372    const decryptingState: DecryptingState | undefined = this._decryptingMap.get(requestId);
373    if (!decryptingState) {
374      HiLog.info(TAG, 'setNeedStartAbility uri is not decrypting');
375      return;
376    }
377    decryptingState.needStartAbility = true;
378    this._isChargeDecrypting = false;
379    this.printAllDecryptingMap();
380  }
381
382  // zip格式提前调用后因为各种原因失败
383  public async hideOpeningDialogByFailed(requestId: string): Promise<void> {
384    this.printAllDecryptingMap();
385    const isDecrypting = this._decryptingMap.has(requestId);
386    HiLog.info(TAG, `hideOpeningDialogByFailed requestId ${requestId}, isDecrypting ${isDecrypting}`);
387    if (!isDecrypting) {
388      HiLog.info(TAG, 'hideOpeningDialogByFailed uri is not decrypting');
389      return;
390    }
391    HiLog.info(TAG, `hideOpeningDialogByFailed showDialogState ${this._showDialogState}`);
392    if (!this._showDialogState) {
393      return;
394    }
395    await this.hideDialog();
396    await ViewAbilityService.getInstance().sendDisconnectMsgWithTimeout();
397  }
398
399  // 根据文件打包类型,文件大小判断是否拉起弹框
400  public async loadOpeningDialog(context: common.ServiceExtensionContext, fileSize: number,
401    openDlpFileData: OpenDlpFileData, state: DecryptState, parseType?: FileParseType): Promise<void> {
402    HiLog.info(TAG, `OpeningDialogManager loadOpeningDialog fileSize: ${fileSize} parseType: ${parseType}`);
403    if ((parseType === FileParseType.ZIP && fileSize >= Constants.DIALOG_SHOW_SIZE_ZIP) ||
404      (parseType === FileParseType.RAW && fileSize >= Constants.DIALOG_SHOW_SIZE_RAW)) {
405      HiLog.info(TAG, 'need show opening dialog');
406      openDlpFileData.needShowToast = true;
407      this._hasCallback = false;
408      await this.loadOpeningDialogUIExtAbility(context);
409      if (parseType === FileParseType.ZIP) {
410        HiLog.info(TAG, 'zip type show opening dialog advance');
411        await this.showOpeningDialog(openDlpFileData.uri, openDlpFileData.requestId, true, true);
412      }
413      return;
414    }
415    HiLog.info(TAG, 'no need show opening dialog');
416    this._hasCallback = true;
417  }
418
419  // viewAbility正常结束
420  public async unLoadOpeningDialogNormal(): Promise<void> {
421    this.printAllDecryptingMap();
422    this._isChargeDecrypting = false;
423    const isDecrypting = this._decryptingMap.size > 0;
424    HiLog.info(TAG, `OpeningDialogManager unLoadOpeningDialogNormal isDecrypting ${isDecrypting}`);
425    if (isDecrypting) {
426      HiLog.info(TAG, 'isDecrypting, no need hideDialog');
427      return;
428    }
429    await this.hideDialog();
430  }
431
432  // viewAbility异常结束
433  public async unLoadOpeningDialogAbnormal(): Promise<void> {
434    HiLog.info(TAG, 'unLoadOpeningDialogAbnormal start');
435    this._isChargeDecrypting = false;
436    await this.hideDialog();
437    await ViewAbilityService.getInstance().sendDisconnectMsg();
438  }
439
440  private async loadOpeningDialogUIExtAbility(context: common.ServiceExtensionContext): Promise<boolean> {
441    HiLog.info(TAG, 'begin loadOpeningDialog');
442    let uiExtWant: Want = {
443      bundleName: Constants.DLP_MANAGER_BUNDLE_NAME,
444      abilityName: Constants.DLP_OPENING_DIALOG_UI_EXT_ABILITY,
445      moduleName: 'entry',
446      parameters: {
447        'ability.want.params.uiExtensionType': 'sys/commonUI',
448      }
449    };
450    try {
451      await context.requestModalUIExtension(uiExtWant);
452      HiLog.info(TAG, 'requestModalUIExtension success');
453      return true;
454    } catch (err) {
455      HiLog.wrapError(TAG, err, 'requestModalUIExtension error');
456      return false;
457    }
458  }
459}