• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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 UIAbility from '@ohos.app.ability.UIAbility';
17import picker from '@ohos.file.picker';
18import Want from '@ohos.app.ability.Want';
19import AbilityConstant from '@ohos.app.ability.AbilityConstant';
20import window from '@ohos.window';
21import { BusinessError } from '@ohos.base';
22import ability from '@ohos.ability.ability';
23import dlpPermission from '@ohos.dlpPermission';
24import fs from '@ohos.file.fs';
25import fileUri from '@ohos.file.fileuri';
26import abilityManager from '@ohos.app.ability.abilityManager';
27import { getFileUriByPath, getFileFd, getAppId, isValidPath, defaultDlpFile } from '../common/utils';
28import Constants from '../common/constant';
29import GlobalContext from '../common/GlobalContext';
30import zlib from '@ohos.zlib';
31import { GetAlertMessage } from '../common/GetAlertMessage';
32import { HiLog } from '../common/HiLog';
33import dialogRequest from '@ohos.app.ability.dialogRequest';
34import AccountManager from '../manager/AccountManager';
35import FileUtil from '../common/external/FileUtil';
36
37const TAG = 'SaveAs';
38
39class ChangeOption {
40  public offset: number = 0
41  public length: number = 0
42}
43
44const SUFFIX_INDEX = 2;
45const HEAD_LENGTH_IN_BYTE = 20;
46const HEAD_LENGTH_IN_U32 = 5;
47const TXT_OFFSET = 3;
48const SIZE_OFFSET = 4;
49const ARGS_ZERO = 0;
50const ARGS_ONE = 1;
51const ARGS_TWO = 2;
52const FILENAME_MAX_LEN = 255;
53const FOPEN_EXCEPTION_IS_DIR = 13900019;
54const SRC_URI_OFFSET = 4;
55const ACTION: Record<string, string> = {
56  'SELECT_ACTION': 'ohos.want.action.OPEN_FILE',
57  'SELECT_ACTION_MODAL': 'ohos.want.action.OPEN_FILE_SERVICE',
58  'SAVE_ACTION': 'ohos.want.action.CREATE_FILE',
59  'SAVE_ACTION_MODAL': 'ohos.want.action.CREATE_FILE_SERVICE',
60};
61
62const errCode: Record<string, number> = {
63  'INVALID_ARGS': 13900020,
64  'RESULT_ERROR': 13900042,
65  'NAME_TOO_LONG': 13900030,
66};
67
68const ERRCODE_MAP = new Map([
69  [errCode.INVALID_ARGS, 'Invalid argument'],
70  [errCode.RESULT_ERROR, 'Unknown error'],
71  [errCode.NAME_TOO_LONG, 'File name too long'],
72]);
73export default class SaveAsAbility extends UIAbility {
74  private result: ability.AbilityResult = {
75    resultCode: -1,
76    want: {
77      bundleName: '',
78      abilityName: '',
79      parameters: {
80        pick_path_return: [],
81        pick_fd_return: 0
82      }
83    }
84  };
85  private dlpFile: dlpPermission.DLPFile = defaultDlpFile;
86  private sandboxBundleName: string = '';
87  private resultUri: string = '';
88  private tokenId: number = -1;
89  private requestCode: number = -1;
90  private fileName: string = '';
91  private suffix: string = '';
92  private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY;
93  private isOK: boolean = true; // use with requestModalUIExtension
94  private zipFlag: boolean = false;
95  private random: string = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END);
96  private filePath: string = '';
97  private dirPath: string = '';
98  private encFile: string = '';
99  private zipPath: string = '';
100  private zipName: string = '';
101  private file: fs.File | undefined = undefined;
102  private dstFdPicker: number = -1;
103  private newDlpFile: dlpPermission.DLPFile = defaultDlpFile;
104  private srcFdPicker: number = -1;
105  private linkFileName: string = '';
106
107  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
108    HiLog.info(TAG, `onCreate`);
109    await this.prepareDlpFile(want);
110    try {
111      await abilityManager.notifySaveAsResult(this.result, this.requestCode);
112    } catch (err) {
113      HiLog.error(TAG, `notifySaveAsResult failed: ${JSON.stringify(err)}`);
114    }
115    if (this.isOK) {
116      this.context.terminateSelf();
117    }
118  }
119
120  onDestroy(): void {
121    HiLog.info(TAG, `onDestroy`);
122  }
123
124  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
125    // Main window is created, set main page for this ability
126    HiLog.info(TAG, `onWindowStageCreate`);
127  }
128
129  onWindowStageDestroy(): void {
130    // Main window is destroyed, release UI related resources
131    HiLog.info(TAG, `onWindowStageDestroy`);
132  }
133
134  onForeground(): void {
135    // Ability has brought to foreground
136    HiLog.info(TAG, `onForeground`);
137  }
138
139  onBackground(): void {
140    // Ability has back to background
141    HiLog.info(TAG, `onBackground`);
142  }
143
144  async parseParams(want: Want): Promise<boolean | void> {
145    if (want.parameters === undefined) {
146      HiLog.error(TAG, `invalid want`);
147      return false;
148    }
149
150    this.requestCode = want.parameters?.['requestCode'] as number;
151    if (this.requestCode === undefined) {
152      HiLog.error(TAG, `invalid requestCode`);
153      return false;
154    }
155
156    this.tokenId = want.parameters?.['ohos.aafwk.param.callerToken'] as number;
157    if (this.tokenId === undefined) {
158      HiLog.error(TAG, `invalid tokenId`);
159      return false;
160    }
161    try {
162      await this.checkParseParams(want);
163      return true;
164    } catch {
165      return false;
166    }
167  }
168
169  checkParseParams(want: Want): Promise<void> {
170    return new Promise(async (resolve, reject) => {
171      this.authPerm = (GlobalContext.load('token2File') as
172      Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
173        .get(this.tokenId)?.[3] as dlpPermission.DLPFileAccess;
174      let dlpFileInfo = (GlobalContext.load('token2File') as
175      Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
176        .get(this.tokenId)?.[0] as dlpPermission.DLPFile;
177      let contactAccount = dlpFileInfo.dlpProperty.contactAccount as string;
178      if (this.authPerm != dlpPermission.DLPFileAccess.CONTENT_EDIT &&
179        this.authPerm != dlpPermission.DLPFileAccess.FULL_CONTROL) {
180        HiLog.error(TAG, `invalid authPerm: ${this.authPerm}`);
181        this.isOK = false;
182        AppStorage.setOrCreate('contactAccount', contactAccount);
183        let accountFlag = await AccountManager.checkAccountInfo(contactAccount);
184        if (!accountFlag) {
185          await GetAlertMessage.requestModalUIExtension(this.context, {
186            code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
187          reject();
188        }
189        await GetAlertMessage.requestModalUIExtension(this.context,
190          { code: Constants.ERR_JS_DLP_FILE_READ_ONLY } as BusinessError);
191        reject();
192      }
193      if (!(GlobalContext.load('token2File') as Map<number, Object[]>).has(this.tokenId)) {
194        HiLog.error(TAG, `invalid token2File`);
195        reject();
196      }
197      let newFileName = want.parameters?.['key_pick_file_name'] as [];
198      if (newFileName === undefined) {
199        HiLog.error(TAG, `invalid newFileName`);
200        reject();
201      }
202      this.fileName = newFileName.join('');
203      let splitNames = this.fileName.split('.');
204      HiLog.info(TAG, `splitNames: ${splitNames}`);
205      if (splitNames.length <= SUFFIX_INDEX) {
206        HiLog.error(TAG, `get suffix failed`);
207        reject();
208      }
209      this.suffix = splitNames[splitNames.length - SUFFIX_INDEX];
210      HiLog.info(TAG, `suffix is: ${this.suffix}`);
211      resolve();
212    })
213  }
214
215  deleteFile(file:string) {
216    try {
217      let res = fs.accessSync(file);
218      if (res) {
219        fs.unlinkSync(file);
220      }
221    } catch (err) {
222      HiLog.error(TAG, `deleteFile: ${JSON.stringify(err)}`);
223    }
224  }
225
226  rmDir(file:string) {
227    try {
228      let res = fs.accessSync(file);
229      if (res) {
230        fs.rmdirSync(file);
231      }
232    } catch (err) {
233      HiLog.error(TAG, `rmdirSync: ${JSON.stringify(err)}`);
234    }
235  }
236
237  async copyDlpHead(srcFd: number, dstFd: number) {
238    return new Promise<boolean>(async (resolve, reject) => {
239      let appDir = this.context.filesDir + '/';
240      this.filePath = appDir + 'saveAs' + this.random;
241      this.dirPath = appDir + 'saveAsUnzip' + this.random;
242      this.encFile = this.dirPath + '/encrypted_data';
243      this.zipPath = appDir + 'saveAsZip' + this.random;
244      this.zipName = this.zipPath + '.zip';
245      try {
246        let success = await this.copyDlpHeadData(srcFd, dstFd);
247        resolve(success);
248      } catch (err) {
249        HiLog.error(TAG, `copyDlpHead: ${JSON.stringify(err)}, message: ${JSON.stringify(err.message)}`);
250        resolve(false);
251      }
252      if (this.zipFlag) {
253        this.deleteFile(this.dirPath + '/encrypted_data');
254        this.deleteFile(this.dirPath + '/dlp_general_info');
255        this.deleteFile(this.dirPath + '/dlp_cert');
256        this.rmDir(this.dirPath);
257        this.deleteFile(this.filePath);
258        this.deleteFile(this.zipName);
259      }
260    })
261  }
262
263  async copyDlpHeadData(srcFd: number, dstFd: number) {
264    return new Promise<boolean>(async (resolve, reject) => {
265      let z = new ArrayBuffer(HEAD_LENGTH_IN_BYTE);
266      let option: ChangeOption = {
267        offset: 0,
268        length: HEAD_LENGTH_IN_BYTE
269      };
270      try {
271        fs.readSync(srcFd, z, option);
272      } catch (error) {
273        HiLog.error(TAG, `readSync exception, error is ${JSON.stringify(error)}`);
274        return;
275      }
276      let buf = new Uint32Array(z, 0, HEAD_LENGTH_IN_U32);
277      let magic = buf[0];
278
279      if (magic !== Constants.DLP_ZIP_MAGIC) {
280        let txtOffset = buf[TXT_OFFSET];
281        let head = new ArrayBuffer(txtOffset);
282        option = {
283          offset: 0,
284          length: txtOffset
285        };
286        try {
287          fs.readSync(srcFd, head, option);
288          let buf2 = new Uint32Array(head, 0, HEAD_LENGTH_IN_U32);
289          buf2[SIZE_OFFSET] = 0;
290          fs.writeSync(dstFd, head, option);
291          resolve(true);
292          return;
293        } catch (error) {
294          HiLog.error(TAG, `readSync or writeSync exception, error is ${JSON.stringify(error)}`);
295          return;
296        }
297      }
298
299      let openFile: fs.File | undefined;
300      let openZipFile: fs.File | undefined;
301      try {
302        this.zipFlag = true;
303        openFile = await fs.open(this.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
304        await fs.copyFile(srcFd, openFile.fd);
305        await fs.mkdir(this.dirPath);
306        let options: zlib.Options = {
307          level: zlib.CompressLevel.COMPRESS_LEVEL_NO_COMPRESSION,
308          memLevel:zlib.MemLevel.MEM_LEVEL_DEFAULT,
309          strategy:zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY,
310        };
311        await zlib.decompressFile(this.filePath, this.dirPath, options);
312        await fs.truncate(this.encFile);
313        await zlib.compressFile(this.dirPath, this.zipPath, options);
314        openZipFile = await fs.open(this.zipName, fs.OpenMode.READ_WRITE);
315        await fs.copyFile(openZipFile.fd, dstFd);
316        resolve(true);
317      } catch (err) {
318        HiLog.error(TAG, `open failed: ${JSON.stringify(err)}`);
319        resolve(false);
320      } finally {
321        FileUtil.closeFileSync(openFile);
322        FileUtil.closeFileSync(openZipFile);
323      }
324    })
325  }
326
327  isValidDirUri(uri: string | undefined) {
328    if (uri == undefined) {
329      return true;
330    }
331    let file: fs.File | undefined = undefined;
332    try {
333      let uriObj = new fileUri.FileUri(uri);
334      let dirUri = uriObj.getFullDirectoryUri(); // get directory path of a uri
335      file = fs.openSync(dirUri, fs.OpenMode.READ_ONLY);
336      let stat = fs.statSync(file.fd);
337      let isDir = stat.isDirectory();
338      HiLog.info(TAG, `test dir ${isDir ? 'is' : 'not'} a directory`);
339      return isDir;
340    } catch (e) {
341      let error = e as BusinessError;
342      if (error.code === FOPEN_EXCEPTION_IS_DIR) {
343        HiLog.info(TAG, `test dir is a directory`);
344        return true;
345      }
346      return false;
347    } finally {
348      FileUtil.closeFileSync(file);
349    }
350  }
351
352  getSourceFileName() {
353    try {
354      let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] =
355        (GlobalContext
356          .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
357          .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[];
358      let srcUri: string = token2FileValue[SRC_URI_OFFSET] as string;
359      let uriObj = new fileUri.FileUri(srcUri);
360      let dir = uriObj.getFullDirectoryUri(); // get directory path
361      let name = srcUri.replace(dir, '').replace('/', ''); // strip directory path and '/'
362      HiLog.info(TAG, 'getSourceFileName: ' + name);
363      return name;
364    } catch (e) {
365      let error = e as BusinessError;
366      HiLog.error(TAG, 'getSourceFileName error:' + error.message);
367      return '';
368    }
369  }
370
371  parseDocumentPickerSaveOption(args: picker.DocumentSaveOptions[], action: string) {
372    let config: Record<string, string | Record<string, Object>> = {
373      'action': action,
374      'parameters': {
375        'startMode': 'save',
376      } as Record<string, Object>
377    };
378
379    if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
380      let option: picker.DocumentSaveOptions = args[ARGS_ZERO];
381      if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) {
382        if (option.newFileNames[0] == undefined) { // if option name not provided, default to empty string
383          option.newFileNames[0] = '';
384        }
385        if (option.newFileNames[0].length > FILENAME_MAX_LEN) {
386          throw new Error('file name exceeds max length');
387        }
388        let srcName = this.getSourceFileName();
389        if (option.newFileNames[0].length > 0 && srcName != option.newFileNames[0]) {
390          HiLog.error(TAG, `src file name is not same with newFileName provided`);
391          throw new Error('picker filename error');
392        }
393        config.parameters['key_pick_file_name'] = option.newFileNames;
394        config.parameters['saveFile'] = option.newFileNames[0];
395      }
396
397      let isDir = this.isValidDirUri(option.defaultFilePathUri); // undefined returns true
398      if (isDir) {
399        config.parameters['key_pick_dir_path'] = option.defaultFilePathUri;
400      } else {
401        throw new Error(`defaultFilePathUri is not a valid directory uri`);
402      }
403      if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) {
404        config.parameters['key_file_suffix_choices'] = option.fileSuffixChoices;
405      }
406    }
407
408    HiLog.info(TAG, `[picker] Save config: ${JSON.stringify(config)}`);
409    return config;
410  }
411
412  getDocumentPickerSaveResult(args: dialogRequest.RequestResult | ability.AbilityResult) {
413    let saveResult: Record<string, BusinessError | string[]> = {
414      'error': {} as BusinessError,
415      'data': []
416    };
417
418    if (((args as dialogRequest.RequestResult).result !== undefined &&
419        (args as dialogRequest.RequestResult).result === 0) ||
420        ((args as ability.AbilityResult).resultCode !== undefined &&
421        (args as ability.AbilityResult).resultCode === 0)) {
422      if (args.want && args.want.parameters) {
423        if (args.want.parameters.pick_path_return) {
424          saveResult.data = args.want.parameters.pick_path_return as string[];
425        } else {
426          saveResult.data = args.want.parameters['ability.params.stream'] as string[];
427        }
428      }
429    } else if (((args as dialogRequest.RequestResult).result !== undefined &&
430        (args as dialogRequest.RequestResult).result === 0) ||
431        ((args as ability.AbilityResult).resultCode !== undefined &&
432        (args as ability.AbilityResult).resultCode === -1)) {
433      saveResult.data = [];
434    } else {
435      saveResult.error = this.getErr(errCode.RESULT_ERROR) as BusinessError;
436    }
437
438    HiLog.info(TAG, `[picker] Save saveResult: ${JSON.stringify(saveResult)}`);
439    return saveResult;
440  }
441
442  getErr(errCode: number) {
443    return {code: errCode, message: ERRCODE_MAP.get(errCode)} as BusinessError;
444  }
445
446  async documentPickerSave(...args: Object[]): Promise<BusinessError | string[] | undefined> {
447    let config: Record<string, string | Record<string, Object>>;
448    let result: dialogRequest.RequestResult | ability.AbilityResult;
449    try {
450      config = this.parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION);
451      result = await this.context.startAbilityForResult(config, {windowMode: 0});
452    } catch (error) {
453      HiLog.error(TAG, `startAbilityForResult error: ${JSON.stringify(error)}`);
454      return undefined;
455    }
456    HiLog.info(TAG, `[picker] Save result: ${JSON.stringify(result)}`);
457    try {
458      const saveResult: Record<string, BusinessError | string[]> = this.getDocumentPickerSaveResult(result);
459      if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
460        return (args[ARGS_ONE] as Function)(saveResult.error, saveResult.data);
461      } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
462        return (args[ARGS_ZERO] as Function)(saveResult.error, saveResult.data);
463      }
464      return new Promise<BusinessError | string[]>((resolve, reject) => {
465        if (saveResult.data !== undefined) {
466          resolve(saveResult.data);
467        } else {
468          reject(saveResult.error);
469        }
470      })
471    } catch (resultError) {
472      HiLog.error(TAG, `[picker] Result error: ${resultError}`);
473    }
474    return undefined;
475  }
476
477  async prepareDlpFile(want: Want): Promise<void> {
478    HiLog.info(TAG, `getFile start prepareDlpFile`);
479    let uri = '';
480    let displayName = '';
481    let ret = await this.parseParams(want);
482    if (!ret) {
483      HiLog.error(TAG, `parseParams failed`);
484      return;
485    }
486    let documentSaveOptions = new picker.DocumentSaveOptions();
487    displayName = this.fileName;
488    documentSaveOptions.newFileNames = [displayName];
489    documentSaveOptions.fileSuffixChoices = [`.${this.suffix}.dlp`];
490    try {
491      let saveRes: BusinessError | string[] | undefined = await this.documentPickerSave(documentSaveOptions);
492      if (saveRes == undefined || (saveRes instanceof Array && saveRes.length == 0)) {
493        HiLog.error(TAG, `fail to get uri`);
494        return;
495      }
496      uri = saveRes[0]
497      if (!isValidPath(uri)) {
498        HiLog.error(TAG, `invalid uri`);
499        return;
500      }
501      try {
502        await this.fileOpen(uri);
503        await this.pickerDialog(uri, want);
504      } catch {
505        HiLog.error(TAG, `fileOpen or pickerDialog start failed`);
506        return;
507      }
508    } catch (err) {
509      HiLog.error(TAG, `DocumentViewPicker failed: ${JSON.stringify(err)}`);
510      FileUtil.closeFileSync(this.file);
511      FileUtil.unlinkSync(uri);
512      this.isOK = false;
513      await GetAlertMessage.requestModalUIExtension(this.context,
514        { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
515      return;
516    }
517  }
518
519  pickerDialog(uri: string, want: Want): Promise<void> {
520    return new Promise(async (resolve, reject) => {
521      let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] =
522        (GlobalContext
523        .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
524        .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[];
525      this.dlpFile = token2FileValue[0] as dlpPermission.DLPFile;
526      this.sandboxBundleName = token2FileValue[1] as string;
527      let appId: number = token2FileValue[2] as number;
528      let srcUri: string = token2FileValue[4] as string;
529      this.srcFdPicker = getFileFd(srcUri);
530      let success = await this.copyDlpHead(this.srcFdPicker, this.dstFdPicker);
531      if (!success) {
532        FileUtil.closeFileSync(this.file);
533        FileUtil.unlinkSync(uri);
534        FileUtil.closeFdSync(this.srcFdPicker);
535        this.isOK = false;
536        await GetAlertMessage.requestModalUIExtension(this.context,
537          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
538        reject();
539      }
540      try {
541        await this.openDLPFile(uri, want);
542        await this.addDLPLinkFile(appId, uri);
543      } catch {
544        FileUtil.closeFileSync(this.file);
545        FileUtil.unlinkSync(uri);
546        FileUtil.closeFdSync(this.srcFdPicker);
547        reject();
548      }
549      let linkFilePath = Constants.FUSE_PATH + this.linkFileName;
550      let linkUri = getFileUriByPath(linkFilePath);
551      (GlobalContext.load('token2File') as Map<number, Object[]>)
552        .set(this.tokenId, [this.dlpFile, this.sandboxBundleName, appId, this.authPerm, srcUri]);
553      let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> =
554        GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>;
555      sandbox2linkFile.get(this.sandboxBundleName + appId)
556        ?.push([this.newDlpFile, this.linkFileName, this.dstFdPicker, this.tokenId]);
557      (GlobalContext.load('fileOpenHistory') as Map<string, Object[]>)
558        .set(uri, [this.sandboxBundleName, appId, this.linkFileName, linkUri]);
559      (GlobalContext.load('linkSet') as Set<string>).add(linkUri);
560      this.resultUri = getFileUriByPath(linkFilePath);
561      (this.result.want?.parameters?.pick_path_return as string[]).push(this.resultUri);
562      this.result.resultCode = 0;
563      FileUtil.closeFdSync(this.srcFdPicker);
564      resolve();
565    })
566  }
567
568  fileOpen(uri: string): Promise<void> {
569    return new Promise(async (resolve, reject) => {
570      try {
571        this.file = await fs.open(uri, fs.OpenMode.READ_WRITE);
572        this.dstFdPicker = this.file.fd;
573        resolve();
574      } catch (err) {
575        HiLog.error(TAG, `open: ${uri}, failed: ${JSON.stringify(err)}`);
576        FileUtil.unlinkSync(uri);
577        this.isOK = false;
578        await GetAlertMessage.requestModalUIExtension(this.context,
579          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
580        reject();
581      }
582    })
583  }
584
585  openDLPFile(uri: string, want: Want): Promise<void> {
586    return new Promise(async (resolve, reject) => {
587      let callerAppId: string;
588      try {
589        let callerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string;
590        callerAppId = await getAppId(callerBundleName);
591        HiLog.info(TAG, `get AppId: ${callerAppId}`);
592      } catch {
593        reject();
594        return;
595      }
596      try {
597        this.newDlpFile = await dlpPermission.openDLPFile(this.dstFdPicker, callerAppId);
598      } catch (err) {
599        HiLog.error(TAG, `generateDlpFile: ${this.dstFdPicker}, failed: ${JSON.stringify(err)}`);
600        await GetAlertMessage.requestModalUIExtension(this.context, err);
601        FileUtil.closeFileSync(this.file);
602        FileUtil.unlinkSync(uri);
603        FileUtil.closeFdSync(this.srcFdPicker);
604        reject();
605      }
606      resolve();
607    })
608  }
609
610  async addDLPLinkFile(appId: number, uri: string): Promise<void> {
611    return new Promise(async (resolve, reject) => {
612      let date = new Date();
613      let timestamp = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
614        date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getMilliseconds()).getTime();
615
616      this.linkFileName = String(this.sandboxBundleName).substring(0, Constants.BUNDLE_LEN) + '_' + appId +
617        '_' + timestamp + String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END) + '.dlp.link.' +
618      this.suffix;
619
620      try {
621        await this.newDlpFile.addDLPLinkFile(this.linkFileName);
622      } catch (err) {
623        HiLog.error(TAG, `addDlpLinkFile failed: ${JSON.stringify(err)}`);
624        try {
625          await this.newDlpFile.closeDLPFile();
626        } catch (err) {
627          HiLog.error(TAG, `closeDlpFile failed: ${JSON.stringify(err)}`);
628        }
629        FileUtil.closeFileSync(this.file);
630        FileUtil.unlinkSync(uri);
631        FileUtil.closeFdSync(this.srcFdPicker);
632        this.isOK = false;
633        await GetAlertMessage.requestModalUIExtension(this.context,
634          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
635        reject();
636      }
637      resolve();
638    })
639  }
640};
641