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