• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
17import UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility';
18import dlpPermission from '@ohos.dlpPermission';
19import emitter from '@ohos.events.emitter';
20import Want from '@ohos.app.ability.Want';
21import { BusinessError } from '@ohos.base';
22import osAccount from '@ohos.account.osAccount';
23import { Configuration } from '@ohos.app.ability.Configuration';
24import Constants from '../common/constant';
25import {
26  getAuthPerm,
27  checkDomainAccountInfo,
28  getOsAccountInfo,
29  judgeIsSandBox,
30  getFileFd,
31  getAppId,
32  DLPGeneralInfo,
33  getDLPInfo,
34  sendDlpManagerAccountLogin,
35  isValidPath,
36  getAccountTypeAndRealFileType
37} from '../common/FileUtils/utils';
38import GlobalContext from '../common/GlobalContext';
39import { GetAlertMessage } from '../common/AlertMessage/GetAlertMessage';
40import { HiLog } from '../common/HiLog';
41import FileUtils from '../common/FileUtils/FileUtils';
42import AccountManager from '../manager/AccountManager';
43import IdDlpRpcServiceProxy from './data/IIdlDlpRpcServiceTs/id_dlpRpc_service_proxy';
44import common from '@ohos.app.ability.common';
45import FileUtil from '../common/external/FileUtil';
46
47const TAG = 'MainEx';
48
49let direction: number = -1;
50const DLP_FILE_PROCESS_ABILITY_NAME = 'DlpFileProcessAbility';
51
52export default class MainAbility extends UIExtensionAbility {
53  private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY;
54  private callerToken: number = 0;
55  private dlpRpcProxy?: IdDlpRpcServiceProxy;
56  private connectionNum: number = -1;
57
58  connectDlpFileProcessAbility(want: Want, session: UIExtensionContentSession) {
59    HiLog.info(TAG, `connectDlpFileProcessAbility`);
60    if (this.dlpRpcProxy !== undefined) {
61      return;
62    }
63
64    let newWant = {
65      'bundleName': Constants.DLP_MANAGER_BUNDLE_NAME,
66      'abilityName': DLP_FILE_PROCESS_ABILITY_NAME
67    } as Record<string, string>;
68
69    let options: common.ConnectOptions = {
70      onConnect: (elementName, proxy) => {
71        HiLog.info(TAG, `${DLP_FILE_PROCESS_ABILITY_NAME}: onConnect success`);
72        this.dlpRpcProxy = new IdDlpRpcServiceProxy(proxy);
73        GlobalContext.store('dlpRpcProxy', this.dlpRpcProxy);
74        HiLog.info(TAG, `DLPManager IDL onConnect success: ${JSON.stringify(this.dlpRpcProxy)}`);
75        this.getNewWantPage(want, session);
76      },
77      onDisconnect: () => {
78        HiLog.info(TAG, `${DLP_FILE_PROCESS_ABILITY_NAME}: onDisconnect`);
79      },
80      onFailed: () => {
81        HiLog.info(TAG, `${DLP_FILE_PROCESS_ABILITY_NAME}: onFailed`);
82      }
83    }
84
85    if (this.dlpRpcProxy === undefined) {
86      HiLog.info(TAG, `try connect`);
87      try {
88        this.connectionNum = this.context.connectServiceExtensionAbility(newWant, options);
89      } catch (err) {
90        HiLog.error(TAG, `connectDlpFileProcessAbility failed: ${JSON.stringify(err)}`);
91      }
92    }
93    HiLog.info(TAG, `connectDlpFileProcessAbility result: ${this.connectionNum}`);
94  }
95
96  async onSessionCreate(want: Want, session: UIExtensionContentSession): Promise<void> {
97    HiLog.info(TAG, `onSessionCreate start`);
98    if (GlobalContext.load('session')) {
99      this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_ENCRYPTING,
100          data: GlobalContext.load('abilityWant').parameters?.displayName
101        } as BusinessError);
102      return;
103    }
104    GlobalContext.store('session', session);
105    let dlpInfoRet = await getDLPInfo();
106    if (dlpInfoRet.errcode === Constants.ERR_CODE_SUCCESS && dlpInfoRet.result) {
107      AppStorage.setOrCreate('hiPNameId', dlpInfoRet.result.name);
108      AppStorage.setOrCreate('hiPVersionId', dlpInfoRet.result.versionCode);
109    }
110    GlobalContext.store('abilityWant', want);
111    GlobalContext.store('uri', want.uri ?? '');
112    direction = this.context.config.direction ?? -1;
113
114    this.connectDlpFileProcessAbility(want, session);
115
116    AccountManager.connectAbility(this.context);
117  }
118
119  onConfigurationUpdate(newConfig: Configuration): void {
120    if (direction !== newConfig.direction) {
121      direction = newConfig.direction ?? -1;
122    }
123    let eventData: emitter.EventData = {
124      data: {
125        'direction': direction,
126      }};
127    let innerEvent: emitter.InnerEvent = {
128      eventId: Constants.ENCRYPTION_EMIT_DIRECTION_STATUS,
129      priority: emitter.EventPriority.HIGH
130    };
131    emitter.emit(innerEvent, eventData);
132  }
133
134  onSessionDestroy(session: UIExtensionContentSession): void {
135    HiLog.info(TAG, `onSessionDestroy`);
136    if (session !== GlobalContext.load('session')) {
137      return;
138    }
139    if (!(GlobalContext.load('requestIsFromSandBox') as boolean)) {
140      this.dlpRpcProxy?.closeDlpFile(GlobalContext.load('uri'), (err: number) => {
141        if (err !== 0) {
142          HiLog.error(TAG, `closeDLPFile failed: ${err}`);
143        }
144      });
145    }
146    GlobalContext.store('session', '');
147  }
148
149  async gotoPage(session: UIExtensionContentSession): Promise<void> {
150    let accountInfo: osAccount.OsAccountInfo = GlobalContext.load('accountInfo');
151    let accountName: string = accountInfo.domainInfo.accountName;
152    this.authPerm = getAuthPerm(accountName, GlobalContext.load('dlpProperty'));
153
154    AppStorage.setOrCreate('authPerm', this.authPerm);
155    AppStorage.setOrCreate<string>('contactAccount', GlobalContext.load('dlpProperty').contactAccount);
156    AppStorage.setOrCreate('validity', GlobalContext.load('dlpProperty').expireTime)
157    if (this.authPerm < dlpPermission.DLPFileAccess.READ_ONLY ||
158      this.authPerm > dlpPermission.DLPFileAccess.FULL_CONTROL) {
159      this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
160      return;
161    }
162    if (this.authPerm === dlpPermission.DLPFileAccess.FULL_CONTROL) {
163      let accountInfo: osAccount.OsAccountInfo = GlobalContext.load('accountInfo');
164      let checkFlag = await AccountManager.checkAccountInfo(accountInfo?.domainInfo?.accountName);
165      if (!checkFlag) {
166        this.gotoAlertPage(session, { code: Constants.ERR_JS_GET_ACCOUNT_ERROR } as BusinessError);
167        return;
168      }
169
170      let storage: LocalStorage = new LocalStorage({
171        'session': session,
172      } as Record<string, UIExtensionContentSession | string>);
173      try {
174        session.loadContent('pages/changeEncryption', storage);
175        session.setWindowBackgroundColor(Constants.TRANSPARENT_BACKGROUND_COLOR);
176      } catch (exception) {
177        HiLog.error(TAG, `Failed to set the background color. Cause: ${JSON.stringify(exception)}`);
178      }
179    } else {
180      let storage: LocalStorage = new LocalStorage({
181        'session': session,
182      } as Record<string, UIExtensionContentSession | string>);
183      try {
184        session.loadContent('pages/permissionStatus', storage);
185        session.setWindowBackgroundColor(Constants.TRANSPARENT_BACKGROUND_COLOR);
186      } catch (exception) {
187        HiLog.error(TAG, `Failed to set the background color. Cause: ${JSON.stringify(exception)}`);
188      }
189    }
190  }
191
192  async checkValidWant(want: Want): Promise<boolean> {
193    let parameters = want.parameters;
194    if (parameters === undefined) {
195      HiLog.error(TAG, `need parameters in want`);
196      return false;
197    }
198    if (parameters.fileName === undefined) {
199      HiLog.error(TAG, `need fileName in want.parameters`);
200      return false;
201    }
202    if ((parameters.fileName as Record<string, string>).name === undefined) {
203      HiLog.error(TAG, `need name in want.parameters.fileName`);
204      return false;
205    }
206    if (want.uri === undefined) {
207      HiLog.error(TAG, `need uri in want`);
208      return false;
209    }
210    this.callerToken = parameters[Constants.PARAMS_CALLER_TOKEN] as number;
211    let callerBundleName: string = parameters[Constants.PARAMS_CALLER_BUNDLE_NAME] as string;
212    if (this.callerToken === undefined || callerBundleName === undefined) {
213      HiLog.error(TAG, `need caller info in want.parameters`);
214      return false;
215    }
216    AppStorage.setOrCreate('hiPkgName', callerBundleName);
217    let uri = String(want.uri);
218    if (!isValidPath(uri)) {
219      HiLog.error(TAG, `invalid uri in want.uri`);
220      return false;
221    }
222    try {
223      await new Promise<void>((resolve, reject) => {
224        this.dlpRpcProxy?.linkSet(uri, (err: number) => {
225          if (err === 0) {
226            HiLog.error(TAG, `invalid uri for opened link uri`);
227            reject();
228          }
229          resolve();
230        })
231      })
232    } catch {
233      return false;
234    }
235
236    if (uri.indexOf(Constants.FUSE_PATH) !== -1) {
237      HiLog.error(TAG, `invalid uri in want.uri`);
238      return false;
239    }
240    return true;
241  }
242
243  async checkValidWantAndAccount(session: UIExtensionContentSession, want: Want): Promise<void> {
244    return new Promise(async (resolve, reject) => {
245      if (!this.checkValidWant(want)) {
246        this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_PARAM_ERROR } as BusinessError);
247        reject();
248        return;
249      }
250      let accountInfo: osAccount.OsAccountInfo;
251      try {
252        accountInfo = await getOsAccountInfo();
253        GlobalContext.store('accountInfo', accountInfo);
254        AppStorage.setOrCreate('accountDomain', accountInfo.domainInfo.domain);
255        resolve();
256      } catch (err) {
257        HiLog.error(TAG, `getOsAccountInfo failed: ${JSON.stringify(err)}`);
258        this.gotoAlertPage(session, { code: Constants.ERR_JS_GET_ACCOUNT_ERROR } as BusinessError);
259        reject();
260        return;
261      }
262    })
263  }
264
265  async getNewWantPage(want: Want, session: UIExtensionContentSession): Promise<void> {
266    HiLog.info(TAG, `getNewWantPage start`);
267    try {
268      await this.checkValidWantAndAccount(session, want)
269    } catch {
270      return;
271    }
272    let codeMessageRet = checkDomainAccountInfo(GlobalContext.load('accountInfo'));
273    sendDlpManagerAccountLogin(0);
274    if (codeMessageRet.errcode !== Constants.ERR_CODE_SUCCESS) {
275      this.gotoAlertPage(session, { code: codeMessageRet.errcode } as BusinessError);
276      return;
277    }
278    let requestIsFromSandBox: boolean = await judgeIsSandBox(want);
279    GlobalContext.store('requestIsFromSandBox', requestIsFromSandBox);
280    HiLog.info(TAG, `request is from sandbox: ${requestIsFromSandBox}`);
281    if (requestIsFromSandBox) {
282      this.requestIsFromSandBox(session, want);
283    } else {
284      this.requestIsNotFromSandBox(session, want);
285    }
286  }
287
288  requestIsFromSandBox(session: UIExtensionContentSession, want: Want): void {
289    const linkFileName: string = (want.parameters?.linkFileName as Record<string, string>)?.name;
290    this.dlpRpcProxy?.sandBoxLinkFile(linkFileName, this.callerToken,
291      (err: number, data: dlpPermission.DLPProperty, uri: string) => {
292      if (err !== 0) {
293        return;
294      }
295      let dlpFileName: string = (want.parameters?.fileName as Record<string, string>)?.name;
296      GlobalContext.store('dlpFileName', dlpFileName);
297      GlobalContext.store('linkFileName', linkFileName);
298      GlobalContext.store('dlpProperty', data);
299      GlobalContext.store('uri', uri ?? '');
300      AppStorage.setOrCreate('permanent', data.expireTime === 0);
301      if (data.expireTime !== 0) {
302        AppStorage.setOrCreate('validity', new Date(data.expireTime as number));
303      }
304      this.gotoPage(session);
305    });
306  }
307
308  async requestIsNotFromSandBox(session: UIExtensionContentSession, want: Want): Promise<void> {
309    let fileName: string = (want.parameters?.fileName as Record<string, string>)?.name;
310    let isDlpSuffix: boolean = false;
311    try {
312      isDlpSuffix = await FileUtils.isDLPFile(GlobalContext.load('uri'));
313    } catch {
314      this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
315      return;
316    }
317    HiLog.info(TAG, `isDlpSuffix: ${isDlpSuffix}`);
318    if (!isDlpSuffix) {
319      HiLog.error(TAG, `${fileName} is not a dlp file`);
320      GlobalContext.store('originFileName', fileName);
321      GlobalContext.store('originFd', getFileFd(GlobalContext.load('uri') as string));
322      let storage: LocalStorage = new LocalStorage({
323        'session': session,
324      } as Record<string, UIExtensionContentSession | string>);
325      try {
326        session.loadContent('pages/encryptionProtection', storage);
327        session.setWindowBackgroundColor(Constants.TRANSPARENT_BACKGROUND_COLOR);
328      } catch (exception) {
329        HiLog.error(TAG, `Failed to set the background color. Cause: ${JSON.stringify(exception)}`);
330      }
331      return;
332    } else {
333      let dlpFd: number = -1;
334      try {
335        let getFileFdRet = getFileFd(String(want.uri));
336        if (getFileFdRet.errcode !== Constants.ERR_CODE_SUCCESS || !getFileFdRet.result) {
337          HiLog.error(TAG, 'getFileFd error')
338          return;
339        }
340        dlpFd = getFileFdRet.result;
341
342        let dlpGeneralInfo: DLPGeneralInfo = await getAccountTypeAndRealFileType(this.context, dlpFd);
343        GlobalContext.store('realFileType', dlpGeneralInfo.realFileType);
344        if (dlpGeneralInfo.accountType === dlpPermission.AccountType.DOMAIN_ACCOUNT) {
345          this.dlpFilesToEncrypt(session, want);
346        } else {
347          await GetAlertMessage.phoneHandle(this.context, {
348            code: Constants.ERR_JS_APP_CANNOT_OPEN } as BusinessError);
349        }
350      } catch {
351        HiLog.error(TAG, 'getFileFd error');
352      } finally {
353        FileUtil.closeSync(dlpFd);
354      }
355    }
356  }
357
358  async dlpFilesToEncrypt(session: UIExtensionContentSession, want: Want): Promise<void> {
359    let uri: string = GlobalContext.load('uri') as string;
360    try {
361      await this.findFileOpenHistoryHome(uri, session);
362    } catch {
363      return;
364    }
365    let dlpFileName: string = (want.parameters?.fileName as Record<string, string>)?.name;
366    GlobalContext.store('dlpFileName', dlpFileName);
367    let callerAppId: string;
368    try {
369      let callerBundleName = Constants.DLP_MANAGER_BUNDLE_NAME;
370      callerAppId = await getAppId(callerBundleName);
371      HiLog.info(TAG, `get AppId: ${callerAppId}`);
372    } catch {
373      HiLog.error(TAG, `get AppId failed`);
374      return;
375    }
376    this.dlpRpcProxy?.openDlpFile(uri, callerAppId,
377      async (err: number, data: dlpPermission.DLPProperty, msg: string) => {
378      if (err !== 0) {
379        let ansErr: BusinessError<void> = {
380          code: err,
381          name: '',
382          message: msg,
383        }
384        let accountFlag: boolean = true;
385        if (err === Constants.ERR_JS_USER_NO_PERMISSION) {
386          let accountName: string = msg.split(', contact:')?.[1];
387          accountFlag = await AccountManager.checkAccountInfo(accountName);
388        }
389        if (!accountFlag) {
390          this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
391          return;
392        }
393        this.gotoAlertPage(session, ansErr as BusinessError);
394        return;
395      } else {
396        this.getOwnerAccountTypeInfo(data, session);
397      }
398    })
399  }
400
401  async getOwnerAccountTypeInfo(data: dlpPermission.DLPProperty, session: UIExtensionContentSession) {
402    GlobalContext.store('dlpProperty', data);
403    AppStorage.setOrCreate('permanent', data.expireTime === 0);
404    if (data.expireTime !== 0) {
405      AppStorage.setOrCreate('validity', new Date(data.expireTime as number));
406    }
407    if (data.ownerAccountType === dlpPermission.AccountType.DOMAIN_ACCOUNT) {
408      this.gotoPage(session);
409    } else {
410      this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
411    }
412  }
413
414  findFileOpenHistoryHome(uri: string, session: UIExtensionContentSession): Promise<void> {
415    return new Promise<void>((resolve, reject) => {
416      this.dlpRpcProxy?.fileOpenHistory(uri, async (err: number) => {
417        if (err === 0) {
418          this.gotoAlertPage(session, { code: Constants.ERR_JS_APP_OPEN_REJECTED } as BusinessError);
419          reject();
420        }
421        resolve();
422      })
423    })
424  }
425
426  gotoAlertPage(session: UIExtensionContentSession, error: BusinessError) {
427    let storage: LocalStorage = new LocalStorage({
428      'session': session,
429      'error': error
430    } as Record<string, UIExtensionContentSession | string | object>);
431    try {
432      session.loadContent('pages/alert', storage);
433      session.setWindowBackgroundColor(Constants.TRANSPARENT_BACKGROUND_COLOR);
434    } catch (exception) {
435      HiLog.error(TAG, `Failed to set the background color. Cause: ${JSON.stringify(exception)}`);
436    }
437  }
438
439  onWindowStageDestroy(): void {
440    HiLog.info(TAG, `onWindowStageDestroy`);
441  }
442
443  onForeground(): void {
444    HiLog.info(TAG, `onForeground`);
445  }
446
447  onBackground() {
448    HiLog.info(TAG, `onBackground`);
449  }
450};
451