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 #define MLOG_TAG "Thumbnail"
16
17 #include "thumbnail_file_utils.h"
18
19 #include <filesystem>
20 #include <ftw.h>
21 #include <unordered_map>
22
23 #ifdef HAS_WIFI_MANAGER_PART
24 #include "wifi_device.h"
25 #endif
26
27 #include "dfx_utils.h"
28 #include "medialibrary_errno.h"
29 #include "medialibrary_kvstore_manager.h"
30 #include "medialibrary_tracer.h"
31 #include "media_file_utils.h"
32 #include "media_log.h"
33 #include "thumbnail_const.h"
34
35 using namespace std;
36
37 namespace OHOS {
38 namespace Media {
39
40 const int32_t OPEN_FDS = 64;
41 const int32_t BEGIN_TIMESTAMP_DIR_LEVEL = 1;
42 const std::string BEGIN_TIMESTAMP_DIR_PREFIX = "beginTimeStamp";
43
44 const std::string LCD_FILE_NAME = "LCD.jpg";
45 const std::string THUMB_FILE_NAME = "THM.jpg";
46 const std::string THUMB_ASTC_FILE_NAME = "THM_ASTC.astc";
47 static const std::unordered_map<ThumbnailType, std::string> THUMB_FILE_NAME_MAP = {
48 { ThumbnailType::LCD, LCD_FILE_NAME },
49 { ThumbnailType::THUMB, THUMB_FILE_NAME },
50 { ThumbnailType::THUMB_ASTC, THUMB_ASTC_FILE_NAME },
51 { ThumbnailType::LCD_EX, LCD_FILE_NAME },
52 { ThumbnailType::THUMB_EX, THUMB_FILE_NAME }
53 };
54
GetThumbnailSuffix(ThumbnailType type)55 std::string ThumbnailFileUtils::GetThumbnailSuffix(ThumbnailType type)
56 {
57 string suffix;
58 switch (type) {
59 case ThumbnailType::THUMB:
60 suffix = THUMBNAIL_THUMB_SUFFIX;
61 break;
62 case ThumbnailType::THUMB_ASTC:
63 suffix = THUMBNAIL_THUMB_ASTC_SUFFIX;
64 break;
65 case ThumbnailType::LCD:
66 suffix = THUMBNAIL_LCD_SUFFIX;
67 break;
68 default:
69 return "";
70 }
71 return suffix;
72 }
73
GetThumbnailDir(const ThumbnailData & data)74 std::string ThumbnailFileUtils::GetThumbnailDir(const ThumbnailData &data)
75 {
76 CHECK_AND_RETURN_RET_LOG(!data.path.empty(), "", "Path is empty");
77 string fileName = GetThumbnailPath(data.path, THUMBNAIL_LCD_SUFFIX);
78 return MediaFileUtils::GetParentPath(fileName);
79 }
80
GetThumbExDir(const ThumbnailData & data)81 std::string ThumbnailFileUtils::GetThumbExDir(const ThumbnailData &data)
82 {
83 CHECK_AND_RETURN_RET_LOG(!data.path.empty(), "", "Path is empty");
84 string fileName = GetThumbnailPath(data.path, THUMBNAIL_THUMB_EX_SUFFIX);
85 return MediaFileUtils::GetParentPath(fileName);
86 }
87
IsExThumbType(ThumbnailType type)88 bool IsExThumbType(ThumbnailType type)
89 {
90 if (type == ThumbnailType::LCD_EX || type == ThumbnailType::THUMB_EX) {
91 return true;
92 }
93 return false;
94 }
95
GetThumbFileSize(const ThumbnailData & data,const ThumbnailType type,size_t & size)96 bool ThumbnailFileUtils::GetThumbFileSize(const ThumbnailData& data, const ThumbnailType type, size_t& size)
97 {
98 CHECK_AND_RETURN_RET_LOG(THUMB_FILE_NAME_MAP.find(type) != THUMB_FILE_NAME_MAP.end(), false,
99 "invalid ThumbnailType: %{public}d", type);
100 std::string thumbDir;
101 if (!IsExThumbType(type)) {
102 thumbDir = GetThumbnailDir(data);
103 } else {
104 thumbDir = GetThumbExDir(data);
105 }
106 CHECK_AND_RETURN_RET_LOG(!thumbDir.empty(), false, "GetThumbnailDir failed. type: %{public}d", type);
107 std::string thumbPath = thumbDir + "/" + THUMB_FILE_NAME_MAP.at(type);
108 CHECK_AND_RETURN_RET_LOG(MediaFileUtils::GetFileSize(thumbPath, size), false,
109 "GetFileSize failed. path: %{public}s", DfxUtils::GetSafePath(thumbPath).c_str());
110 return true;
111 }
112
DeleteThumbnailDir(const ThumbnailData & data)113 bool ThumbnailFileUtils::DeleteThumbnailDir(const ThumbnailData &data)
114 {
115 string dirName = GetThumbnailDir(data);
116 CHECK_AND_RETURN_RET_LOG(!dirName.empty(), false, "Path:%{public}s is invalid",
117 DfxUtils::GetSafePath(data.path).c_str());
118 CHECK_AND_RETURN_RET_LOG(RemoveDirectoryAndFile(dirName), false, "Failed to remove path: %{public}s",
119 DfxUtils::GetSafePath(dirName).c_str());
120 return true;
121 }
122
DeleteAllThumbFiles(const ThumbnailData & data)123 bool ThumbnailFileUtils::DeleteAllThumbFiles(const ThumbnailData &data)
124 {
125 bool isDelete = true;
126 isDelete = DeleteThumbFile(data, ThumbnailType::THUMB) && isDelete;
127 isDelete = DeleteThumbFile(data, ThumbnailType::THUMB_ASTC) && isDelete;
128 isDelete = DeleteThumbFile(data, ThumbnailType::LCD) && isDelete;
129 isDelete = DeleteThumbExDir(data) && isDelete;
130 isDelete = DeleteBeginTimestampDir(data) && isDelete;
131 return isDelete;
132 }
133
DeleteMonthAndYearAstc(const ThumbnailData & data)134 bool ThumbnailFileUtils::DeleteMonthAndYearAstc(const ThumbnailData &data)
135 {
136 bool isDelete = true;
137 isDelete = DeleteAstcDataFromKvStore(data, ThumbnailType::MTH_ASTC) && isDelete;
138 isDelete = DeleteAstcDataFromKvStore(data, ThumbnailType::YEAR_ASTC) && isDelete;
139 return isDelete;
140 }
141
BatchDeleteMonthAndYearAstc(const ThumbnailDataBatch & dataBatch)142 bool ThumbnailFileUtils::BatchDeleteMonthAndYearAstc(const ThumbnailDataBatch &dataBatch)
143 {
144 bool isBatchDeleteSuccess = true;
145 isBatchDeleteSuccess = BatchDeleteAstcData(dataBatch, ThumbnailType::MTH_ASTC) && isBatchDeleteSuccess;
146 isBatchDeleteSuccess = BatchDeleteAstcData(dataBatch, ThumbnailType::YEAR_ASTC) && isBatchDeleteSuccess;
147 return isBatchDeleteSuccess;
148 }
149
DeleteThumbFile(const ThumbnailData & data,ThumbnailType type)150 bool ThumbnailFileUtils::DeleteThumbFile(const ThumbnailData &data, ThumbnailType type)
151 {
152 string fileName = GetThumbnailPath(data.path, GetThumbnailSuffix(type));
153 CHECK_AND_RETURN_RET_LOG(!fileName.empty(), false, "Path:%{public}s or type:%{public}d is invalid",
154 DfxUtils::GetSafePath(data.path).c_str(), type);
155
156 CHECK_AND_RETURN_RET(access(fileName.c_str(), F_OK) == 0, true);
157 CHECK_AND_RETURN_RET_LOG(MediaFileUtils::DeleteFile(fileName), false,
158 "Delete file faild, errno:%{public}d, path:%{public}s", errno, DfxUtils::GetSafePath(fileName).c_str());
159 return true;
160 }
161
DeleteThumbExDir(const ThumbnailData & data)162 bool ThumbnailFileUtils::DeleteThumbExDir(const ThumbnailData &data)
163 {
164 string dirName = GetThumbExDir(data);
165 CHECK_AND_RETURN_RET_LOG(!dirName.empty(), false, "Path:%{public}s is invalid",
166 DfxUtils::GetSafePath(data.path).c_str());
167
168 CHECK_AND_RETURN_RET_LOG(RemoveDirectoryAndFile(dirName), false,
169 "Failed to delete THM_EX directory, path: %{public}s, id: %{public}s",
170 DfxUtils::GetSafePath(dirName).c_str(), data.id.c_str());
171 return true;
172 }
173
DeleteBeginTimestampDirCallback(const char * fpath,const struct stat * sb,int32_t typeflag,struct FTW * ftwbuf)174 int32_t DeleteBeginTimestampDirCallback(const char *fpath, const struct stat *sb, int32_t typeflag, struct FTW *ftwbuf)
175 {
176 CHECK_AND_RETURN_RET_LOG(fpath != nullptr && ftwbuf != nullptr, E_ERR, "Fpath or ftwbuf is nullptr");
177 CHECK_AND_RETURN_RET(typeflag == FTW_D || typeflag == FTW_DNR, E_OK);
178 CHECK_AND_RETURN_RET(ftwbuf->level == BEGIN_TIMESTAMP_DIR_LEVEL, E_OK);
179
180 string path(fpath);
181 string folderName = MediaFileUtils::GetFileName(path);
182 CHECK_AND_RETURN_RET(folderName.find(BEGIN_TIMESTAMP_DIR_PREFIX) == 0, E_OK);
183 CHECK_AND_RETURN_RET_LOG(ThumbnailFileUtils::RemoveDirectoryAndFile(path), E_ERR,
184 "Failed to remove path: %{public}s", DfxUtils::GetSafePath(path).c_str());
185 return E_OK;
186 }
187
DeleteBeginTimestampDir(const ThumbnailData & data)188 bool ThumbnailFileUtils::DeleteBeginTimestampDir(const ThumbnailData &data)
189 {
190 string dirName = GetThumbnailDir(data);
191 CHECK_AND_RETURN_RET_LOG(!dirName.empty(), false, "Path:%{public}s is invalid",
192 DfxUtils::GetSafePath(data.path).c_str());
193 CHECK_AND_RETURN_RET(access(dirName.c_str(), F_OK) == 0, true);
194
195 int32_t err = nftw(dirName.c_str(), DeleteBeginTimestampDirCallback, OPEN_FDS, FTW_PHYS);
196 CHECK_AND_RETURN_RET_LOG(err == 0, false, "DeleteBeginTimestampDir failed, errno:%{public}d, "
197 "path: %{public}s, id: %{public}s", errno, DfxUtils::GetSafePath(dirName).c_str(), data.id.c_str());
198 return true;
199 }
200
CheckRemainSpaceMeetCondition(int32_t freeSizePercentLimit)201 bool ThumbnailFileUtils::CheckRemainSpaceMeetCondition(int32_t freeSizePercentLimit)
202 {
203 static int64_t totalSize = MediaFileUtils::GetTotalSize();
204 if (totalSize <= 0) {
205 totalSize = MediaFileUtils::GetTotalSize();
206 }
207 CHECK_AND_RETURN_RET_LOG(totalSize > 0, false, "Get total size failed, totalSize:%{public}" PRId64, totalSize);
208 int64_t freeSize = MediaFileUtils::GetFreeSize();
209 CHECK_AND_RETURN_RET_LOG(freeSize > 0, false, "Get free size failed, freeSize:%{public}" PRId64, freeSize);
210 int32_t freeSizePercent = static_cast<int32_t>(freeSize * 100 / totalSize);
211 CHECK_AND_RETURN_RET_LOG(freeSizePercent > freeSizePercentLimit, false,
212 "Check free size failed, totalSize:%{public}" PRId64 ", freeSize:%{public}" PRId64 ", "
213 "freeSizePercentLimit:%{public}d", totalSize, freeSize, freeSizePercentLimit);
214 return true;
215 }
216
GetKvStore(const ThumbnailType & type)217 std::shared_ptr<MediaLibraryKvStore> GetKvStore(const ThumbnailType &type)
218 {
219 if (type == ThumbnailType::MTH_ASTC) {
220 return MediaLibraryKvStoreManager::GetInstance()
221 .GetKvStore(KvStoreRoleType::OWNER, KvStoreValueType::MONTH_ASTC);
222 } else if (type == ThumbnailType::YEAR_ASTC) {
223 return MediaLibraryKvStoreManager::GetInstance()
224 .GetKvStore(KvStoreRoleType::OWNER, KvStoreValueType::YEAR_ASTC);
225 } else {
226 MEDIA_ERR_LOG("Invalid thumbnailType, type:%{public}d", type);
227 return nullptr;
228 }
229 }
230
DeleteAstcDataFromKvStore(const ThumbnailData & data,const ThumbnailType & type)231 bool ThumbnailFileUtils::DeleteAstcDataFromKvStore(const ThumbnailData &data, const ThumbnailType &type)
232 {
233 string key;
234 if (!MediaFileUtils::GenerateKvStoreKey(data.id, data.dateTaken, key)) {
235 MEDIA_ERR_LOG("GenerateKvStoreKey failed, id:%{public}s", data.id.c_str());
236 return false;
237 }
238
239 std::shared_ptr<MediaLibraryKvStore> kvStore = GetKvStore(type);
240 CHECK_AND_RETURN_RET_LOG(kvStore != nullptr, false, "KvStore is nullptr");
241 int status = kvStore->Delete(key);
242 return status == E_OK;
243 }
244
BatchDeleteAstcData(const ThumbnailDataBatch & dataBatch,const ThumbnailType & type)245 bool ThumbnailFileUtils::BatchDeleteAstcData(const ThumbnailDataBatch &dataBatch, const ThumbnailType &type)
246 {
247 size_t dataBatchSize = dataBatch.ids.size();
248 CHECK_AND_RETURN_RET_LOG(dataBatchSize == dataBatch.dateTakens.size(), false, "Failed to check dataBatch");
249 if (dataBatchSize == 0) {
250 return true;
251 }
252
253 vector<string> keys;
254 for (size_t i = 0; i < dataBatchSize; i++) {
255 string key;
256 if (!MediaFileUtils::GenerateKvStoreKey(dataBatch.ids[i], dataBatch.dateTakens[i], key)) {
257 MEDIA_ERR_LOG("GenerateKvStoreKey failed, id:%{public}s", dataBatch.ids[i].c_str());
258 continue;
259 }
260 keys.push_back(key);
261 }
262
263 std::shared_ptr<MediaLibraryKvStore> kvStore = GetKvStore(type);
264 CHECK_AND_RETURN_RET_LOG(kvStore != nullptr, false, "KvStore is nullptr");
265 constexpr int32_t ONE_BATCH_SIZE = 100;
266 bool batchDeleteSuccess = true;
267 size_t totalSize = keys.size();
268 for (size_t i = 0; i < totalSize; i += ONE_BATCH_SIZE) {
269 size_t endIndex = std::min(i + ONE_BATCH_SIZE, totalSize);
270 vector<string> batchKeys(keys.begin() + i, keys.begin() + endIndex);
271 int32_t status = kvStore->DeleteBatch(batchKeys);
272 if (status != E_OK) {
273 string startKey = batchKeys.front();
274 string endKey = batchKeys.back();
275 MEDIA_ERR_LOG("Failed to delete batch from %{public}s to %{public}s, status:%{public}d",
276 startKey.c_str(), endKey.c_str(), status);
277 batchDeleteSuccess = false;
278 }
279 }
280 return batchDeleteSuccess;
281 }
282
RemoveDirectoryAndFile(const std::string & path)283 bool ThumbnailFileUtils::RemoveDirectoryAndFile(const std::string &path)
284 {
285 CHECK_AND_RETURN_RET_LOG(!path.empty(), false, "Path is empty");
286 CHECK_AND_RETURN_RET(access(path.c_str(), F_OK) == 0, true);
287
288 std::error_code errCode;
289 std::uintmax_t num = std::filesystem::remove_all(path, errCode);
290 CHECK_AND_RETURN_RET_LOG(errCode.value() == E_OK, false,
291 "Remove path failed, errno:%{public}d, path:%{public}s, errCode:%{public}d",
292 errno, DfxUtils::GetSafePath(path).c_str(), errCode.value());
293 return true;
294 }
295
IsWifiConnected()296 bool ThumbnailFileUtils::IsWifiConnected()
297 {
298 bool isWifiConnected = false;
299 #ifdef HAS_WIFI_MANAGER_PART
300 auto wifiDevicePtr = Wifi::WifiDevice::GetInstance(WIFI_DEVICE_ABILITY_ID);
301 CHECK_AND_RETURN_RET_LOG(wifiDevicePtr != nullptr, false, "WifiDevicePtr is nullptr");
302 int32_t ret = wifiDevicePtr->IsConnected(isWifiConnected);
303 CHECK_AND_RETURN_RET_LOG(ret == Wifi::WIFI_OPT_SUCCESS, false, "Get Is Connnected Fail: %{public}d", ret);
304 #endif
305 return isWifiConnected;
306 }
307 } // namespace Media
308 } // namespace OHOS