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'; 34 35const TAG = 'SaveAs'; 36 37class ChangeOption { 38 public offset: number = 0 39 public length: number = 0 40} 41 42const SUFFIX_INDEX = 2; 43const HEAD_LENGTH_IN_BYTE = 20; 44const HEAD_LENGTH_IN_U32 = 5; 45const TXT_OFFSET = 3; 46const SIZE_OFFSET = 4; 47const ARGS_ZERO = 0; 48const ARGS_ONE = 1; 49const ARGS_TWO = 2; 50const FILENAME_MAX_LEN = 255; 51const FOPEN_EXCEPTION_IS_DIR = 13900019; 52const SRC_URI_OFFSET = 4; 53const ACTION: Record<string, string> = { 54 'SELECT_ACTION': 'ohos.want.action.OPEN_FILE', 55 'SELECT_ACTION_MODAL': 'ohos.want.action.OPEN_FILE_SERVICE', 56 'SAVE_ACTION': 'ohos.want.action.CREATE_FILE', 57 'SAVE_ACTION_MODAL': 'ohos.want.action.CREATE_FILE_SERVICE', 58}; 59 60const errCode: Record<string, number> = { 61 'INVALID_ARGS': 13900020, 62 'RESULT_ERROR': 13900042, 63 'NAME_TOO_LONG': 13900030, 64}; 65 66const ERRCODE_MAP = new Map([ 67 [errCode.INVALID_ARGS, 'Invalid argument'], 68 [errCode.RESULT_ERROR, 'Unknown error'], 69 [errCode.NAME_TOO_LONG, 'File name too long'], 70]); 71export default class SaveAsAbility extends UIAbility { 72 private result: ability.AbilityResult = { 73 resultCode: -1, 74 want: { 75 bundleName: '', 76 abilityName: '', 77 parameters: { 78 pick_path_return: [], 79 pick_fd_return: 0 80 } 81 } 82 }; 83 private dlpFile: dlpPermission.DLPFile = defaultDlpFile; 84 private sandboxBundleName: string = ''; 85 private resultUri: string = ''; 86 private tokenId: number = -1; 87 private requestCode: number = -1; 88 private fileName: string = ''; 89 private suffix: string = ''; 90 private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY; 91 private isOK: boolean = true; // use with startAlertAbility 92 private zipFlag: boolean = false; 93 private random: string = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END); 94 private filePath: string = ''; 95 private dirPath: string = ''; 96 private encFile: string = ''; 97 private zipPath: string = ''; 98 private ff: fs.File | undefined = undefined; 99 private zf2: fs.File | undefined = undefined; 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 GetAlertMessage.checkAccountInfo(contactAccount); 184 if (!accountFlag) { 185 await GetAlertMessage.startAlertAbility(this.context, { 186 code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 187 reject(); 188 } 189 await GetAlertMessage.startAlertAbility(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 try { 251 await fs.close(this.ff); 252 await fs.close(this.zf2); 253 } catch (error) { 254 HiLog.error(TAG, `close: ${JSON.stringify(error)}`); 255 } 256 resolve(false); 257 } 258 if (this.zipFlag) { 259 this.deleteFile(this.dirPath + '/encrypted_data'); 260 this.deleteFile(this.dirPath + '/dlp_general_info'); 261 this.deleteFile(this.dirPath + '/dlp_cert'); 262 this.rmDir(this.dirPath); 263 this.deleteFile(this.filePath); 264 this.deleteFile(this.zipName); 265 } 266 }) 267 } 268 269 async copyDlpHeadData(srcFd: number, dstFd: number) { 270 return new Promise<boolean>(async (resolve, reject) => { 271 let z = new ArrayBuffer(HEAD_LENGTH_IN_BYTE); 272 let option: ChangeOption = { 273 offset: 0, 274 length: HEAD_LENGTH_IN_BYTE 275 }; 276 let num = fs.readSync(srcFd, z, option); 277 let buf = new Uint32Array(z, 0, HEAD_LENGTH_IN_U32); 278 let magic = buf[0]; 279 280 if (magic !== Constants.DLP_ZIP_MAGIC) { 281 let txtOffset = buf[TXT_OFFSET]; 282 let head = new ArrayBuffer(txtOffset); 283 option = { 284 offset: 0, 285 length: txtOffset 286 }; 287 num = fs.readSync(srcFd, head, option); 288 let buf2 = new Uint32Array(head, 0, HEAD_LENGTH_IN_U32); 289 buf2[SIZE_OFFSET] = 0; 290 num = fs.writeSync(dstFd, head, option); 291 resolve(true); 292 return; 293 } 294 295 try { 296 this.zipFlag = true; 297 this.ff = await fs.open(this.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 298 await fs.copyFile(srcFd, this.ff.fd); 299 await fs.mkdir(this.dirPath); 300 let options: zlib.Options = { 301 level: zlib.CompressLevel.COMPRESS_LEVEL_NO_COMPRESSION, 302 memLevel:zlib.MemLevel.MEM_LEVEL_DEFAULT, 303 strategy:zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY, 304 }; 305 await zlib.decompressFile(this.filePath, this.dirPath, options); 306 await fs.truncate(this.encFile); 307 await zlib.compressFile(this.dirPath, this.zipPath, options); 308 this.zf2 = await fs.open(this.zipName, fs.OpenMode.READ_WRITE); 309 await fs.copyFile(this.zf2.fd, dstFd); 310 resolve(true); 311 } catch (err) { 312 HiLog.error(TAG, `open failed: ${JSON.stringify(err)}`); 313 resolve(false); 314 } finally { 315 if (this.ff || this.zf2) { 316 await fs.close(this.ff); 317 await fs.close(this.zf2); 318 } 319 } 320 }) 321 } 322 323 isValidDirUri(uri: string | undefined) { 324 if (uri == undefined) { 325 return true; 326 } 327 let uriObj = new fileUri.FileUri(uri); 328 let dirUri = uriObj.getFullDirectoryUri(); // get directory path of a uri 329 let file: fs.File | undefined = undefined; 330 try { 331 file = fs.openSync(dirUri, fs.OpenMode.READ_ONLY); 332 let stat = fs.statSync(file.fd); 333 let isDir = stat.isDirectory(); 334 HiLog.info(TAG, `test dir ${isDir ? 'is' : 'not'} a directory`); 335 return isDir; 336 } catch (e) { 337 let error = e as BusinessError; 338 if (error.code === FOPEN_EXCEPTION_IS_DIR) { 339 HiLog.info(TAG, `test dir is a directory`); 340 return true; 341 } 342 return false; 343 } finally { 344 if (file != undefined) { 345 fs.closeSync(file.fd); 346 } 347 } 348 } 349 350 getSourceFileName() { 351 try { 352 let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] = 353 (GlobalContext 354 .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>) 355 .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]; 356 let srcUri: string = token2FileValue[SRC_URI_OFFSET] as string; 357 let uriObj = new fileUri.FileUri(srcUri); 358 let dir = uriObj.getFullDirectoryUri(); // get directory path 359 let name = srcUri.replace(dir, '').replace('/', ''); // strip directory path and '/' 360 HiLog.info(TAG, 'getSourceFileName: ' + name); 361 return name; 362 } catch (e) { 363 let error = e as BusinessError; 364 HiLog.error(TAG, 'getSourceFileName error:' + error.message); 365 return ''; 366 } 367 } 368 369 parseDocumentPickerSaveOption(args: picker.DocumentSaveOptions[], action: string) { 370 let config: Record<string, string | Record<string, Object>> = { 371 'action': action, 372 'parameters': { 373 'startMode': 'save', 374 } as Record<string, Object> 375 }; 376 377 if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') { 378 let option: picker.DocumentSaveOptions = args[ARGS_ZERO]; 379 if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) { 380 if (option.newFileNames[0] == undefined) { // if option name not provided, default to empty string 381 option.newFileNames[0] = ''; 382 } 383 if (option.newFileNames[0].length > FILENAME_MAX_LEN) { 384 throw new Error('file name exceeds max length'); 385 } 386 let srcName = this.getSourceFileName(); 387 if (option.newFileNames[0].length > 0 && srcName != option.newFileNames[0]) { 388 HiLog.error(TAG, `src file name is not same with newFileName provided`); 389 throw new Error('picker filename error'); 390 } 391 config.parameters['key_pick_file_name'] = option.newFileNames; 392 config.parameters['saveFile'] = option.newFileNames[0]; 393 } 394 395 let isDir = this.isValidDirUri(option.defaultFilePathUri); // undefined returns true 396 if (isDir) { 397 config.parameters['key_pick_dir_path'] = option.defaultFilePathUri; 398 } else { 399 throw new Error(`defaultFilePathUri is not a valid directory uri`); 400 } 401 if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) { 402 config.parameters['key_file_suffix_choices'] = option.fileSuffixChoices; 403 } 404 } 405 406 HiLog.info(TAG, `[picker] Save config: ${JSON.stringify(config)}`); 407 return config; 408 } 409 410 getDocumentPickerSaveResult(args: dialogRequest.RequestResult | ability.AbilityResult) { 411 let saveResult: Record<string, BusinessError | string[]> = { 412 'error': {} as BusinessError, 413 'data': [] 414 }; 415 416 if (((args as dialogRequest.RequestResult).result !== undefined && 417 (args as dialogRequest.RequestResult).result === 0) || 418 ((args as ability.AbilityResult).resultCode !== undefined && 419 (args as ability.AbilityResult).resultCode === 0)) { 420 if (args.want && args.want.parameters) { 421 if (args.want.parameters.pick_path_return) { 422 saveResult.data = args.want.parameters.pick_path_return as string[]; 423 } else { 424 saveResult.data = args.want.parameters['ability.params.stream'] as string[]; 425 } 426 } 427 } else if (((args as dialogRequest.RequestResult).result !== undefined && 428 (args as dialogRequest.RequestResult).result === 0) || 429 ((args as ability.AbilityResult).resultCode !== undefined && 430 (args as ability.AbilityResult).resultCode === -1)) { 431 saveResult.data = []; 432 } else { 433 saveResult.error = this.getErr(errCode.RESULT_ERROR) as BusinessError; 434 } 435 436 HiLog.info(TAG, `[picker] Save saveResult: ${JSON.stringify(saveResult)}`); 437 return saveResult; 438 } 439 440 getErr(errCode: number) { 441 return {code: errCode, message: ERRCODE_MAP.get(errCode)} as BusinessError; 442 } 443 444 async documentPickerSave(...args: Object[]): Promise<BusinessError | string[] | undefined> { 445 let config: Record<string, string | Record<string, Object>>; 446 let result: dialogRequest.RequestResult | ability.AbilityResult; 447 try { 448 config = this.parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION); 449 result = await this.context.startAbilityForResult(config, {windowMode: 0}); 450 } catch (error) { 451 HiLog.info(TAG, `startAbilityForResult${JSON.stringify(error)}`); 452 return undefined; 453 } 454 HiLog.info(TAG, `[picker] Save result: ${JSON.stringify(result)}`); 455 try { 456 const saveResult: Record<string, BusinessError | string[]> = this.getDocumentPickerSaveResult(result); 457 if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { 458 return (args[ARGS_ONE] as Function)(saveResult.error, saveResult.data); 459 } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { 460 return (args[ARGS_ZERO] as Function)(saveResult.error, saveResult.data); 461 } 462 return new Promise<BusinessError | string[]>((resolve, reject) => { 463 if (saveResult.data !== undefined) { 464 resolve(saveResult.data); 465 } else { 466 reject(saveResult.error); 467 } 468 }) 469 } catch (resultError) { 470 HiLog.info(TAG, `[picker] Result error: ${resultError}`); 471 } 472 return undefined; 473 } 474 475 async prepareDlpFile(want: Want): Promise<void> { 476 HiLog.info(TAG, `getFile start prepareDlpFile`); 477 let uri = ''; 478 let displayName = ''; 479 let ret = await this.parseParams(want); 480 if (!ret) { 481 HiLog.info(TAG, `parseParams failed`); 482 return; 483 } 484 let documentSaveOptions = new picker.DocumentSaveOptions(); 485 displayName = this.fileName; 486 documentSaveOptions.newFileNames = [displayName]; 487 documentSaveOptions.fileSuffixChoices = [`.${this.suffix}.dlp`]; 488 try { 489 let saveRes: BusinessError | string[] | undefined = await this.documentPickerSave(documentSaveOptions); 490 if (saveRes == undefined || (saveRes instanceof Array && saveRes.length == 0)) { 491 HiLog.error(TAG, `fail to get uri`); 492 return; 493 } 494 uri = saveRes[0] 495 if (!isValidPath(uri)) { 496 HiLog.error(TAG, `invalid uri`); 497 return; 498 } 499 try { 500 await this.fileOpen(uri); 501 await this.pickerDialog(uri, want); 502 } catch { 503 HiLog.info(TAG, `fileOpen or pickerDialog start failed`); 504 return; 505 } 506 } catch (err) { 507 HiLog.error(TAG, `DocumentViewPicker failed: ${JSON.stringify(err)}`); 508 try { 509 if (this.file != undefined) { 510 await fs.close(this.file); 511 } 512 await this.deleteUri(uri); 513 } catch (err) { 514 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 515 } 516 this.isOK = false; 517 await GetAlertMessage.startAlertAbility(this.context, 518 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 519 return; 520 } 521 } 522 523 pickerDialog(uri: string, want: Want): Promise<void> { 524 return new Promise(async (resolve, reject) => { 525 let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] = 526 (GlobalContext 527 .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>) 528 .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]; 529 this.dlpFile = token2FileValue[0] as dlpPermission.DLPFile; 530 this.sandboxBundleName = token2FileValue[1] as string; 531 let appId: number = token2FileValue[2] as number; 532 let srcUri: string = token2FileValue[4] as string; 533 this.srcFdPicker = getFileFd(srcUri); 534 let success = await this.copyDlpHead(this.srcFdPicker, this.dstFdPicker); 535 if (!success) { 536 try { 537 await fs.close(this.file); 538 await this.deleteUri(uri); 539 } catch (err) { 540 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 541 } 542 fs.closeSync(this.srcFdPicker); 543 this.isOK = false; 544 await GetAlertMessage.startAlertAbility(this.context, 545 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 546 reject(); 547 } 548 try { 549 await this.openDLPFile(uri, want); 550 await this.addDLPLinkFile(appId, uri); 551 } catch { 552 reject(); 553 } 554 let linkFilePath = Constants.FUSE_PATH + this.linkFileName; 555 let linkUri = getFileUriByPath(linkFilePath); 556 (GlobalContext.load('token2File') as Map<number, Object[]>) 557 .set(this.tokenId, [this.dlpFile, this.sandboxBundleName, appId, this.authPerm, srcUri]); 558 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = 559 GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 560 sandbox2linkFile.get(this.sandboxBundleName + appId) 561 ?.push([this.newDlpFile, this.linkFileName, this.dstFdPicker, this.tokenId]); 562 (GlobalContext.load('fileOpenHistory') as Map<string, Object[]>) 563 .set(uri, [this.sandboxBundleName, appId, this.linkFileName, linkUri]); 564 (GlobalContext.load('linkSet') as Set<string>).add(linkUri); 565 this.resultUri = getFileUriByPath(linkFilePath); 566 (this.result.want?.parameters?.pick_path_return as string[]).push(this.resultUri); 567 this.result.resultCode = 0; 568 fs.closeSync(this.srcFdPicker); 569 resolve(); 570 }) 571 } 572 573 fileOpen(uri: string): Promise<void> { 574 return new Promise(async (resolve, reject) => { 575 try { 576 this.file = await fs.open(uri, fs.OpenMode.READ_WRITE); 577 this.dstFdPicker = this.file.fd; 578 resolve(); 579 } catch (err) { 580 HiLog.error(TAG, `open: ${uri}, failed: ${JSON.stringify(err)}`); 581 try { 582 if (this.file != undefined) { 583 await fs.close(this.file); 584 } 585 await this.deleteUri(uri); 586 } catch (err) { 587 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 588 } 589 this.isOK = false; 590 await GetAlertMessage.startAlertAbility(this.context, 591 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 592 reject(); 593 } 594 }) 595 } 596 597 openDLPFile(uri: string, want: Want): Promise<void> { 598 return new Promise(async (resolve, reject) => { 599 let callerAppId: string; 600 try { 601 let callerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string; 602 callerAppId = await getAppId(callerBundleName); 603 HiLog.info(TAG, `get AppId: ${callerAppId}`); 604 } catch { 605 reject(); 606 return; 607 } 608 try { 609 this.newDlpFile = await dlpPermission.openDLPFile(this.dstFdPicker, callerAppId); 610 } catch (err) { 611 HiLog.error(TAG, `generateDlpFile: ${this.dstFdPicker}, failed: ${JSON.stringify(err)}`); 612 await GetAlertMessage.startAlertAbility(this.context, err); 613 try { 614 await fs.close(this.file); 615 await this.deleteUri(uri); 616 } catch (err) { 617 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 618 } 619 fs.closeSync(this.srcFdPicker); 620 reject(); 621 } 622 resolve(); 623 }) 624 } 625 626 async addDLPLinkFile(appId: number, uri: string): Promise<void> { 627 return new Promise(async (resolve, reject) => { 628 let date = new Date(); 629 let timestamp = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 630 date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getMilliseconds()).getTime(); 631 632 this.linkFileName = String(this.sandboxBundleName).substring(0, Constants.BUNDLE_LEN) + '_' + appId + 633 '_' + timestamp + String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END) + '.dlp.link.' + 634 this.suffix; 635 636 try { 637 await this.newDlpFile.addDLPLinkFile(this.linkFileName); 638 } catch (err) { 639 HiLog.error(TAG, `addDlpLinkFile failed: ${JSON.stringify(err)}`); 640 try { 641 await this.newDlpFile.closeDLPFile(); 642 } catch (err) { 643 HiLog.error(TAG, `closeDlpFile failed: ${JSON.stringify(err)}`); 644 } 645 try { 646 await fs.close(this.file); 647 await this.deleteUri(uri); 648 } catch (err) { 649 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 650 } 651 fs.closeSync(this.srcFdPicker); 652 this.isOK = false; 653 await GetAlertMessage.startAlertAbility(this.context, 654 { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 655 reject(); 656 } 657 resolve(); 658 }) 659 } 660 661 deleteUri(uri: string): Promise<void> { 662 return new Promise(async (resolve, reject) => { 663 let uriInfo: fileUri.FileUri = new fileUri.FileUri(''); 664 try { 665 uriInfo = new fileUri.FileUri(uri); 666 } catch (err) { 667 HiLog.error(TAG, `fileUri fail: ${JSON.stringify(err)}`); 668 reject(); 669 } 670 try { 671 await fs.unlink(uriInfo.path); 672 } catch (err) { 673 HiLog.error(TAG, `unlink fail: ${JSON.stringify(err)}`); 674 reject(); 675 } 676 resolve(); 677 }) 678 } 679}; 680