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