1 /*
2 * Copyright (C) 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
16 #define MLOG_TAG "MediaLibraryCloudUploadChecker"
17
18 #include "cloud_upload_checker.h"
19
20 #include <sys/stat.h>
21
22 #include "media_file_utils.h"
23 #include "media_file_uri.h"
24 #include "medialibrary_unistore_manager.h"
25 #include "photo_album_column.h"
26 #include "thumbnail_const.h"
27 #include "media_column.h"
28 #include "preferences.h"
29 #include "preferences_helper.h"
30 #include "result_set_utils.h"
31 #include "thumbnail_const.h"
32 #include "medialibrary_object_utils.h"
33 #include "medialibrary_photo_operations.h"
34 #include "moving_photo_file_utils.h"
35 #include "medialibrary_subscriber.h"
36
37 namespace OHOS {
38 namespace Media {
39 using namespace std;
40 using namespace NativeRdb;
41
42 const std::int32_t BATCH_SIZE = 500;
43 const int32_t NO_ORIGIN_NO_LCD = 101;
44
45 const int SCANLINE_DEFAULT_VERSION = 0;
46 const int SCANLINE_CURRENT_VERSION = 1;
47
48 const std::string TASK_PROGRESS_XML = "/data/storage/el2/base/preferences/task_progress.xml";
49 const std::string NO_ORIGIN_PHOTO_NUMBER = "no_origin_photo_number";
50 const std::string NO_ORIGIN_BUT_LCD_PHOTO_NUMBER = "no_origin_but_lcd_photo_number";
51 const std::string SCANLINE_VERSION = "scanline_version";
52
53 std::mutex CloudUploadChecker::mutex_;
54
55 const std::string SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_COUNT = "SELECT"
56 " COUNT( * ) AS Count "
57 "FROM"
58 " Photos "
59 "WHERE"
60 " ( dirty = 100 OR dirty = 1 )"
61 " AND thumbnail_ready >= 3"
62 " AND lcd_visit_time >= 2"
63 " AND file_id > ?;";
64
65 const std::string SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_PHOTO = "SELECT"
66 " file_id,"
67 " data,"
68 " size,"
69 " subtype,"
70 " moving_photo_effect_mode "
71 "FROM"
72 " Photos "
73 "WHERE"
74 " ( dirty = 100 OR dirty = 1 )"
75 " AND thumbnail_ready >= 3"
76 " AND lcd_visit_time >= 2"
77 " AND file_id > ?"
78 " LIMIT ?;";
79
80 static const std::string SQL_REPAIR_DETAIL_TIME =
81 " UPDATE Photos "
82 " SET detail_time = ("
83 " CASE"
84 " WHEN date_taken / 10000000000 == 0 THEN strftime( '%Y:%m:%d %H:%M:%S', date_taken, 'unixepoch', 'localtime' )"
85 " ELSE strftime( '%Y:%m:%d %H:%M:%S', date_taken / 1000, 'unixepoch', 'localtime' )"
86 " END ) "
87 " WHERE"
88 " CASE"
89 " WHEN date_taken / 10000000000 == 0 THEN strftime( '%Y:%m:%d %H:%M:%S', date_taken, 'unixepoch', 'localtime' )"
90 " <> detail_time"
91 " ELSE strftime( '%Y:%m:%d %H:%M:%S', date_taken / 1000, 'unixepoch', 'localtime' ) <> detail_time "
92 " END ;";
93
HandleNoOriginPhoto()94 void CloudUploadChecker::HandleNoOriginPhoto()
95 {
96 MEDIA_INFO_LOG("start handle no origin photo!");
97 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
98 int32_t errCode = E_OK;
99 shared_ptr<NativePreferences::Preferences> prefs =
100 NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
101 CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
102 int32_t curFileId = prefs->GetInt(NO_ORIGIN_PHOTO_NUMBER, 0);
103 MEDIA_INFO_LOG("start file id: %{public}d", curFileId);
104 while (MedialibrarySubscriber::IsCurrentStatusOn() && QueryLcdPhotoCount(curFileId) > 0) {
105 MEDIA_INFO_LOG("handle origin photo curFileId: %{public}d", curFileId);
106 std::vector<CheckedPhotoInfo> photoInfos = QueryPhotoInfo(curFileId);
107 HandlePhotoInfos(photoInfos, curFileId);
108 prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, curFileId);
109 prefs->FlushSync();
110 }
111 MEDIA_INFO_LOG(
112 "end handle no origin photo! cost: %{public}" PRId64, MediaFileUtils::UTCTimeMilliSeconds() - startTime);
113 return;
114 }
115
IsMovingPhoto(int32_t subtype,int32_t movingPhotoEffectMode)116 inline bool IsMovingPhoto(int32_t subtype, int32_t movingPhotoEffectMode)
117 {
118 return subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
119 movingPhotoEffectMode == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY);
120 }
121
GetPhotoRealSize(const bool isMovingPhoto,const std::string & path,size_t & size)122 int32_t GetPhotoRealSize(const bool isMovingPhoto, const std::string &path, size_t &size)
123 {
124 if (isMovingPhoto) {
125 size = MovingPhotoFileUtils::GetMovingPhotoSize(path);
126 return E_OK;
127 }
128 struct stat st {};
129 CHECK_AND_RETURN_RET_LOG(stat(path.c_str(), &st) == 0,
130 E_ERR,
131 "stat syscall failed, path=%{public}s, errno=%{public}d",
132 path.c_str(),
133 errno);
134
135 size = st.st_size;
136 return E_OK;
137 }
138
UpdateFileSize(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto)139 void CloudUploadChecker::UpdateFileSize(const CheckedPhotoInfo &photoInfo, bool isMovingPhoto)
140 {
141 size_t size = 0;
142 CHECK_AND_RETURN_LOG(GetPhotoRealSize(isMovingPhoto, photoInfo.path, size) == E_OK,
143 "get photo real size failed, path=%{public}s",
144 photoInfo.path.c_str());
145
146 CHECK_AND_RETURN_INFO_LOG(photoInfo.size != size,
147 "no need to update db file size, file_id=%{public}d, size=%{public}zu",
148 photoInfo.fileId,
149 size);
150
151 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
152 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is nullptr");
153
154 RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
155 predicates.EqualTo(MediaColumn::MEDIA_ID, photoInfo.fileId);
156
157 ValuesBucket values;
158 values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast<int32_t>(DirtyTypes::TYPE_NEW));
159 values.PutLong(MediaColumn::MEDIA_SIZE, static_cast<int64_t>(size));
160
161 int32_t updateCount = 0;
162 int32_t err = rdbStore->Update(updateCount, values, predicates);
163
164 CHECK_AND_RETURN_LOG(err == NativeRdb::E_OK,
165 "update db file size failed, file_id=%{public}d, err=%{public}d",
166 photoInfo.fileId,
167 err);
168
169 MEDIA_INFO_LOG("update db file size succeed, file_id=%{public}d, old size=%{public}zu, new size=%{public}zu",
170 photoInfo.fileId,
171 photoInfo.size,
172 size);
173 }
174
HandleMissingFile(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto,std::vector<std::string> & noLcdList)175 void CloudUploadChecker::HandleMissingFile(
176 const CheckedPhotoInfo &photoInfo, bool isMovingPhoto, std::vector<std::string> &noLcdList)
177 {
178 std::string lcdPath = GetThumbnailPath(photoInfo.path, THUMBNAIL_LCD_SUFFIX);
179 if (!MediaFileUtils::IsFileExists(lcdPath)) {
180 MEDIA_WARN_LOG("lcd path does not exist, file_id: %{public}d", photoInfo.fileId);
181 noLcdList.push_back(std::to_string(photoInfo.fileId));
182 return;
183 }
184
185 CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(lcdPath, photoInfo.path),
186 "copy lcd to origin photo failed, file_id: %{public}d",
187 photoInfo.fileId);
188
189 MEDIA_INFO_LOG("copy lcd to origin photo succeed, file_id: %{public}d", photoInfo.fileId);
190 UpdateFileSize(photoInfo, isMovingPhoto);
191 }
192
HandlePhotoInfos(const std::vector<CheckedPhotoInfo> & photoInfos,int32_t & curFileId)193 void CloudUploadChecker::HandlePhotoInfos(const std::vector<CheckedPhotoInfo> &photoInfos, int32_t &curFileId)
194 {
195 std::vector<std::string> noLcdList;
196
197 for (const CheckedPhotoInfo &photoInfo : photoInfos) {
198 if (!MedialibrarySubscriber::IsCurrentStatusOn()) {
199 MEDIA_INFO_LOG("current status is off, break");
200 break;
201 }
202 curFileId = photoInfo.fileId;
203 bool isMovingPhoto = IsMovingPhoto(photoInfo.subtype, photoInfo.movingPhotoEffectMode);
204 if (MediaFileUtils::IsFileExists(photoInfo.path)) {
205 UpdateFileSize(photoInfo, isMovingPhoto);
206 continue;
207 }
208 HandleMissingFile(photoInfo, isMovingPhoto, noLcdList);
209 }
210 UpdateDirty(noLcdList, NO_ORIGIN_NO_LCD);
211 }
212
UpdateDirty(const std::vector<std::string> & idList,int32_t dirtyType)213 void CloudUploadChecker::UpdateDirty(const std::vector<std::string> &idList, int32_t dirtyType)
214 {
215 CHECK_AND_RETURN_INFO_LOG(!idList.empty(), "idList is empty");
216 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
217 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "Failed to get rdbstore!");
218 RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
219 predicates.In(MediaColumn::MEDIA_ID, idList);
220 ValuesBucket values;
221 values.PutInt(PhotoColumn::PHOTO_DIRTY, dirtyType);
222 int32_t updateCount = 0;
223 int32_t err = rdbStore->Update(updateCount, values, predicates);
224 MEDIA_INFO_LOG("dirty: %{public}d, idList size: %{public}d, update size: %{public}d, err: %{public}d",
225 dirtyType,
226 static_cast<int32_t>(idList.size()),
227 updateCount,
228 err);
229 }
230
QueryPhotoInfo(int32_t startFileId)231 std::vector<CheckedPhotoInfo> CloudUploadChecker::QueryPhotoInfo(int32_t startFileId)
232 {
233 std::vector<CheckedPhotoInfo> photoInfos;
234 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
235 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, photoInfos, "Failed to get rdbstore!");
236
237 const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId, BATCH_SIZE};
238 auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_PHOTO, bindArgs);
239 bool cond = resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK;
240 CHECK_AND_RETURN_RET_LOG(cond, photoInfos, "resultSet is null or count is 0");
241
242 do {
243 std::string path =
244 get<std::string>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
245 if (path.empty()) {
246 MEDIA_ERR_LOG("Failed to get data path");
247 continue;
248 }
249 CheckedPhotoInfo photoInfo;
250 photoInfo.fileId = get<int32_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_ID, resultSet, TYPE_INT32));
251 photoInfo.path = path;
252 photoInfo.size =
253 get<std::int64_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_SIZE, resultSet, TYPE_INT64));
254 photoInfo.subtype =
255 get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
256 photoInfo.movingPhotoEffectMode = get<int32_t>(
257 ResultSetUtils::GetValFromColumn(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet, TYPE_INT32));
258 photoInfos.push_back(photoInfo);
259 } while (MedialibrarySubscriber::IsCurrentStatusOn() && resultSet->GoToNextRow() == NativeRdb::E_OK);
260 return photoInfos;
261 }
262
QueryLcdPhotoCount(int32_t startFileId)263 int32_t CloudUploadChecker::QueryLcdPhotoCount(int32_t startFileId)
264 {
265 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
266 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "Failed to get rdbStore.");
267 const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId};
268 auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_COUNT, bindArgs);
269 CHECK_AND_RETURN_RET_LOG(
270 resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK, 0, "resultSet is null or count is 0");
271 return get<int32_t>(ResultSetUtils::GetValFromColumn("Count", resultSet, TYPE_INT32));
272 }
273
RepairNoOriginPhoto()274 void CloudUploadChecker::RepairNoOriginPhoto()
275 {
276 std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
277 if (!lock.try_lock()) {
278 MEDIA_WARN_LOG("Repairing no origin photos has started, skipping this operation");
279 return;
280 }
281 MEDIA_INFO_LOG("start repair no origin photo!");
282 int32_t errCode = E_OK;
283 shared_ptr<NativePreferences::Preferences> prefs =
284 NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
285 CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
286 int scanlineVersion = prefs->GetInt(SCANLINE_VERSION, SCANLINE_DEFAULT_VERSION);
287 MEDIA_INFO_LOG("scanline version: %{public}d", scanlineVersion);
288 if (scanlineVersion < SCANLINE_CURRENT_VERSION) {
289 prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, 0);
290 prefs->PutInt(SCANLINE_VERSION, SCANLINE_CURRENT_VERSION);
291 prefs->FlushSync();
292 }
293 HandleNoOriginPhoto();
294 MEDIA_INFO_LOG("end repair no origin photo!");
295 }
296
RepairNoDetailTime()297 void CloudUploadChecker::RepairNoDetailTime()
298 {
299 MEDIA_INFO_LOG("start repair detail time");
300 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
301 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "Failed to get rdbstore!");
302 int32_t err = rdbStore->ExecuteSql(SQL_REPAIR_DETAIL_TIME);
303 CHECK_AND_RETURN_LOG(err == NativeRdb::E_OK, "Failed to RepairNoDetailTime: %{public}d", err);
304 MEDIA_INFO_LOG("end repair detail time!");
305 }
306 } // namespace Media
307 } // namespace OHOS