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