1/* 2 * Copyright (c) 2025 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 { HiLog } from '../../common/HiLog'; 17import Result from '../../common/Result'; 18import { ResultMsg } from '../../common/ResultMsg'; 19import DecryptContent from '../data/DecryptContent'; 20import { ArkTSUtils } from '@kit.ArkTS'; 21import Constants from '../../common/constant'; 22import FileUtils from '../../common/FileUtils/FileUtils'; 23 24const TAG: string = 'OpenDlpFileManager'; 25 26export enum DecryptState { 27 NOT_STARTED = 1, 28 DECRYPTING = 2, 29 DECRYPTED = 3, 30 ENCRYPTING = 4, 31} 32 33export interface DecryptStatus { 34 state: DecryptState; 35 startTime?: number; 36} 37 38export class OpenDlpFileManager { 39 private static instance: OpenDlpFileManager; 40 private statusMap: Map<string, DecryptStatus>; 41 private contentMap: Map<string, DecryptContent>; 42 private lock: ArkTSUtils.locks.AsyncLock; 43 44 private constructor() { 45 this.statusMap = new Map<string, DecryptStatus>(); 46 this.contentMap = new Map<string, DecryptContent>(); 47 this.lock = new ArkTSUtils.locks.AsyncLock(); 48 } 49 50 static getInstance(): OpenDlpFileManager { 51 if (!OpenDlpFileManager.instance) { 52 OpenDlpFileManager.instance = new OpenDlpFileManager(); 53 } 54 return OpenDlpFileManager.instance; 55 } 56 57 public async setStatus(uri: string, status: DecryptStatus): Promise<Result<void>> { 58 HiLog.info(TAG, `setStatus: filename: ${FileUtils.getFileNameByUri(uri)}, status = ${status.state}`); 59 try { 60 await this.lock.lockAsync(() => { 61 this.statusMap.set(uri, status); 62 }); 63 return ResultMsg.buildSuccess(); 64 } catch (error) { 65 HiLog.wrapError(TAG, error, 'setStatus lockAsync error'); 66 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 67 } 68 } 69 70 public getStatus(uri: string): Result<DecryptStatus> { 71 let status: DecryptStatus | undefined; 72 status = this.statusMap.get(uri) || { state: DecryptState.NOT_STARTED }; 73 HiLog.info(TAG, `getStatus: filename: ${FileUtils.getFileNameByUri(uri)}, status = ${status.state}`); 74 return ResultMsg.buildSuccess(status); 75 } 76 77 public async deleteStatus(uri: string): Promise<Result<void>> { 78 HiLog.info(TAG, `deleteStatus: filename: ${FileUtils.getFileNameByUri(uri)}`); 79 try { 80 await this.lock.lockAsync(() => { 81 this.statusMap.delete(uri); 82 }); 83 return ResultMsg.buildSuccess(); 84 } catch (error) { 85 HiLog.wrapError(TAG, error, 'deleteStatus lockAsync error'); 86 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 87 } 88 } 89 90 public async deleteStatusButNotHasDecrypted(uri: string): Promise<Result<void>> { 91 HiLog.info(TAG, `deleteStatusButNotHasDecrypted: filename: ${FileUtils.getFileNameByUri(uri)}`); 92 try { 93 const status = this.statusMap.get(uri) || { state: DecryptState.NOT_STARTED }; 94 if (status.state !== DecryptState.DECRYPTED) { 95 await this.lock.lockAsync(() => { 96 HiLog.info(TAG, `delete filename: ${FileUtils.getFileNameByUri(uri)}`); 97 this.statusMap.delete(uri); 98 }); 99 } 100 return ResultMsg.buildSuccess(); 101 } catch (error) { 102 HiLog.wrapError(TAG, error, 'deleteStatus lockAsync error'); 103 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 104 } 105 } 106 107 public async addDecryptContent(uri: string, content: DecryptContent): Promise<Result<void>> { 108 HiLog.info(TAG, `addDecryptContent filename: ${FileUtils.getFileNameByUri(uri)}`); 109 try { 110 await this.lock.lockAsync(() => { 111 this.contentMap.set(uri, content); 112 this.statusMap.set(uri, { state: DecryptState.DECRYPTED }); 113 }); 114 return ResultMsg.buildSuccess(); 115 } catch (error) { 116 HiLog.wrapError(TAG, error, 'setContent lockAsync error'); 117 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 118 } 119 } 120 121 public getHasDecryptedContent(uri: string): Result<DecryptContent> { 122 let content = this.contentMap.get(uri); 123 if (!content) { 124 HiLog.error(TAG, 'getHasDecryptedContent error'); 125 return ResultMsg.getErrMsg(Constants.ERR_CODE_PARAMS_CHECK_ERROR); 126 } 127 return ResultMsg.buildSuccess(content); 128 } 129 130 private getContentByLinkFileName(linkFileName: string): DecryptContent | undefined { 131 for (const entry of this.contentMap) { 132 if (entry[1].linkFileName === linkFileName) { 133 HiLog.info(TAG, 'getContentByLinkFileName found'); 134 return entry[1]; 135 } 136 } 137 HiLog.info(TAG, 'getContentByLinkFileName not found'); 138 return undefined; 139 } 140 141 public getHasDecryptedContentByLinkFileName(linkFileName: string): Result<DecryptContent> { 142 const content = this.getContentByLinkFileName(linkFileName); 143 return ResultMsg.buildSuccess(content); 144 } 145 146 private getContentByTokenId(tokenId: number): DecryptContent | undefined { 147 for (const entry of this.contentMap) { 148 if (entry[1].appInfo.tokenID === tokenId) { 149 HiLog.info(TAG, 'getContentByTokenId found'); 150 return entry[1]; 151 } 152 } 153 HiLog.info(TAG, 'getContentByTokenId not found'); 154 return undefined; 155 } 156 157 public getHasDecryptedContentByTokenId(tokenId: number): Result<DecryptContent> { 158 const content = this.getContentByTokenId(tokenId); 159 return ResultMsg.buildSuccess(content); 160 } 161 162 private removeMatchingEntries(bundleName: string, sandboxAppIndex: number): Set<DecryptContent> { 163 let decryptContents = new Set<DecryptContent>(); 164 const entries = Array.from(this.contentMap.entries()); 165 entries.forEach(entry => { 166 const key = entry[0]; 167 const value = entry[1]; 168 if (value.openDlpFileData.sandboxBundleName === bundleName && 169 value.appInfo.appIndex === sandboxAppIndex) { 170 decryptContents.add(value); 171 this.contentMap.delete(key); 172 this.statusMap.delete(key); 173 } 174 }); 175 return decryptContents; 176 } 177 178 public async removeByBundleNameAndAppIndex(bundleName: string, sandboxAppIndex: number): 179 Promise<Result<Set<DecryptContent>>> { 180 try { 181 let decryptContents: Set<DecryptContent> = new Set<DecryptContent>(); 182 await this.lock.lockAsync(() => { 183 decryptContents = this.removeMatchingEntries(bundleName, sandboxAppIndex); 184 }); 185 return ResultMsg.buildSuccess(decryptContents); 186 } catch (error) { 187 HiLog.wrapError(TAG, error, 'removeByBundleNameAndAppIndex lockAsync error'); 188 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 189 } 190 } 191 192 public getHasDecryptedSize(): Result<number> { 193 const size = this.contentMap.size; 194 return ResultMsg.buildSuccess(size); 195 } 196 197 public async removeAllByUri(uri: string): Promise<Result<void>> { 198 try { 199 await this.lock.lockAsync(() => { 200 this.statusMap.delete(uri); 201 this.contentMap.delete(uri); 202 }); 203 return ResultMsg.buildSuccess(); 204 } catch (error) { 205 HiLog.wrapError(TAG, error, 'removeContentByUri lockAsync error'); 206 return ResultMsg.getErrMsg(Constants.ERR_CODE_GET_LOCK_ASYNC_ERROR); 207 } 208 } 209}