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 */ 15 16import { BusinessError } from '@ohos.base'; 17import Want from '@ohos.app.ability.Want'; 18import fileUri from '@ohos.file.fileuri'; 19import ServiceExtension from '@ohos.app.ability.ServiceExtensionAbility'; 20import fs from '@ohos.file.fs'; 21import dlpPermission from '@ohos.dlpPermission'; 22import IdlDlpRpcServiceStub from '../MainAbility/data/IIdlDlpRpcServiceTs/id_dlpRpc_service_stub'; 23import { 24 closeDlpFileCallback, 25 sandBoxLinkFileCallback, 26 fileOpenHistoryCallback, 27 linkSetCallback, 28 genDlpFileCallback, 29 openDlpFileCallback, 30 recoverDlpFileCallback, 31 replaceDlpLinkFileCallback, 32 resumeFuseLinkCallback, 33 stopFuseLinkCallback 34} from '../MainAbility/data/IIdlDlpRpcServiceTs/i_id_dlpRpc_service'; 35import IDLDLPProperty from './sequenceable/dlpClass'; 36import { IAuthUser } from './sequenceable/dlpClass'; 37import GlobalContext from '../common/GlobalContext'; 38import Constants from '../common/constant'; 39import { HiLog } from '../common/HiLog'; 40 41const TAG = 'service_extability'; 42 43class DlpRpcServiceStub extends IdlDlpRpcServiceStub { 44 private dlpFileMap: Map<string, dlpPermission.DLPFile | null> = new Map<string, dlpPermission.DLPFile | null>(); 45 private inFile: fs.File | undefined = undefined; 46 private outFile: fs.File | undefined = undefined; 47 48 constructor(des: string) { 49 super(des); 50 } 51 52 getOpeningFile(inputUri: string) : dlpPermission.DLPFile | null { 53 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 54 if (sandbox2linkFile !== undefined) { 55 for (let key of Array.from<(number | string | dlpPermission.DLPFile)[][]>(sandbox2linkFile.values())) { 56 for (let j of key) { 57 if (j[4] === inputUri) { 58 let dlpFile: dlpPermission.DLPFile = j[0] as dlpPermission.DLPFile; 59 return dlpFile; 60 } 61 } 62 } 63 } 64 65 if (this.dlpFileMap.has(inputUri)) { 66 let dlpFile: dlpPermission.DLPFile = this.dlpFileMap.get(inputUri) as dlpPermission.DLPFile; 67 return dlpFile; 68 } 69 return null; 70 } 71 72 async genDlpFile(inputUri: string, outputUri: string, dlp: IDLDLPProperty, callback: genDlpFileCallback 73 ): Promise<void> { 74 HiLog.info(TAG, `genDlpFile in service`); 75 let result: Record<string, number>; 76 try { 77 result = await this.genDlpFileFd(inputUri, outputUri); 78 } catch (error) { 79 callback(error); 80 return; 81 } 82 let dlpP: dlpPermission.DLPProperty = { 83 'ownerAccount' : dlp.ownerAccount, 84 'ownerAccountID' : dlp.ownerAccountID, 85 'ownerAccountType' : dlp.ownerAccountType, 86 'authUserList' : dlp.authUserList, 87 'contactAccount' : dlp.contactAccount, 88 'offlineAccess' : dlp.offlineAccess, 89 'everyoneAccessList' : dlp.everyoneAccessList, 90 'expireTime': dlp.expireTime 91 } 92 try { 93 let dlpFile = await dlpPermission.generateDLPFile(result.inFileFd, result.outFileFd, dlpP); 94 if (!this.dlpFileMap.has(outputUri)) { 95 this.dlpFileMap.set(outputUri, dlpFile); 96 AppStorage.setOrCreate('dlpFileMap', this.dlpFileMap); 97 } else { 98 let rawDlpFile = this.dlpFileMap.get(outputUri) ?? null; 99 if (rawDlpFile !== null) { 100 try { 101 await rawDlpFile.closeDLPFile(); 102 } catch (err) { 103 HiLog.error(TAG, `closeDlpFile file: ${JSON.stringify(err)}`); 104 } 105 } 106 this.dlpFileMap.delete(outputUri); 107 this.dlpFileMap.set(outputUri, dlpFile); 108 AppStorage.setOrCreate('dlpFileMap', this.dlpFileMap); 109 } 110 callback(0); 111 } catch (err) { 112 HiLog.info(TAG, `genDlpFile file: ${JSON.stringify(err)}`); 113 await this.closeFile(); 114 callback((err as BusinessError).code); 115 } 116 } 117 118 async closeFile(): Promise<void> { 119 try { 120 await fs.close(this.inFile); 121 } catch (err) { 122 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 123 } 124 try { 125 await fs.close(this.outFile); 126 } catch (err) { 127 HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`); 128 } 129 } 130 131 async genDlpFileFd(inputUri: string, outputUri: string): Promise<Record<string, number>> { 132 return new Promise(async (resolve, reject) => { 133 let inFileFd: number = -1; 134 let outFileFd: number = -1; 135 try { 136 this.inFile = await fs.open(inputUri, fs.OpenMode.READ_WRITE); 137 inFileFd = this.inFile.fd; 138 } catch (error) { 139 HiLog.error(TAG, `open: ${inputUri}, failed: ${JSON.stringify(error)}`); 140 reject((error as BusinessError).code); 141 return; 142 } 143 let uriInfo: fileUri.FileUri = new fileUri.FileUri(''); 144 try { 145 uriInfo = new fileUri.FileUri(outputUri); 146 } catch (err) { 147 HiLog.error(TAG, `fileUri fail: ${JSON.stringify(err)}`); 148 } 149 try { 150 this.outFile = await fs.open(outputUri, fs.OpenMode.READ_WRITE); 151 outFileFd = this.outFile.fd; 152 } catch (error) { 153 try { 154 await fs.close(this.inFile); 155 await fs.unlink(uriInfo.path); 156 } catch (err) { 157 HiLog.error(TAG, `unlink fail: ${JSON.stringify(err)}`); 158 } 159 reject((error as BusinessError).code); 160 return; 161 } 162 let result = { 163 'inFileFd': inFileFd, 164 'outFileFd': outFileFd 165 } as Record<string, number>; 166 resolve(result); 167 }) 168 } 169 170 async openDlpFile(srcUri: string, callerAppId: string, callback: openDlpFileCallback): Promise<void> { 171 HiLog.info(TAG, `openDlpFile start: ${srcUri}`); 172 let inFile = await fs.open(srcUri, fs.OpenMode.READ_WRITE); 173 let dlpFile: dlpPermission.DLPFile; 174 let authUserListNew: IAuthUser[] = []; 175 try { 176 dlpFile = await dlpPermission.openDLPFile(inFile.fd, callerAppId); 177 dlpFile.dlpProperty.authUserList?.forEach(item => { 178 authUserListNew.push( 179 new IAuthUser( 180 item.authAccount, 181 item.authAccountType, 182 item.dlpFileAccess, 183 item.permExpiryTime 184 )) 185 }) 186 let _dlp = new IDLDLPProperty( 187 dlpFile.dlpProperty.ownerAccount, 188 dlpFile.dlpProperty.ownerAccountID, 189 dlpFile.dlpProperty.ownerAccountType, 190 authUserListNew, 191 dlpFile.dlpProperty.contactAccount, 192 dlpFile.dlpProperty.offlineAccess, 193 dlpFile.dlpProperty.everyoneAccessList ?? [], 194 dlpFile.dlpProperty.expireTime ?? 0, 195 ); 196 callback(0, _dlp, ''); 197 if (!this.dlpFileMap.has(srcUri)) { 198 this.dlpFileMap.set(srcUri, dlpFile); 199 AppStorage.setOrCreate('dlpFileMap', this.dlpFileMap); 200 } else { 201 HiLog.info(TAG, `map is overwrite`); 202 this.dlpFileMap.delete(srcUri); 203 this.dlpFileMap.set(srcUri, dlpFile); 204 AppStorage.setOrCreate('dlpFileMap', this.dlpFileMap); 205 } 206 } catch (err) { 207 let _dlp = new IDLDLPProperty('', '', 0, [], '', true, [], 0); 208 callback((err as BusinessError).code, _dlp, (err as BusinessError).message); 209 } finally { 210 if (inFile) { 211 fs.closeSync(inFile); 212 } 213 } 214 } 215 216 async stopFuseLink(uri: string, callback: stopFuseLinkCallback): Promise<void> { 217 HiLog.info(TAG, `stopFuseLink start: ${uri}`); 218 let dlpFile: dlpPermission.DLPFile | null = this.getOpeningFile(uri); 219 if (dlpFile !== null) { 220 await dlpFile.stopFuseLink(); 221 } else { 222 HiLog.info(TAG, `stopFuseLink not find: ${uri}`); 223 callback(-1); 224 } 225 } 226 227 async resumeFuseLink(uri: string, callback: resumeFuseLinkCallback): Promise<void> { 228 HiLog.info(TAG, `resumeFuseLink start`); 229 let dlpFile: dlpPermission.DLPFile | null = this.getOpeningFile(uri); 230 if (dlpFile !== null) { 231 await dlpFile.resumeFuseLink(); 232 } else { 233 HiLog.info(TAG, `resumeFuseLink not find: ${uri}`); 234 callback(-1); 235 } 236 } 237 238 async replaceDlpLinkFile(srcUri: string, linkFileName: string, callback: replaceDlpLinkFileCallback): Promise<void> { 239 GlobalContext.load('sandbox2linkFile'); 240 if (this.dlpFileMap.has(srcUri)) { 241 let dlpFile: dlpPermission.DLPFile = this.dlpFileMap.get(srcUri) as dlpPermission.DLPFile; 242 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 243 for (let key of Array.from<(number | string | dlpPermission.DLPFile)[][]>(sandbox2linkFile.values())) { 244 for (let j of key) { 245 if (j[1] === linkFileName) { 246 j[0] = dlpFile; 247 } 248 } 249 } 250 await dlpFile.replaceDLPLinkFile(linkFileName); 251 } else { 252 HiLog.info(TAG, `replaceDLPLinkFile not find: ${srcUri}`); 253 callback(-1); 254 } 255 } 256 257 async recoverDlpFile(srcUri: string, pathUri: string, callback: recoverDlpFileCallback): Promise<void> { 258 let dlpFile: dlpPermission.DLPFile | null = this.getOpeningFile(srcUri); 259 if (dlpFile !== null) { 260 let inFile: fs.File | undefined; 261 try { 262 inFile = await fs.open(pathUri, fs.OpenMode.READ_WRITE); 263 await dlpFile.recoverDLPFile(inFile.fd); 264 } catch (err) { 265 HiLog.error(TAG, `recoverDlpFileInner4: ${JSON.stringify(err)}`); 266 callback((err as BusinessError).code); 267 } finally { 268 if (inFile) { 269 fs.closeSync(inFile); 270 } 271 } 272 } else { 273 HiLog.info(TAG, `recoverDlpFile not find: ${srcUri}`); 274 callback(-1); 275 } 276 } 277 278 async closeDlpFile(srcUri: string, callback: closeDlpFileCallback): Promise<void> { 279 HiLog.info(TAG, `closeDlpFile start`); 280 let dlpFile: dlpPermission.DLPFile | null = this.getOpeningFile(srcUri); 281 if (dlpFile !== null) { 282 try { 283 await dlpFile.closeDLPFile(); 284 if (this.dlpFileMap.has(srcUri)) { 285 this.dlpFileMap.delete(srcUri); 286 AppStorage.setOrCreate('dlpFileMap', this.dlpFileMap); 287 } 288 callback(0); 289 } catch (err) { 290 HiLog.error(TAG, `closeDlpFile file: ${JSON.stringify(err)}`); 291 callback((err as BusinessError).code); 292 } 293 } 294 } 295 296 async sandBoxLinkFile(linkFileName: string, callerToken: number, callback: sandBoxLinkFileCallback): Promise<void> { 297 let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> = GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>; 298 for (let value of Array.from<(number | string | dlpPermission.DLPFile)[][]>(sandbox2linkFile.values())) { 299 for (let linkFile of value) { 300 let _dlp = new IDLDLPProperty('', '', 0, [], '', true, [], 0); 301 if (linkFile[Constants.FILE_OPEN_HISTORY_ONE] === linkFileName) { 302 let authUserListNew: IAuthUser[] = []; 303 if (callerToken !== linkFile[Constants.FILE_OPEN_HISTORY_THREE]) { 304 HiLog.error(TAG, `found file, but token invalid: ${linkFileName}`); 305 callback(-1, _dlp, ''); 306 } else { 307 let linkFileInfo: Record<number, number | string | dlpPermission.DLPFile> = 308 linkFile[0] as Record<number, number | string | dlpPermission.DLPFile>; 309 linkFileInfo['dlpProperty'].authUserList.forEach((item: IAuthUser)=> { 310 authUserListNew.push( 311 new IAuthUser( 312 item.authAccount, 313 item.authAccountType, 314 item.dlpFileAccess, 315 item.permExpiryTime 316 )) 317 }) 318 _dlp = new IDLDLPProperty( 319 linkFileInfo['dlpProperty'].ownerAccount, 320 linkFileInfo['dlpProperty'].ownerAccountID, 321 linkFileInfo['dlpProperty'].ownerAccountType, 322 authUserListNew, 323 linkFileInfo['dlpProperty'].contactAccount, 324 linkFileInfo['dlpProperty'].offlineAccess, 325 linkFileInfo['dlpProperty'].everyoneAccessList ?? [], 326 linkFileInfo['dlpProperty'].expireTime ?? 0 327 ); 328 let fileUri = linkFile[4]; 329 callback(0, _dlp, fileUri.toString()); 330 } 331 } else { 332 HiLog.error(TAG, `request from sandbox, but can not find dlp file by linkFileName: ${linkFileName}`); 333 callback(-1, _dlp, ''); 334 } 335 } 336 } 337 } 338 339 async fileOpenHistory(uri: string, callback: fileOpenHistoryCallback): Promise<void> { 340 HiLog.info(TAG, `fileOpenHistory start`); 341 if ((GlobalContext.load('fileOpenHistory') as Map<string, (number | string)[]>)?.has(uri)) { 342 callback(0); 343 } else { 344 callback(-1); 345 } 346 } 347 348 async linkSet(uri: string, callback: linkSetCallback): Promise<void> { 349 HiLog.info(TAG, `linkSet start`); 350 if ((GlobalContext.load('linkSet') as Map<string, (number | string)[]>)?.has(uri)) { 351 callback(0); 352 } else { 353 callback(-1); 354 } 355 } 356} 357 358export default class ServiceExtAbility extends ServiceExtension { 359 onCreate(want: Want) { 360 HiLog.info(TAG, `onCreate, want: ${want.abilityName}`); 361 } 362 363 onRequest(want: Want, startId: number) { 364 HiLog.info(TAG, `onRequest, want: ${want.abilityName}`); 365 } 366 367 onConnect(want: Want) { 368 HiLog.info(TAG, `service onConnect, want: ${want.abilityName}`); 369 return new DlpRpcServiceStub('dlpRpc service stub'); 370 } 371 372 onDisconnect(want: Want): void { 373 HiLog.info(TAG, `onDisconnect, want: ${want.abilityName}`); 374 } 375 376 onDestroy(): void { 377 HiLog.info(TAG, `onDestroy`); 378 } 379}