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