1/* 2 * Copyright (c) 2023 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 */ 15import UIAbility from '@ohos.app.ability.UIAbility'; 16import datafile from '@ohos.file.fileAccess'; 17import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; 18import { Permissions } from '@ohos.abilityAccessCtrl'; 19import dlpPermission from '@ohos.dlpPermission'; 20import emitter from '@ohos.events.emitter'; 21import deviceInfo from '@ohos.deviceInfo'; 22import Constants from '../common/constant'; 23import { 24 getAuthPerm, 25 checkAccountLogin, 26 getOsAccountInfo, 27 judgeIsSandBox, 28 getFileFd, 29 isValidPath 30} from '../common/utils'; 31import GlobalContext from '../common/GlobalContext' 32import { Configuration } from '@ohos.app.ability.Configuration'; 33import Want from '@ohos.app.ability.Want'; 34import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 35import { BusinessError } from '@ohos.base'; 36import window from '@ohos.window'; 37import osAccount from '@ohos.account.osAccount'; 38import common from '@ohos.app.ability.common'; 39 40const PHONE = 'phone'; 41const TAG = '[DLPManager_Main]'; 42let permissionList: Array<Permissions> = [ 43 'ohos.permission.READ_MEDIA', 44 'ohos.permission.WRITE_MEDIA', 45 'ohos.permission.FILE_ACCESS_MANAGER' 46]; 47 48let direction: number = -1; 49let languageValue: string = 'zh'; 50 51export default class MainAbility extends UIAbility { 52 authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY; 53 callerToken:number = 0; 54 55 async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> { 56 console.info(TAG, 'onCreate'); 57 GlobalContext.store('abilityWant', want); 58 GlobalContext.store('context', this.context); 59 GlobalContext.store('domainAccount', deviceInfo.deviceType === PHONE ? false : true); 60 GlobalContext.store('uri', want.uri ?? ''); 61 62 GlobalContext.store('dsHelper', datafile.createFileAccessHelper(this.context)); 63 direction = this.context.config.direction ?? -1; 64 languageValue = this.context.config.language ?? 'zh'; 65 if (!GlobalContext.load('fileOpenHistoryFromMain')) { 66 GlobalContext.store('fileOpenHistoryFromMain', new Map<string, (string | number)[]>()); 67 } 68 if (!GlobalContext.load('linkSet')) { 69 GlobalContext.store('linkSet', new Set<string>()); 70 } 71 } 72 onConfigurationUpdate(newConfig: Configuration): void { 73 if (direction !== newConfig.direction) { 74 direction = newConfig.direction ?? -1; 75 } 76 if (languageValue !== newConfig.language) { 77 languageValue = newConfig.language ?? 'zh'; 78 } 79 let eventData: emitter.EventData = { 80 data: { 81 'direction': direction, 82 }}; 83 let innerEvent: emitter.InnerEvent = { 84 eventId: Constants.ENCRYPTION_EMIT_DIRECTION_STATUS, 85 priority: emitter.EventPriority.HIGH 86 }; 87 emitter.emit(innerEvent, eventData); 88 89 let languageData: emitter.EventData = { 90 data: { 91 'languageValue': languageValue, 92 }}; 93 let languageEvent: emitter.InnerEvent = { 94 eventId: Constants.ENCRYPTION_EMIT_LANGUAGE_VALUE, 95 priority: emitter.EventPriority.HIGH 96 }; 97 emitter.emit(languageEvent, languageData); 98 } 99 100 onDestroy(): void { 101 console.info(TAG, 'onDestroy'); 102 if (GlobalContext.load('fileOpenHistoryFromMain')) { 103 (GlobalContext.load('fileOpenHistoryFromMain') as Map<string, Object>).delete(GlobalContext.load('uri') as string) 104 } 105 } 106 107 onNewWant(want: Want) { 108 console.log(TAG, 'onNewWant', JSON.stringify(want)); 109 GlobalContext.store('abilityWant', want); 110 GlobalContext.store('uri', (GlobalContext.load('abilityWant') as Want).uri as string); 111 112 this.getNewWantPage(); 113 } 114 115 async showErrorDialogAndExit(error: BusinessError): Promise<void> { 116 let abilityWant = GlobalContext.load('abilityWant') as Want; 117 if (abilityWant.parameters) { 118 abilityWant.parameters.error = error; 119 } 120 let context: common.UIAbilityContext = GlobalContext.load('context') as common.UIAbilityContext; 121 GlobalContext.store('alertContext', context); 122 (GlobalContext.load('windowStage') as window.WindowStage).loadContent('pages/alert', (err: BusinessError) => { 123 if (err.code !== 0) { 124 console.error(TAG, 'loadContent failed', err.code, err.message); 125 } 126 }); 127 } 128 129 async gotoPage(windowStage: window.WindowStage, accountInfo: osAccount.OsAccountInfo): Promise<void> { 130 let accountName: string = (GlobalContext.load('domainAccount') as boolean) ? accountInfo.domainInfo.accountName : accountInfo.distributedInfo.name; 131 this.authPerm = getAuthPerm(accountName, (GlobalContext.load('dlpFile') as dlpPermission.DLPFile).dlpProperty); 132 console.info(TAG, accountName, 'has dlp access', JSON.stringify(this.authPerm)); 133 134 AppStorage.SetOrCreate('authPerm', this.authPerm); 135 AppStorage.SetOrCreate<string>('contactAccount', (GlobalContext.load('dlpFile') as dlpPermission.DLPFile).dlpProperty.contactAccount); 136 if (this.authPerm < dlpPermission.DLPFileAccess.READ_ONLY || 137 this.authPerm > dlpPermission.DLPFileAccess.FULL_CONTROL) { 138 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 139 return; 140 } 141 if (this.authPerm === dlpPermission.DLPFileAccess.FULL_CONTROL) { 142 windowStage.loadContent('pages/changeEncryption', (err: BusinessError) =>{ 143 if (err.code !== 0) { 144 console.error(TAG, 'loadContent failed', err.code, err.message); 145 } 146 }); 147 } else { 148 windowStage.loadContent('pages/permissionStatus', (err: BusinessError) =>{ 149 if (err.code !== 0) { 150 console.error(TAG, 'loadContent failed', err.code, err.message); 151 } 152 }); 153 } 154 windowStage.getMainWindow().then((win: window.Window) => { 155 win.setBackgroundColor('#00FFFFFF'); 156 }); 157 } 158 159 async findDlpFile(): Promise<void> { 160 return new Promise(async (resolve, reject) => { 161 let abilityWant: Want = GlobalContext.load('abilityWant') as Want; 162 const linkFileName: string = (abilityWant.parameters?.linkFileName as Record<string, string>)?.name; 163 let sandbox2linkFile:Map<string, (number | string | dlpPermission.DLPFile)[][]> = GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 164 for (let value of Array.from<(number | string | dlpPermission.DLPFile)[][]>(sandbox2linkFile.values())) { 165 for (let linkFile of value) { 166 if (linkFile[Constants.FILE_OPEN_HISTORY_ONE] === linkFileName) { 167 if (this.callerToken !== linkFile[Constants.FILE_OPEN_HISTORY_THREE]) { 168 console.error(TAG, 'found file, buf token invalid', linkFileName); 169 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 170 reject(); 171 return; 172 } 173 GlobalContext.store('dlpFile', linkFile[Constants.FILE_OPEN_HISTORY_ZERO]); 174 GlobalContext.store('dlpFd', linkFile[Constants.FILE_OPEN_HISTORY_TWO]); 175 let dlpFileName: string = (abilityWant.parameters?.fileName as Record<string, string>)?.name; 176 GlobalContext.store('dlpFileName', dlpFileName); 177 GlobalContext.store('linkFileName', linkFileName); 178 console.info(TAG, 'find dlp file', dlpFileName, GlobalContext.load('dlpFd')); 179 resolve(); 180 return; 181 } 182 } 183 } 184 console.error(TAG, 'request from sandbox, but can not find dlp file by linkFileName', linkFileName); 185 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError); 186 reject(); 187 }); 188 } 189 190 async openDlpFile(): Promise<void> { 191 return new Promise(async (resolve, reject) => { 192 try { 193 let dlpFileName: string = ((GlobalContext.load('abilityWant')as Want).parameters?.fileName as Record<string, string>)?.name; 194 GlobalContext.store('dlpFileName', dlpFileName); 195 let dlpFd: number = getFileFd(GlobalContext.load('uri') as string); 196 GlobalContext.store('dlpFd', dlpFd); 197 console.info(TAG, 'openDLPFile', dlpFileName, dlpFd); 198 GlobalContext.store('dlpFile', await dlpPermission.openDLPFile(dlpFd)); 199 resolve(); 200 } catch (err) { 201 console.error(TAG, 'openDLPFile', GlobalContext.load('dlpFileName') as string, 'failed', (err as BusinessError).code, (err as BusinessError).message); 202 await this.showErrorDialogAndExit(err as BusinessError); 203 reject(err); 204 } 205 }); 206 } 207 208 checkValidWant(): boolean { 209 let parameters = (GlobalContext.load('abilityWant') as Want).parameters; 210 if (parameters === undefined) { 211 console.error(TAG, 'need parameters in want'); 212 return false; 213 } 214 if (parameters.fileName === undefined) { 215 console.error(TAG, 'need fileName in want.parameters'); 216 return false; 217 } 218 if ((parameters.fileName as Record<string, string>).name === undefined) { 219 console.error(TAG, 'need name in want.parameters.fileName'); 220 return false; 221 } 222 if ((GlobalContext.load('abilityWant') as Want).uri === undefined) { 223 console.error(TAG, 'need uri in want'); 224 return false; 225 } 226 this.callerToken = parameters['ohos.aafwk.param.callerToken'] as number; 227 let callerBundleName: string = parameters['ohos.aafwk.param.callerBundleName'] as string; 228 if (this.callerToken === undefined || callerBundleName === undefined) { 229 console.error(TAG, 'need caller info in want.parameters'); 230 return false; 231 } 232 let uri = String((GlobalContext.load('abilityWant') as Want).uri); 233 if ((GlobalContext.load('linkSet') as Set<string>).has(uri)) { 234 console.error(TAG, 'invalid uri for opened link uri'); 235 return false; 236 } 237 238 if (uri.indexOf(Constants.FUSE_PATH) !== -1 || !isValidPath(uri)) { 239 console.error(TAG, 'invalid uri in want.uri'); 240 return false; 241 } 242 return true; 243 } 244 245 async getNewWantPage(): Promise<void> { 246 console.log(TAG, 'getNewWantPage start'); 247 let windowStage: window.WindowStage = GlobalContext.load('windowStage') as window.WindowStage; 248 if (!this.checkValidWant()) { 249 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_PARAM_ERROR } as BusinessError); 250 return; 251 } 252 let accountInfo: osAccount.OsAccountInfo; 253 try { 254 accountInfo = await getOsAccountInfo(); 255 } catch (err) { 256 console.error(TAG, 'getOsAccountInfo failed', (err as BusinessError).code, (err as BusinessError).message); 257 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_GET_ACCOUNT_ERROR } as BusinessError); 258 return; 259 } 260 if (!checkAccountLogin(accountInfo)) { 261 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_NO_ACCOUNT_ERROR } as BusinessError); 262 return; 263 } 264 let requestIsFromSandBox: boolean = await judgeIsSandBox(); 265 GlobalContext.store('requestIsFromSandBox', requestIsFromSandBox); 266 console.info(TAG, 'request is from sandbox', requestIsFromSandBox); 267 if (requestIsFromSandBox) { 268 try { 269 await this.findDlpFile(); 270 } catch { 271 return; 272 } 273 } else { 274 let fileName: string = ((GlobalContext.load('abilityWant') as Want).parameters?.fileName as Record<string, string>)?.name; 275 let isDlpSuffix: boolean = fileName.endsWith('.dlp'); 276 if (!isDlpSuffix) { 277 console.info(TAG, fileName, 'is not a dlp file'); 278 GlobalContext.store('originFileName', fileName); 279 GlobalContext.store('originFd', getFileFd(GlobalContext.load('uri') as string)); 280 windowStage.loadContent('pages/encryptionProtection', (err: BusinessError) =>{ 281 if (err.code !== 0) { 282 console.error(TAG, 'loadContent failed', err.code, err.message); 283 } 284 }); 285 windowStage.getMainWindow().then((win: window.Window) => { 286 win.setBackgroundColor('#00FFFFFF'); 287 }); 288 return; 289 } else { 290 let uri: string = GlobalContext.load('uri') as string; 291 if ((GlobalContext.load('fileOpenHistory') as Map<string, (number | string)[]>)?.has(uri)) { 292 await this.showErrorDialogAndExit({ code: Constants.ERR_JS_APP_OPEN_REJECTED } as BusinessError); 293 return; 294 } 295 try { 296 await this.openDlpFile(); 297 } catch { 298 return; 299 } 300 GlobalContext.store('fileOpenHistoryFromMain', new Map<string, (string | number)[]>()); 301 let fileOpenHistoryFromMain: Map<string, (string | number)[]> = GlobalContext.load('fileOpenHistoryFromMain') as Map<string, (string | number)[]>; 302 fileOpenHistoryFromMain.set(uri, [uri, GlobalContext.load('dlpFileName') as string, GlobalContext.load('dlpFd') as number]); 303 console.log(TAG, 'fileOpenHistoryFromMain add', JSON.stringify(fileOpenHistoryFromMain)); 304 } 305 } 306 this.gotoPage(windowStage, accountInfo); 307} 308 309 async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> { 310 console.info(TAG, 'onWindowStageCreate'); 311 GlobalContext.store('windowStage', windowStage); 312 try { 313 let atManager = abilityAccessCtrl.createAtManager(); 314 await atManager.requestPermissionsFromUser(GlobalContext.load('context') as common.UIAbilityContext, permissionList); 315 } catch (err) { 316 console.error(TAG, 'requestPermissionsFromUser failed', err.code, err.message); 317 return; 318 } 319 this.getNewWantPage(); 320 } 321 322 onWindowStageDestroy(): void { 323 console.info(TAG, 'onWindowStageDestroy'); 324 } 325 326 onForeground(): void { 327 console.info(TAG, 'onForeground'); 328 } 329 330 onBackground() { 331 console.info(TAG, 'onBackground'); 332 } 333}; 334