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
HandleNoOriginPhoto()80 void CloudUploadChecker::HandleNoOriginPhoto()
81 {
82 MEDIA_INFO_LOG("start handle no origin photo!");
83 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
84 int32_t errCode = E_OK;
85 shared_ptr<NativePreferences::Preferences> prefs =
86 NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
87 CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
88 int32_t curFileId = prefs->GetInt(NO_ORIGIN_PHOTO_NUMBER, 0);
89 MEDIA_INFO_LOG("start file id: %{public}d", curFileId);
90 while (MedialibrarySubscriber::IsCurrentStatusOn() && QueryLcdPhotoCount(curFileId) > 0) {
91 MEDIA_INFO_LOG("handle origin photo curFileId: %{public}d", curFileId);
92 std::vector<CheckedPhotoInfo> photoInfos = QueryPhotoInfo(curFileId);
93 HandlePhotoInfos(photoInfos, curFileId);
94 prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, curFileId);
95 prefs->FlushSync();
96 }
97 MEDIA_INFO_LOG(
98 "end handle no origin photo! cost: %{public}" PRId64, MediaFileUtils::UTCTimeMilliSeconds() - startTime);
99 return;
100 }
101
IsMovingPhoto(int32_t subtype,int32_t movingPhotoEffectMode)102 inline bool IsMovingPhoto(int32_t subtype, int32_t movingPhotoEffectMode)
103 {
104 return subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO) ||
105 movingPhotoEffectMode == static_cast<int32_t>(MovingPhotoEffectMode::IMAGE_ONLY);
106 }
107
GetPhotoRealSize(const bool isMovingPhoto,const std::string & path,int64_t & size)108 int32_t GetPhotoRealSize(const bool isMovingPhoto, const std::string &path, int64_t &size)
109 {
110 if (isMovingPhoto) {
111 size = static_cast<int64_t>(MovingPhotoFileUtils::GetMovingPhotoSize(path));
112 return E_OK;
113 }
114 struct stat st {};
115 CHECK_AND_RETURN_RET_LOG(stat(path.c_str(), &st) == 0,
116 E_ERR,
117 "stat syscall failed, path=%{public}s, errno=%{public}d",
118 path.c_str(),
119 errno);
120
121 size = static_cast<int64_t>(st.st_size);
122 return E_OK;
123 }
124
UpdateFileSize(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto)125 void CloudUploadChecker::UpdateFileSize(const CheckedPhotoInfo &photoInfo, bool isMovingPhoto)
126 {
127 int64_t size = 0;
128 CHECK_AND_RETURN_LOG(GetPhotoRealSize(isMovingPhoto, photoInfo.path, size) == E_OK,
129 "get photo real size failed, path=%{public}s",
130 photoInfo.path.c_str());
131
132 CHECK_AND_RETURN_INFO_LOG(photoInfo.size != size,
133 "no need to update db file size, file_id=%{public}d, size=%{public}" PRId64,
134 photoInfo.fileId,
135 size);
136
137 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
138 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is nullptr");
139
140 RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
141 predicates.EqualTo(MediaColumn::MEDIA_ID, photoInfo.fileId);
142
143 ValuesBucket values;
144 values.PutInt(PhotoColumn::PHOTO_DIRTY, static_cast<int32_t>(DirtyTypes::TYPE_NEW));
145 values.PutLong(MediaColumn::MEDIA_SIZE, size);
146
147 int32_t updateCount = 0;
148 int32_t err = rdbStore->Update(updateCount, values, predicates);
149
150 CHECK_AND_RETURN_LOG(err == NativeRdb::E_OK,
151 "update db file size failed, file_id=%{public}d, err=%{public}d",
152 photoInfo.fileId,
153 err);
154
155 MEDIA_INFO_LOG("update db file size succeed, file_id=%{public}d, old size=%{public}" PRId64
156 ", new size=%{public}" PRId64,
157 photoInfo.fileId,
158 photoInfo.size,
159 size);
160 }
161
HandleMissingFile(const CheckedPhotoInfo & photoInfo,bool isMovingPhoto,std::vector<std::string> & noLcdList)162 void CloudUploadChecker::HandleMissingFile(
163 const CheckedPhotoInfo &photoInfo, bool isMovingPhoto, std::vector<std::string> &noLcdList)
164 {
165 std::string lcdPath = GetThumbnailPath(photoInfo.path, THUMBNAIL_LCD_SUFFIX);
166 if (!MediaFileUtils::IsFileExists(lcdPath)) {
167 MEDIA_WARN_LOG("lcd path does not exist, file_id: %{public}d", photoInfo.fileId);
168 noLcdList.push_back(std::to_string(photoInfo.fileId));
169 return;
170 }
171
172 CHECK_AND_RETURN_LOG(MediaFileUtils::CopyFileUtil(lcdPath, photoInfo.path),
173 "copy lcd to origin photo failed, file_id: %{public}d",
174 photoInfo.fileId);
175
176 MEDIA_INFO_LOG("copy lcd to origin photo succeed, file_id: %{public}d", photoInfo.fileId);
177 UpdateFileSize(photoInfo, isMovingPhoto);
178 }
179
HandlePhotoInfos(const std::vector<CheckedPhotoInfo> & photoInfos,int32_t & curFileId)180 void CloudUploadChecker::HandlePhotoInfos(const std::vector<CheckedPhotoInfo> &photoInfos, int32_t &curFileId)
181 {
182 std::vector<std::string> noLcdList;
183
184 for (const CheckedPhotoInfo &photoInfo : photoInfos) {
185 CHECK_AND_BREAK_INFO_LOG(MedialibrarySubscriber::IsCurrentStatusOn(), "current status is off, break");
186 curFileId = photoInfo.fileId;
187 bool isMovingPhoto = IsMovingPhoto(photoInfo.subtype, photoInfo.movingPhotoEffectMode);
188 if (MediaFileUtils::IsFileExists(photoInfo.path)) {
189 UpdateFileSize(photoInfo, isMovingPhoto);
190 continue;
191 }
192 HandleMissingFile(photoInfo, isMovingPhoto, noLcdList);
193 }
194 UpdateDirty(noLcdList, NO_ORIGIN_NO_LCD);
195 }
196
UpdateDirty(const std::vector<std::string> & idList,int32_t dirtyType)197 void CloudUploadChecker::UpdateDirty(const std::vector<std::string> &idList, int32_t dirtyType)
198 {
199 CHECK_AND_RETURN_INFO_LOG(!idList.empty(), "idList is empty");
200 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
201 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "Failed to get rdbstore!");
202 RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
203 predicates.In(MediaColumn::MEDIA_ID, idList);
204 ValuesBucket values;
205 values.PutInt(PhotoColumn::PHOTO_DIRTY, dirtyType);
206 int32_t updateCount = 0;
207 int32_t err = rdbStore->Update(updateCount, values, predicates);
208 MEDIA_INFO_LOG("dirty: %{public}d, idList size: %{public}zu, update size: %{public}d, err: %{public}d",
209 dirtyType,
210 idList.size(),
211 updateCount,
212 err);
213 }
214
QueryPhotoInfo(int32_t startFileId)215 std::vector<CheckedPhotoInfo> CloudUploadChecker::QueryPhotoInfo(int32_t startFileId)
216 {
217 std::vector<CheckedPhotoInfo> photoInfos;
218 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
219 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, photoInfos, "Failed to get rdbstore!");
220
221 const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId, BATCH_SIZE};
222 auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_PHOTO, bindArgs);
223 bool cond = resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK;
224 CHECK_AND_RETURN_RET_LOG(cond, photoInfos, "resultSet is null or count is 0");
225
226 do {
227 std::string path =
228 get<std::string>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_FILE_PATH, resultSet, TYPE_STRING));
229 CHECK_AND_CONTINUE_ERR_LOG(!path.empty(), "Failed to get data path");
230 CheckedPhotoInfo photoInfo;
231 photoInfo.fileId = get<int32_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_ID, resultSet, TYPE_INT32));
232 photoInfo.path = path;
233 photoInfo.size = get<int64_t>(ResultSetUtils::GetValFromColumn(MediaColumn::MEDIA_SIZE, resultSet, TYPE_INT64));
234 photoInfo.subtype =
235 get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoColumn::PHOTO_SUBTYPE, resultSet, TYPE_INT32));
236 photoInfo.movingPhotoEffectMode = get<int32_t>(
237 ResultSetUtils::GetValFromColumn(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet, TYPE_INT32));
238 photoInfos.push_back(photoInfo);
239 } while (MedialibrarySubscriber::IsCurrentStatusOn() && resultSet->GoToNextRow() == NativeRdb::E_OK);
240 return photoInfos;
241 }
242
QueryLcdPhotoCount(int32_t startFileId)243 int32_t CloudUploadChecker::QueryLcdPhotoCount(int32_t startFileId)
244 {
245 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
246 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, E_HAS_DB_ERROR, "Failed to get rdbStore.");
247 const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId};
248 auto resultSet = rdbStore->QuerySql(SQL_PHOTOS_TABLE_QUERY_NO_ORIGIN_BUT_LCD_COUNT, bindArgs);
249 CHECK_AND_RETURN_RET_LOG(
250 resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK, 0, "resultSet is null or count is 0");
251 return get<int32_t>(ResultSetUtils::GetValFromColumn("Count", resultSet, TYPE_INT32));
252 }
253
RepairNoOriginPhoto()254 void CloudUploadChecker::RepairNoOriginPhoto()
255 {
256 std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
257 CHECK_AND_RETURN_WARN_LOG(lock.try_lock(), "Repairing no origin photos has started, skipping this operation");
258 MEDIA_INFO_LOG("start repair no origin photo!");
259 int32_t errCode = E_OK;
260 shared_ptr<NativePreferences::Preferences> prefs =
261 NativePreferences::PreferencesHelper::GetPreferences(TASK_PROGRESS_XML, errCode);
262 CHECK_AND_RETURN_LOG(prefs, "get preferences error: %{public}d", errCode);
263 int scanlineVersion = prefs->GetInt(SCANLINE_VERSION, SCANLINE_DEFAULT_VERSION);
264 MEDIA_INFO_LOG("scanline version: %{public}d", scanlineVersion);
265 if (scanlineVersion < SCANLINE_CURRENT_VERSION) {
266 prefs->PutInt(NO_ORIGIN_PHOTO_NUMBER, 0);
267 prefs->PutInt(SCANLINE_VERSION, SCANLINE_CURRENT_VERSION);
268 prefs->FlushSync();
269 }
270 HandleNoOriginPhoto();
271 MEDIA_INFO_LOG("end repair no origin photo!");
272 }
273 } // namespace Media
274 } // namespace OHOS