1/* 2 * Copyright (c) 2023-2024 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 ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility'; 17import dlpPermission from '@ohos.dlpPermission'; 18import fileio from '@ohos.fileio'; 19import Want from '@ohos.app.ability.Want'; 20import hiTraceMeter from '@ohos.hiTraceMeter'; 21import hiSysEvent from '@ohos.hiSysEvent'; 22import wantConstant from '@ohos.app.ability.wantConstant'; 23import fs from '@ohos.file.fs'; 24import fileUri from '@ohos.file.fileuri'; 25import { BusinessError } from '@ohos.base'; 26import osAccount from '@ohos.account.osAccount'; 27import uriPermissionManager from '@ohos.application.uriPermissionManager'; 28import { 29 getOsAccountInfo, 30 getUserId, 31 checkDomainAccountInfo, 32 getAuthPerm, 33 getFileFd, 34 getFileUriByPath, 35 getAppId, 36 DLPInfo, 37 getDLPInfo, 38 sendDlpFileOpenProperties, 39 isValidPath, 40 defaultDlpFile, 41 getAccountType, 42 getConnectionStatus 43} from '../common/utils'; 44import { 45 DlpFileInfo, 46 deleteSandbox2linkFileData, 47 deleteFileOpenHistoryData, 48 deleteLinkSetData, 49 deleteToken2FileData, 50 getDlpFileInfoFromSandbox2linkFileData, 51} from '../common/DataUtils'; 52import Constants from '../common/constant'; 53import GlobalContext from '../common/GlobalContext'; 54import rpc from '@ohos.rpc'; 55import { GetAlertMessage } from '../common/GetAlertMessage'; 56import { HiLog } from '../common/HiLog'; 57import { ObjectUtil } from '../common/ObjectUtil'; 58import AccountManager from '../manager/AccountManager'; 59import FileUtil from '../common/external/FileUtil'; 60 61const TAG = 'View'; 62const SUFFIX_INDEX = 2; 63const APPID_FOR_HOPE: string = '5765880207854533797'; 64const BUNDLE_NAME_FOR_HOPE: string = 'com.huawei.it.welink'; 65const DLP_FILE_SUFFIX: string = '.dlp'; 66 67interface DlpConnectionPluginIdObj { 68 id: string 69} 70 71let opening: boolean = false; 72export default class ViewAbility extends ServiceExtensionAbility { 73 private dlpFd: number = -1; 74 private linkFileName: string = ''; 75 private linkFilePath: string = ''; 76 private appIndex: number = -1; 77 private tokenId: number = -1; 78 private dlpFile: dlpPermission.DLPFile = defaultDlpFile; 79 private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY; 80 private needCallAccount: boolean = true; 81 private needCheckAccountType: boolean = false; 82 private sandboxBundleName: string = ''; 83 private sandboxAbilityName: string = ''; 84 private sandboxModuleName: string = ''; 85 private fileName: string = ''; 86 private uri: string = ''; 87 private stat?: fs.Stat; 88 private accountInfo?: osAccount.OsAccountInfo; 89 private uriInfo: fileUri.FileUri = new fileUri.FileUri(''); 90 private linkUri: string = ''; 91 private alreadyOpened: boolean = false; 92 private userId: number = -1; 93 private linkFileWriteable: boolean = false; 94 private callerBundleName: string = ''; 95 private accountType: number = dlpPermission.AccountType.DOMAIN_ACCOUNT; 96 private distributedInfoId: string = ''; 97 98 async onCreate(want: Want): Promise<void> { 99 if (!GlobalContext.load('sandbox2linkFile')) { 100 GlobalContext.store('sandbox2linkFile', new Map<string, (number | string | dlpPermission.DLPFile)[][]>()); 101 } 102 if (!GlobalContext.load('fileOpenHistory')) { 103 GlobalContext.store('fileOpenHistory', new Map<string, (number | string)[]>()); 104 } 105 if (!GlobalContext.load('token2File')) { 106 GlobalContext.store('token2File', new Map<number, (number | string | dlpPermission.DLPFile)[]>()); 107 } 108 if (!GlobalContext.load('linkSet')) { 109 GlobalContext.store('linkSet', new Set<string>()); 110 } 111 let dlpInfo: DLPInfo = await getDLPInfo(); 112 AppStorage.setOrCreate('hiPNameId', dlpInfo.name); 113 AppStorage.setOrCreate('hiPVersionId', dlpInfo.versionCode); 114 AppStorage.setOrCreate('viewContext', this.context); 115 116 AccountManager.connectAbility(this.context); 117 } 118 119 async terminateCall(): Promise<void> { 120 return new Promise((resolve, reject) => { 121 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = 122 GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 123 HiLog.debug(TAG, `sandbox2linkFile size: ${sandbox2linkFile.size}`); 124 if (sandbox2linkFile.size === 0) { 125 try { 126 this.context.terminateSelf(); 127 } catch (error) { 128 HiLog.error(TAG, `terminateSelf exception, error is ${JSON.stringify(error)}`); 129 } 130 } 131 reject(); 132 return; 133 }); 134 } 135 136 async startDataAbility(): Promise<void> { 137 let want: Want = { 138 bundleName: Constants.DLP_MANAGER_BUNDLE_NAME, 139 abilityName: 'DataAbility' 140 }; 141 try { 142 await this.context.startAbility(want); 143 } catch { 144 HiLog.error(TAG, `startDataAbility failed`); 145 } 146 } 147 148 startAbility(want: Want, startId: number): void { 149 HiLog.info(TAG, `start sandbox begin`); 150 this.context.startAbility(want, async (err) => { 151 hiTraceMeter.finishTrace('DlpStartSandboxJs', startId); 152 if (err && err.code !== 0) { 153 HiLog.error(TAG, `startSandboxApp failed: ${JSON.stringify(err)}`); 154 try { 155 await this.dlpFile.deleteDLPLinkFile(this.linkFileName); 156 } catch (err) { 157 HiLog.error(TAG, `dlpFile deleteDLPLinkFile failed: ${JSON.stringify(err)}`); 158 } 159 try { 160 await this.dlpFile.closeDLPFile(); 161 } catch (err) { 162 HiLog.error(TAG, `dlpFile closeDLPFile failed: ${JSON.stringify(err)}`); 163 } 164 opening = false; 165 //when errCode is ERR_JS_APP_NOT_EXIST, delete info. 166 if (err.code === Constants.ERR_JS_APP_NOT_EXIST) { 167 await this.deleteData(true, false); 168 } 169 await GetAlertMessage.requestModalUIExtension(this.context, 170 {code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 171 await this.sendDlpFileOpenFault(Constants.DLP_START_SANDBOX_ERROR, this.sandboxBundleName, this.appIndex, 172 undefined); // 105: DLP_START_SANDBOX_ERROR 173 } else { 174 // 203: DLP_START_SANDBOX_SUCCESS 175 await this.sendDlpFileOpenEvent(Constants.DLP_START_SANDBOX_SUCCESS, this.sandboxBundleName, this.appIndex); 176 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = 177 GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 178 if (!sandbox2linkFile.has(this.sandboxBundleName + this.appIndex)) { 179 sandbox2linkFile.set(this.sandboxBundleName + this.appIndex, []); 180 } 181 if (!this.alreadyOpened) { 182 sandbox2linkFile.get(this.sandboxBundleName + this.appIndex)?.push( 183 [this.dlpFile, this.linkFileName, this.dlpFd, this.tokenId, this.uri]); 184 (GlobalContext.load('fileOpenHistory') as Map<string, (number | string)[]>).set( 185 this.uri, [this.sandboxBundleName, this.appIndex, this.linkFileName, this.linkUri, this.distributedInfoId] 186 ); 187 (GlobalContext.load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | 188 dlpPermission.DLPFileAccess | null)[]>).set(this.tokenId, 189 [this.dlpFile, this.sandboxBundleName, this.appIndex, this.authPerm, this.uri, null, -1]); 190 (GlobalContext.load('linkSet') as Set<string>).add(this.linkUri); 191 } 192 await this.startDataAbility(); 193 opening = false; 194 HiLog.debug(TAG, `startDataAbility success`); 195 } 196 }); 197 } 198 199 async deleteData(isNeedCloseFd: boolean, isNeedDeleteLink: boolean): Promise<void> { 200 HiLog.info(TAG, `deleteData start`); 201 if (isNeedCloseFd) { 202 FileUtil.closeFdSync(this.dlpFd); 203 } 204 try { 205 await deleteSandbox2linkFileData(this.sandboxBundleName + this.appIndex, isNeedDeleteLink); 206 await deleteToken2FileData(this.tokenId); 207 await deleteLinkSetData(this.linkUri); 208 await deleteFileOpenHistoryData(this.uri); 209 } catch (err) { 210 HiLog.error(TAG, `deleteData failed`); 211 } 212 } 213 214 startSandboxApp(startId: number, want: Want): void { 215 startId = Number(startId); 216 hiTraceMeter.startTrace('DlpStartSandboxJs', startId); 217 want.bundleName = this.sandboxBundleName; 218 want.abilityName = this.sandboxAbilityName; 219 want.moduleName = this.sandboxModuleName; 220 want.uri = this.linkUri; 221 want.flags = this.linkFileWriteable ? 222 wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION : wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION; 223 let dlpWant: Want = { 224 parameters: { 225 'linkFileName': { 226 'name': this.linkFileName 227 }, 228 'fileAsset': { 229 'displayName': this.uriInfo.name, 230 'relativePath': this.uriInfo.path, 231 'dateModified': this.stat?.ctime 232 }, 233 'uri': this.linkUri, 234 'dlpUri': { 235 'name': this.uri 236 }, 237 'linkFileWriteable': { 238 'name': this.linkFileWriteable 239 }, 240 'fileName': { 241 'name': decodeURIComponent(this.fileName) 242 }, 243 'ohos.dlp.params.index': this.appIndex, 244 'ohos.dlp.params.moduleName': this.sandboxModuleName, 245 'ohos.dlp.params.securityFlag': this.authPerm === 246 dlpPermission.DLPFileAccess.READ_ONLY ? true : false 247 } 248 }; 249 ObjectUtil.Assign(want.parameters, dlpWant.parameters); 250 this.startAbility(want, startId); 251 } 252 253 async sendDlpFileOpenFault(code: number, sandboxName: string, appIndex: number, reason?: string): Promise<void> { 254 let event: hiSysEvent.SysEventInfo = { 255 domain: 'DLP', 256 name: 'DLP_FILE_OPEN', 257 eventType: hiSysEvent?.EventType?.FAULT, 258 params: { 259 'CODE': code, 260 'USER_ID': this.userId, 261 'SANDBOX_PKGNAME': sandboxName, 262 } as Record<string, number | string> 263 }; 264 if (appIndex !== -1 && event.params) { 265 event.params['SANDBOX_INDEX'] = appIndex; 266 } 267 if (reason !== undefined && event.params) { 268 event.params['REASON'] = reason; 269 } 270 try { 271 await hiSysEvent.write(event); 272 } catch (err) { 273 HiLog.error(TAG, `sendDlpFileOpenFault failed`); 274 } 275 } 276 277 async sendDlpFileOpenEvent(code: number, sandboxName: string, appIndex: number): Promise<void> { 278 let event: hiSysEvent.SysEventInfo = { 279 domain: 'DLP', 280 name: 'DLP_FILE_OPEN_EVENT', 281 eventType: hiSysEvent?.EventType?.BEHAVIOR, 282 params: { 283 'CODE': code, 284 'USER_ID': this.userId, 285 'SANDBOX_PKGNAME': sandboxName, 286 } as Record<string, number | string> 287 }; 288 if (appIndex !== -1 && event.params) { 289 event.params['SANDBOX_INDEX'] = appIndex; 290 } 291 try { 292 await hiSysEvent.write(event); 293 } catch (err) { 294 HiLog.error(TAG, `sendDlpFileOpenEvent failed`); 295 } 296 } 297 298 299 async closeFile(): Promise<void> { 300 try { 301 await this.dlpFile.closeDLPFile(); 302 FileUtil.closeFdSync(this.dlpFd); 303 } catch (err) { 304 HiLog.error(TAG, `closeFile failed: ${JSON.stringify(err)}`); 305 } 306 } 307 308 async startLoginAbility(): Promise<void> { 309 let accountWant: Want = { 310 bundleName: Constants.DLP_CREDMGR_BUNDLE_NAME, 311 abilityName: Constants.DLP_CREDMGR_LOGIN_ABILITY_NAME, 312 }; 313 if (await getConnectionStatus() === false) { 314 GetAlertMessage.requestModalUIExtension(this.context, { 315 code: Constants.ERR_JS_APP_NETWORK_INVALID } as BusinessError); 316 return; 317 } 318 try { 319 await this.context.startAbility(accountWant); 320 } catch (err) { 321 HiLog.error(TAG, `Failed to invoke startAbility, ${JSON.stringify(err)}`) 322 return; 323 } 324 await this.terminateCall(); 325 } 326 327 async getAccountAndOpenDLPFile(startId: number): Promise<void> { 328 hiTraceMeter.startTrace('DlpGetOsAccountJs', startId); 329 return new Promise(async (resolve, reject) => { 330 try { 331 this.accountInfo = await getOsAccountInfo(); 332 this.distributedInfoId = this.accountInfo.distributedInfo.id; 333 AppStorage.setOrCreate('accountDomain', this.accountInfo.domainInfo.domain); 334 this.userId = await getUserId(); 335 } catch (err) { 336 HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`); 337 hiTraceMeter.finishTrace('DlpGetOsAccountJs', startId); 338 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 339 opening = false; 340 await GetAlertMessage.requestModalUIExtension(this.context, { 341 code: Constants.ERR_JS_GET_ACCOUNT_ERROR } as BusinessError); 342 FileUtil.closeFdSync(this.dlpFd); 343 reject(err); 344 return; 345 } 346 hiTraceMeter.finishTrace('DlpGetOsAccountJs', startId); 347 if (this.accountType === dlpPermission.AccountType.CLOUD_ACCOUNT) { 348 if (this.accountInfo.distributedInfo.name === 'ohosAnonymousName' && 349 this.accountInfo.distributedInfo.id === 'ohosAnonymousUid') { 350 HiLog.error(TAG, 'account not login'); 351 opening = false; 352 await this.startLoginAbility(); 353 reject(); 354 return; 355 } 356 resolve(); 357 return; 358 } 359 let codeMessage = checkDomainAccountInfo(this.accountInfo); 360 if (codeMessage) { 361 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 362 opening = false; 363 await GetAlertMessage.requestModalUIExtension(this.context, { 364 code: codeMessage } as BusinessError); 365 FileUtil.closeFdSync(this.dlpFd); 366 reject(); 367 return; 368 } 369 resolve(); 370 }); 371 } 372 373 async callAlertAbility(errCode: BusinessError): Promise<void> { 374 await GetAlertMessage.requestModalUIExtension(this.context, errCode); 375 } 376 377 async getOpenedDLPFile(startId: number): Promise<void> { 378 return new Promise(async (resolve, reject) => { 379 hiTraceMeter.startTrace('DlpOpenDlpFileJs', startId); 380 try { 381 HiLog.info(TAG, `openDLPFile: ${this.fileName}, dlpFd: ${this.dlpFd}`); 382 let callerAppId: string; 383 try { 384 callerAppId = await getAppId(this.callerBundleName); 385 HiLog.info(TAG, `get AppId success`); 386 } catch { 387 HiLog.error(TAG, `get AppId failed`); 388 await this.callAlertAbility({code: Constants.ERR_JS_NOT_AUTHORIZED_APPLICATION } as BusinessError); 389 return; 390 } 391 this.dlpFile = await dlpPermission.openDLPFile(this.dlpFd, callerAppId); 392 this.accountType = this.dlpFile.dlpProperty.ownerAccountType; 393 } catch (err) { 394 HiLog.error(TAG, `openDLPFile: ${decodeURIComponent(this.fileName)}, failed: ${JSON.stringify(err)}`); 395 hiTraceMeter.finishTrace('DlpOpenDlpFileJs', startId); 396 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 397 opening = false; 398 await this.sendDlpFileOpenFault( 399 Constants.DLP_FILE_PARSE_ERROR, this.sandboxBundleName, -1, (err as BusinessError<string>).data 400 ); // 103:DLP_FILE_PARSE_ERROR 401 402 if (err.code === Constants.ERR_JS_NOT_AUTHORIZED_APPLICATION ) { 403 await this.callAlertAbility({code: Constants.ERR_JS_NOT_AUTHORIZED_APPLICATION} as BusinessError); 404 return; 405 } 406 let accountFlag: boolean = true; 407 if (this.accountType === dlpPermission.AccountType.DOMAIN_ACCOUNT && 408 err.code === Constants.ERR_JS_USER_NO_PERMISSION) { 409 let accountName: string = err.message.split(', contact:')?.[1]; 410 accountFlag = await AccountManager.checkAccountInfo(accountName); 411 } 412 if (!accountFlag) { 413 await this.callAlertAbility({code: Constants.ERR_JS_USER_NO_PERMISSION } as BusinessError); 414 return; 415 } 416 await this.callAlertAbility(err.code === Constants.ERR_JS_APP_PERMISSION_DENY ? 417 {code: Constants.ERR_JS_RELEASE_FILE_OPEN } as BusinessError : err); 418 FileUtil.closeFdSync(this.dlpFd); 419 reject(); 420 return; 421 } 422 hiTraceMeter.finishTrace('DlpOpenDlpFileJs', startId); 423 try { 424 await this.dlpGetAuthPerm(); 425 } catch (err) { 426 reject(); 427 return; 428 } 429 resolve(); 430 }) 431 } 432 433 async dlpGetAuthPerm(): Promise<void> { 434 return new Promise(async (resolve, reject) => { 435 if (this.needCallAccount && this.accountType === dlpPermission.AccountType.DOMAIN_ACCOUNT) { 436 this.authPerm = getAuthPerm(this.accountInfo?.domainInfo.accountName ?? '', this.dlpFile.dlpProperty); 437 } else { 438 this.authPerm = dlpPermission.DLPFileAccess.READ_ONLY; 439 } 440 if (this.authPerm < dlpPermission.DLPFileAccess.READ_ONLY || 441 this.authPerm > dlpPermission.DLPFileAccess.FULL_CONTROL) { 442 opening = false; 443 await GetAlertMessage.requestModalUIExtension(this.context, { 444 code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 445 await this.closeFile(); 446 reject(); 447 return; 448 } 449 resolve(); 450 }); 451 } 452 453 async getPolicyAndInstallSandbox(startId: number): Promise<void> { 454 return new Promise(async (resolve, reject) => { 455 this.alreadyOpened = false; 456 try { 457 await this.sandboxSetData(startId); 458 } catch { 459 reject(); 460 return; 461 } 462 AppStorage.setOrCreate('hiSandboxIndex', this.appIndex); 463 hiTraceMeter.finishTrace('DlpInstallSandboxJs', startId); 464 // 202: DLP_INSTALL_SANDBOX_SUCCESS 465 await this.sendDlpFileOpenEvent(Constants.DLP_INSTALL_SANDBOX_SUCCESS, this.sandboxBundleName, this.appIndex); 466 if (!this.alreadyOpened) { 467 try { 468 await this.getAlreadyOpen(startId); 469 } catch { 470 reject(); 471 return; 472 } 473 } 474 resolve(); 475 }); 476 } 477 478 async sandboxSetData(startId: number): Promise<void> { 479 return new Promise(async (resolve, reject) => { 480 try { 481 let fileOpenHistory: Map<string, (number | string)[]> = 482 GlobalContext.load('fileOpenHistory') as Map<string, (number | string)[]>; 483 if (fileOpenHistory.has(this.uri) && this.historyOpenSame(fileOpenHistory)) { 484 HiLog.info(TAG, `file: ${this.fileName} already open`); 485 let value: (number | string)[] = fileOpenHistory.get(this.uri) as (number | string)[]; 486 this.appIndex = value[Constants.FILE_OPEN_HISTORY_ONE] as number; 487 this.linkFileName = value[Constants.FILE_OPEN_HISTORY_TWO] as string; 488 this.linkUri = value[Constants.FILE_OPEN_HISTORY_THREE] as string; 489 FileUtil.closeFdSync(this.dlpFd); 490 let dlpFileInfo = 491 await getDlpFileInfoFromSandbox2linkFileData(this.sandboxBundleName + this.appIndex, this.uri); 492 this.dlpFile = dlpFileInfo.dlpFile; 493 this.tokenId = dlpFileInfo.tokenId; 494 this.accountType = this.dlpFile.dlpProperty.ownerAccountType; 495 await this.dlpGetAuthPerm(); 496 this.alreadyOpened = true; 497 } else { 498 await this.getOpenedDLPFile(startId); 499 } 500 hiTraceMeter.startTrace('DlpInstallSandboxJs', startId); 501 let appInfo: dlpPermission.DLPSandboxInfo = await dlpPermission.installDLPSandbox( 502 this.sandboxBundleName, this.authPerm, this.userId, this.uri 503 ); 504 if (this.alreadyOpened && (this.appIndex != appInfo.appIndex || this.tokenId != appInfo.tokenID)) { 505 await this.deleteData(false, true); 506 this.dlpFd = getFileFd(this.uri, fs.OpenMode.READ_WRITE); 507 await this.getOpenedDLPFile(startId); 508 this.alreadyOpened = false; 509 } 510 this.appIndex = appInfo.appIndex; 511 this.tokenId = appInfo.tokenID; 512 resolve(); 513 } catch (err) { 514 HiLog.error(TAG, `installDLPSandbox failed: ${JSON.stringify(err)}`); 515 hiTraceMeter.finishTrace('DlpInstallSandboxJs', startId); 516 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 517 opening = false; 518 await this.sendDlpFileOpenFault( 519 Constants.DLP_INSTALL_SANDBOX_ERROR, this.sandboxBundleName, -1, (err as BusinessError<string>).data 520 ); // 104:DLP_INSTALL_SANDBOX_ERROR 521 await GetAlertMessage.requestModalUIExtension(this.context, { 522 code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 523 await this.closeFile(); 524 reject(); 525 } 526 }) 527 } 528 529 historyOpenSame(fileOpenHistory: Map<string, (number | string)[]>): boolean { 530 let historyDistributedInfoId = fileOpenHistory.get(this.uri); 531 let distributeId = historyDistributedInfoId ? historyDistributedInfoId[4] : ''; 532 if (distributeId === this.distributedInfoId) { 533 return true; 534 }; 535 return false; 536 } 537 538 async generateLinkFileName(startId: number): Promise<string> { 539 return new Promise<string>(async (resolve, reject) => { 540 let timestamp = new Date().getTime(); 541 let splitNames = this.fileName.split('.'); 542 HiLog.debug(TAG, `splitNames: ${splitNames}`); 543 if (splitNames.length <= SUFFIX_INDEX) { 544 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 545 opening = false; 546 await GetAlertMessage.requestModalUIExtension(this.context, { 547 code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 548 await this.closeFile(); 549 reject(''); 550 return; 551 } 552 let secondarySuffix = splitNames[splitNames.length - SUFFIX_INDEX]; 553 this.linkFileName = String(this.sandboxBundleName).substring(0, Constants.BUNDLE_LEN) + '_' + this.appIndex + 554 '_' + timestamp + String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END) + '.dlp.link.' + 555 secondarySuffix; 556 resolve(secondarySuffix); 557 }); 558 } 559 560 async getAlreadyOpen(startId: number): Promise<void> { 561 return new Promise(async (resolve, reject) => { 562 try { 563 await this.generateLinkFileName(startId); 564 } catch { 565 reject(); 566 return; 567 } 568 hiTraceMeter.startTrace('DlpAddLinkFileJs', startId); 569 try { 570 await this.getAddDLPLinkFile(startId); 571 } catch { 572 reject(); 573 return; 574 } 575 hiTraceMeter.finishTrace('DlpAddLinkFileJs', startId); 576 try { 577 this.linkFilePath = Constants.FUSE_PATH + this.linkFileName; 578 let stat = fs.statSync(this.linkFilePath); 579 const WRITE_ACCESS: number = 0o0200; 580 if (stat.mode & WRITE_ACCESS) { 581 this.linkFileWriteable = true; 582 } else { 583 this.linkFileWriteable = false; 584 } 585 } catch (err) { 586 HiLog.error(TAG, `file error: ${JSON.stringify(err)}`); 587 opening = false; 588 try { 589 await this.terminateCall(); 590 } catch (err) { 591 reject(); 592 return; 593 } 594 } 595 this.linkUri = getFileUriByPath(this.linkFilePath); 596 if (this.linkUri === '') { 597 HiLog.error(TAG, `get linkUri ByPath fail`); 598 opening = false; 599 try { 600 await this.terminateCall(); 601 } catch (err) { 602 reject(); 603 return; 604 } 605 } 606 resolve(); 607 }); 608 } 609 610 async getAddDLPLinkFile(startId: number): Promise<void> { 611 return new Promise(async (resolve, reject) => { 612 try { 613 await this.dlpFile.addDLPLinkFile(this.linkFileName); 614 } catch (error) { 615 HiLog.error(TAG, `addDLPLinkFile failed: ${JSON.stringify(error)}`); 616 try { 617 await this.dlpFile.closeDLPFile(); 618 } catch (err) { 619 HiLog.error(TAG, `closeDLPFile failed: ${JSON.stringify(err)}`); 620 } 621 opening = false; 622 await GetAlertMessage.requestModalUIExtension(this.context, error); 623 hiTraceMeter.finishTrace('DlpAddLinkFileJs', startId); 624 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 625 await this.closeFile(); 626 reject(); 627 return; 628 } 629 resolve(); 630 }); 631 } 632 633 async getUriInfo(startId: number): Promise<void> { 634 return new Promise(async (resolve, reject) => { 635 try { 636 this.uriInfo = new fileUri.FileUri(this.uri); 637 } catch (error) { 638 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 639 opening = false; 640 await GetAlertMessage.requestModalUIExtension(this.context, { 641 code: Constants.ERR_JS_APP_GET_FILE_ASSET_ERROR } as BusinessError); 642 await this.closeFile(); 643 HiLog.error(TAG, `open: ${this.uri}, failed: ${JSON.stringify(error)}`); 644 reject(); 645 return; 646 } 647 try { 648 this.stat = await fs.stat(this.uriInfo.path); 649 AppStorage.setOrCreate('hiFileSize', this.stat.size); 650 AppStorage.setOrCreate('hiPolicySizeEnc', this.stat.size); 651 } catch (err) { 652 HiLog.error(TAG, `stat fail: ${JSON.stringify(err)}`); 653 } 654 resolve(); 655 }); 656 } 657 658 onConnect(want: Want): rpc.RemoteObject { 659 return new rpc.RemoteObject(TAG); 660 } 661 662 onDisconnect(want: Want): void { 663 HiLog.info(TAG, `onDisconnect`); 664 } 665 666 dlpFileMapHistory(want: Want): Promise<void> { 667 return new Promise(async (resolve, reject) => { 668 this.callerBundleName = want.parameters?.['ohos.dlp.params.bundleName'] as string; 669 AppStorage.setOrCreate('hiSandboxPkgName', this.callerBundleName); 670 if ((AppStorage.get('dlpFileMap') as Map<string, (string | number)[]>)?.has(want.uri ?? '')) { 671 await GetAlertMessage.requestModalUIExtension(this.context, { 672 code: Constants.ERR_JS_APP_ENCRYPTION_REJECTED } as BusinessError); 673 reject(); 674 return; 675 } 676 this.uri = want.uri as string; 677 if (opening) { 678 HiLog.info(TAG, `file is opening: ${this.uri}`); 679 reject(); 680 return; 681 } else { 682 opening = true; 683 HiLog.info(TAG, `file is opened: ${this.uri}`); 684 } 685 if (!isValidPath(this.uri)) { 686 opening = false; 687 HiLog.error(TAG, `invalid uri in want.uri`); 688 try { 689 await this.terminateCall(); 690 } catch (err) { 691 reject(); 692 return; 693 } 694 } 695 let strArray: string[] = this.uri.split('/'); 696 let len: number = strArray.length; 697 this.fileName = strArray[len - 1]; 698 this.dlpFd = getFileFd(this.uri, fs.OpenMode.READ_WRITE); 699 HiLog.debug(TAG, `dlpFd: ${this.dlpFd}`); 700 if (this.dlpFd === -1) { 701 opening = false; 702 try { 703 await this.terminateCall(); 704 } catch (err) { 705 reject(); 706 return; 707 } 708 } 709 resolve(); 710 }) 711 } 712 713 async checkNeedCallAccount(): Promise<boolean> { 714 switch (this.accountType) { 715 case dlpPermission.AccountType.CLOUD_ACCOUNT: { 716 return true; 717 } 718 case dlpPermission.AccountType.DOMAIN_ACCOUNT: { 719 try { 720 this.accountInfo = await getOsAccountInfo(); 721 this.userId = await getUserId(); 722 } catch (err) { 723 HiLog.error(TAG, 'getOsAccountInfo failed: ${JSON.stringify(err)}'); 724 return true; 725 } 726 if (this.accountInfo.domainInfo.accountName === '') { 727 HiLog.debug(TAG, 'not need check call account'); 728 return false; 729 } 730 } 731 defalut: { 732 break; 733 } 734 } 735 return true; 736 } 737 738 grandUriPermission() { 739 let flag = wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | 740 wantConstant.Flags.FLAG_AUTH_PERSISTABLE_URI_PERMISSION; 741 let targetBundleName = this.sandboxBundleName; 742 uriPermissionManager.grantUriPermission(this.uri, flag, targetBundleName, (result) => { 743 HiLog.info(TAG, `grandUriPermission result: ${JSON.stringify(result)}`); 744 }); 745 } 746 747 async checkPermissionWithPluginId(want: Want): Promise<boolean> { 748 let paramCallerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string; 749 AppStorage.setOrCreate('paramCallerBundleName', paramCallerBundleName); 750 751 let pluginId: string = (want.parameters?.['dlpConnectionPluginId'] as DlpConnectionPluginIdObj)?.id; 752 if (pluginId === null || pluginId === undefined) { 753 return true; 754 } 755 756 let callerAppId = want.parameters?.['ohos.aafwk.param.callerAppIdentifier'] as string; 757 if (callerAppId !== APPID_FOR_HOPE || paramCallerBundleName !== BUNDLE_NAME_FOR_HOPE) { 758 HiLog.error(TAG, `callerAppId or callerBundleNameis error.`); 759 await this.callAlertAbility({code: Constants.ERR_JS_NOT_AUTHORIZED_APPLICATION} as BusinessError); 760 return false; 761 } 762 763 this.needCheckAccountType = true; 764 return true; 765 } 766 767 CheckSuffixByUri(): Boolean { 768 if (this.uri.length < DLP_FILE_SUFFIX.length) { 769 return false; 770 } 771 772 let fileSuffix: string = this.uri.substring(this.uri.length - DLP_FILE_SUFFIX.length); 773 let lowerFileSuffix: string = fileSuffix.toLowerCase(); 774 if (lowerFileSuffix === DLP_FILE_SUFFIX) { 775 return true; 776 } 777 778 return false; 779 } 780 781 async onRequest(want: Want, startId: number): Promise<void> { 782 HiLog.debug(TAG, `enter onRequest`); 783 if (!this.checkPermissionWithPluginId(want)) { 784 return; 785 } 786 try { 787 await this.dlpFileMapHistory(want); 788 this.accountType = await getAccountType(this.context, this.dlpFd); 789 } catch (err) { 790 if (err.code === Constants.SHARE_NO_SPACE_LEFT_ON_DEVICE) { 791 await GetAlertMessage.requestModalUIExtension(this.context, { 792 code: Constants.ERR_JS_NO_SPACE } as BusinessError); 793 } 794 return; 795 } 796 startId = Number(startId); 797 hiTraceMeter.startTrace('DlpOpenFileJs', startId); 798 this.sandboxBundleName = want.parameters?.['ohos.dlp.params.bundleName'] as string; 799 this.sandboxAbilityName = want.parameters?.['ohos.dlp.params.abilityName'] as string; 800 this.sandboxModuleName = want.parameters?.['ohos.dlp.params.moduleName'] as string; 801 if (this.needCheckAccountType) { 802 this.needCallAccount = await this.checkNeedCallAccount(); 803 } 804 if (this.fileName === undefined || this.dlpFd === undefined || this.uri === undefined || 805 this.sandboxBundleName === undefined || this.sandboxAbilityName === undefined || 806 this.sandboxModuleName === undefined || !this.CheckSuffixByUri()) { 807 opening = false; 808 HiLog.error(TAG, `get parameters failed`); 809 try { 810 await this.terminateCall(); 811 } catch (err) { 812 return; 813 } 814 } 815 let fileOpenHistory: Map<string, (number | string)[]> = 816 GlobalContext.load('fileOpenHistory') as Map<string, (number | string)[]>; 817 if (fileOpenHistory.has(this.uri)) { 818 let value: (number | string)[] = fileOpenHistory.get(this.uri) as (number | string)[]; 819 if (this.sandboxBundleName !== value[Constants.FILE_OPEN_HISTORY_ZERO] as string) { 820 HiLog.error(TAG, `other app is opening this file`); 821 opening = false; 822 await GetAlertMessage.requestModalUIExtension(this.context, { 823 code: Constants.ERR_JS_OTHER_APP_OPEN_FILE } as BusinessError); 824 return; 825 } 826 } 827 try { 828 if (this.needCallAccount) { 829 await this.getAccountAndOpenDLPFile(startId); 830 } 831 await this.getPolicyAndInstallSandbox(startId); 832 await this.getUriInfo(startId); 833 } catch { 834 return; 835 } 836 this.grandUriPermission(); 837 this.startSandboxApp(startId, want); 838 AppStorage.setOrCreate('hiCode', Constants.DLP_START_SANDBOX_SUCCESS); 839 sendDlpFileOpenProperties(); 840 hiTraceMeter.finishTrace('DlpOpenFileJs', startId); 841 } 842} 843