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