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