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 "PhotoDayMonthYearOperation"
17
18 #include "photo_day_month_year_operation.h"
19
20 #include <charconv>
21
22 #include "media_exif.h"
23 #include "media_file_utils.h"
24 #include "media_log.h"
25 #include "medialibrary_unistore_manager.h"
26 #include "media_column.h"
27 #include "medialibrary_subscriber.h"
28 #include "photo_file_utils.h"
29 #include "preferences_helper.h"
30 #include "result_set_utils.h"
31 #include "thumbnail_service.h"
32
33 namespace OHOS {
34 namespace Media {
35 using namespace std;
36 using namespace NativeRdb;
37
38 const std::string REPAIR_DATE_TIME_XML = "/data/storage/el2/base/preferences/repair_date_time.xml";
39 const std::string CURRENT_FILE_ID = "CURRENT_FILE_ID";
40 const std::string ZEROTIMESTRING = "0000:00:00 00:00:00";
41 const std::int32_t BATCH_SIZE = 500;
42 const int32_t UPDATE_BATCH_SIZE = 200;
43
44 const int32_t HOURS_TO_SECOND = 3600;
45 const int32_t MINUTES_TO_SECOND = 60;
46 const size_t OFFSET_STR_SIZE = 6; // ±HH:MM
47 const size_t COLON_POSITION = 3;
48
49 std::mutex PhotoDayMonthYearOperation::mutex_;
50
51 const std::string QUERY_NEED_UPDATE_FILE_IDS = ""
52 "SELECT file_id FROM Photos "
53 "WHERE"
54 " date_added = 0"
55 " OR date_taken = 0";
56
57 const std::string UPDATE_DAY_MONTH_YEAR = ""
58 "UPDATE Photos "
59 "SET date_added ="
60 " CASE"
61 " WHEN date_added <> 0 THEN"
62 " date_added "
63 " WHEN date_taken <> 0 THEN"
64 " date_taken "
65 " WHEN date_modified <> 0 THEN"
66 " date_modified ELSE strftime( '%s', 'now' ) "
67 " END, "
68 "date_taken ="
69 " CASE"
70 " WHEN date_taken <> 0 THEN"
71 " date_taken "
72 " WHEN date_added <> 0 THEN"
73 " date_added "
74 " WHEN date_modified <> 0 THEN"
75 " date_modified ELSE strftime( '%s', 'now' ) "
76 " END, "
77 "date_day ="
78 " CASE"
79 " WHEN date_taken <> 0 THEN"
80 " strftime( '%Y%m%d', date_taken / 1000, 'unixepoch', 'localtime' ) "
81 " WHEN date_added <> 0 THEN"
82 " strftime( '%Y%m%d', date_added / 1000, 'unixepoch', 'localtime' ) "
83 " WHEN date_modified <> 0 THEN"
84 " strftime( '%Y%m%d', date_modified / 1000, 'unixepoch', 'localtime' ) "
85 " ELSE strftime( '%Y%m%d', strftime( '%s', 'now' ) / 1000, 'unixepoch', 'localtime' ) "
86 " END, "
87 "date_month ="
88 " CASE"
89 " WHEN date_taken <> 0 THEN"
90 " strftime( '%Y%m', date_taken / 1000, 'unixepoch', 'localtime' ) "
91 " WHEN date_added <> 0 THEN"
92 " strftime( '%Y%m', date_added / 1000, 'unixepoch', 'localtime' ) "
93 " WHEN date_modified <> 0 THEN"
94 " strftime( '%Y%m', date_modified / 1000, 'unixepoch', 'localtime' ) "
95 " ELSE strftime( '%Y%m', strftime( '%s', 'now' ) / 1000, 'unixepoch', 'localtime' ) "
96 " END, "
97 "date_year ="
98 " CASE"
99 " WHEN date_taken <> 0 THEN"
100 " strftime( '%Y', date_taken / 1000, 'unixepoch', 'localtime' ) "
101 " WHEN date_added <> 0 THEN"
102 " strftime( '%Y', date_added / 1000, 'unixepoch', 'localtime' ) "
103 " WHEN date_modified <> 0 THEN"
104 " strftime( '%Y', date_modified / 1000, 'unixepoch', 'localtime' ) "
105 " ELSE strftime( '%Y', strftime( '%s', 'now' ) / 1000, 'unixepoch', 'localtime' ) "
106 " END, "
107 "detail_time ="
108 " CASE"
109 " WHEN date_taken <> 0 THEN"
110 " strftime( '%Y:%m:%d %H:%M:%S', date_taken / 1000, 'unixepoch', 'localtime' ) "
111 " WHEN date_added <> 0 THEN"
112 " strftime( '%Y:%m:%d %H:%M:%S', date_added / 1000, 'unixepoch', 'localtime' ) "
113 " WHEN date_modified <> 0 THEN"
114 " strftime( '%Y:%m:%d %H:%M:%S', date_modified / 1000, 'unixepoch', 'localtime' ) "
115 " ELSE strftime( '%Y:%m:%d %H:%M:%S', strftime( '%s', 'now' ) / 1000, 'unixepoch', 'localtime' ) "
116 " END, "
117 "dirty ="
118 " CASE"
119 " WHEN dirty = 0 THEN"
120 " 2 ELSE dirty "
121 " END "
122 "WHERE"
123 " file_id IN ( ";
124 // LCOV_EXCL_START
UpdatePhotosDate(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)125 int32_t PhotoDayMonthYearOperation::UpdatePhotosDate(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)
126 {
127 MEDIA_INFO_LOG("update photos date start");
128 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
129 bool cond = (rdbStore == nullptr || !rdbStore->CheckRdbStore());
130 CHECK_AND_RETURN_RET_LOG(!cond, NativeRdb::E_ERROR,
131 "Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
132 auto resultSet = rdbStore->QueryByStep(QUERY_NEED_UPDATE_FILE_IDS);
133 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, NativeRdb::E_ERROR, "query photos by step failed");
134
135 std::vector<std::string> needUpdateFileIds;
136 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
137 needUpdateFileIds.push_back(GetStringVal(PhotoColumn::MEDIA_ID, resultSet));
138 }
139 resultSet->Close();
140 auto needChangedSize = needUpdateFileIds.size();
141 CHECK_AND_RETURN_RET(needChangedSize > 0, NativeRdb::E_OK);
142
143 int32_t ret = NativeRdb::E_OK;
144 int64_t totalChanged = 0;
145 for (size_t start = 0; start < needChangedSize; start += UPDATE_BATCH_SIZE) {
146 size_t end = std::min(start + UPDATE_BATCH_SIZE, needChangedSize);
147 std::stringstream updateSql;
148 updateSql << UPDATE_DAY_MONTH_YEAR;
149 for (size_t i = start; i < end; ++i) {
150 if (i != start) {
151 updateSql << ", ";
152 }
153 updateSql << needUpdateFileIds[i];
154 }
155 updateSql << " );";
156 int64_t batchStart = MediaFileUtils::UTCTimeMilliSeconds();
157 int64_t changedRowCount = 0;
158 auto errCode = rdbStore->ExecuteForChangedRowCount(changedRowCount, updateSql.str());
159 if (errCode != NativeRdb::E_OK) {
160 ret = errCode;
161 MEDIA_ERR_LOG("update photos date failed, errCode: %{public}d, batchStart: %{public}" PRId64
162 ", cost: %{public}" PRId64,
163 errCode, batchStart, MediaFileUtils::UTCTimeMilliSeconds() - batchStart);
164 } else {
165 totalChanged += changedRowCount;
166 MEDIA_DEBUG_LOG("update photos date, batchStart: %{public}" PRId64 ", cost: %{public}" PRId64
167 ", changedRowCount: %{public}" PRId64,
168 batchStart, MediaFileUtils::UTCTimeMilliSeconds() - batchStart, changedRowCount);
169 }
170 }
171
172 MEDIA_INFO_LOG("update photos date end, startTime: %{public}" PRId64 ", cost: %{public}" PRId64
173 ", needChangedSize: %{public}zu, totalChanged: %{public}" PRId64,
174 startTime, MediaFileUtils::UTCTimeMilliSeconds() - startTime, needChangedSize, totalChanged);
175 return ret;
176 }
177
UpdatePhotosDateAndIdx(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)178 int32_t PhotoDayMonthYearOperation::UpdatePhotosDateAndIdx(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)
179 {
180 MEDIA_INFO_LOG("update phots date start");
181 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
182 bool cond = (rdbStore == nullptr || !rdbStore->CheckRdbStore());
183 CHECK_AND_RETURN_RET_LOG(!cond, NativeRdb::E_ERROR,
184 "Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
185
186 auto ret = UpdatePhotosDate(rdbStore);
187 CHECK_AND_RETURN_RET_LOG(ret == NativeRdb::E_OK, ret, "update day month year failed, ret=%{public}d", ret);
188 MEDIA_INFO_LOG("update phots date end, startTime: %{public}" PRId64 ", cost: %{public}" PRId64, startTime,
189 (MediaFileUtils::UTCTimeMilliSeconds() - startTime));
190 return NativeRdb::E_OK;
191 }
192
UpdatePhotosDate(NativeRdb::RdbStore & rdbStore)193 int32_t PhotoDayMonthYearOperation::UpdatePhotosDate(NativeRdb::RdbStore &rdbStore)
194 {
195 MEDIA_INFO_LOG("update photos date start");
196 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
197
198 auto resultSet = rdbStore.QueryByStep(QUERY_NEED_UPDATE_FILE_IDS);
199 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr, NativeRdb::E_ERROR, "query photos by step failed");
200
201 std::vector<std::string> needUpdateFileIds;
202 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
203 needUpdateFileIds.push_back(GetStringVal(PhotoColumn::MEDIA_ID, resultSet));
204 }
205 resultSet->Close();
206 auto needChangedSize = needUpdateFileIds.size();
207 CHECK_AND_RETURN_RET(needChangedSize > 0, NativeRdb::E_OK);
208
209 std::stringstream updateSql;
210 updateSql << UPDATE_DAY_MONTH_YEAR;
211 for (size_t i = 0; i < needChangedSize; ++i) {
212 if (i != 0) {
213 updateSql << ", ";
214 }
215 updateSql << needUpdateFileIds[i];
216 }
217 updateSql << " );";
218 int64_t changedRowCount = 0;
219 auto errCode = rdbStore.ExecuteForChangedRowCount(changedRowCount, updateSql.str());
220 CHECK_AND_RETURN_RET_LOG(errCode == NativeRdb::E_OK, errCode,
221 "update photos date failed, errCode: %{public}d, startTime: %{public}" PRId64
222 ", cost: %{public}" PRId64 ", needChangedSize: %{public}zu",
223 errCode, startTime, MediaFileUtils::UTCTimeMilliSeconds() - startTime, needChangedSize);
224
225 MEDIA_INFO_LOG("update photos date end, startTime: %{public}" PRId64 ", cost: %{public}" PRId64
226 ", needChangedSize: %{public}zu, changedRowCount: %{public}" PRId64,
227 startTime, MediaFileUtils::UTCTimeMilliSeconds() - startTime, needChangedSize, changedRowCount);
228 return NativeRdb::E_OK;
229 }
230
UpdatePhotosDateIdx(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)231 int32_t PhotoDayMonthYearOperation::UpdatePhotosDateIdx(const std::shared_ptr<MediaLibraryRdbStore> rdbStore)
232 {
233 MEDIA_INFO_LOG("update photos date idx start");
234 int64_t startTime = MediaFileUtils::UTCTimeMilliSeconds();
235 bool cond = (rdbStore == nullptr || !rdbStore->CheckRdbStore());
236 CHECK_AND_RETURN_RET_LOG(!cond, NativeRdb::E_ERROR,
237 "Pointer rdbStore_ is nullptr. Maybe it didn't init successfully.");
238
239 auto ret = rdbStore->ExecuteSql(PhotoColumn::DROP_SCHPT_DAY_INDEX);
240 CHECK_AND_PRINT_LOG(ret == NativeRdb::E_OK, "drop idx date_day failed, ret=%{public}d", ret);
241
242 ret = rdbStore->ExecuteSql(PhotoColumn::CREATE_SCHPT_DAY_INDEX);
243 CHECK_AND_PRINT_LOG(ret == NativeRdb::E_OK, "create idx date_day failed, ret=%{public}d", ret);
244
245 ret = rdbStore->ExecuteSql(PhotoColumn::DROP_SCHPT_MONTH_COUNT_READY_INDEX);
246 CHECK_AND_PRINT_LOG(ret == NativeRdb::E_OK, "drop idx date_month failed, ret=%{public}d", ret);
247
248 ret = rdbStore->ExecuteSql(PhotoColumn::CREATE_SCHPT_MONTH_COUNT_READY_INDEX);
249 CHECK_AND_PRINT_LOG(ret == NativeRdb::E_OK, "create idx date_month failed, ret=%{public}d", ret);
250
251 ret = rdbStore->ExecuteSql(PhotoColumn::DROP_SCHPT_YEAR_COUNT_READY_INDEX);
252 CHECK_AND_PRINT_LOG(ret == NativeRdb::E_OK, "drop idx date_year failed, ret=%{public}d", ret);
253
254 ret = rdbStore->ExecuteSql(PhotoColumn::CREATE_SCHPT_YEAR_COUNT_READY_INDEX);
255 CHECK_AND_RETURN_RET_LOG(ret == NativeRdb::E_OK, ret, "create idx date_year failed, ret=%{public}d", ret);
256
257 MEDIA_INFO_LOG("update photos date idx end, startTime: %{public}" PRId64 ", cost: %{public}" PRId64, startTime,
258 (MediaFileUtils::UTCTimeMilliSeconds() - startTime));
259 return NativeRdb::E_OK;
260 }
261 // LCOV_EXCL_STOP
262
QueryDateAnomalyPhotos(const int32_t startFileId)263 std::vector<DateAnomalyPhoto> PhotoDayMonthYearOperation::QueryDateAnomalyPhotos(const int32_t startFileId)
264 {
265 std::vector<DateAnomalyPhoto> photos;
266 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
267 CHECK_AND_RETURN_RET_LOG(rdbStore != nullptr, photos, "Failed to get rdbstore!");
268
269 const std::vector<NativeRdb::ValueObject> bindArgs = {startFileId, BATCH_SIZE};
270 std::string sql = "SELECT"
271 " file_id,"
272 " date_taken,"
273 " date_modified,"
274 " date_day,"
275 " detail_time,"
276 " all_exif "
277 "FROM"
278 " Photos "
279 "WHERE"
280 " ("
281 " date_taken <= 0 "
282 " OR all_exif != '' "
283 " OR date_day IS NULL "
284 " OR date_day = '' "
285 " OR detail_time IS NULL "
286 " OR detail_time = '' "
287 " OR date_day != REPLACE ( SUBSTR( detail_time, 1, 10 ), ':', '' ) "
288 " ) "
289 " AND file_id > ? "
290 "ORDER BY"
291 " file_id ASC "
292 " LIMIT ?;";
293 auto resultSet = rdbStore->QuerySql(sql, bindArgs);
294 bool cond = resultSet != nullptr && resultSet->GoToFirstRow() == NativeRdb::E_OK;
295 CHECK_AND_RETURN_RET_LOG(cond, photos, "resultSet is null or count is 0");
296
297 do {
298 DateAnomalyPhoto photo;
299 photo.fileId = GetInt32Val(MediaColumn::MEDIA_ID, resultSet);
300 photo.dateTaken = GetInt64Val(MediaColumn::MEDIA_DATE_TAKEN, resultSet);
301 photo.dateModified = GetInt64Val(PhotoColumn::MEDIA_DATE_MODIFIED, resultSet);
302 photo.dateDay = GetStringVal(PhotoColumn::PHOTO_DATE_DAY, resultSet);
303 photo.detailTime = GetStringVal(PhotoColumn::PHOTO_DETAIL_TIME, resultSet);
304 photo.exif = GetStringVal(PhotoColumn::PHOTO_ALL_EXIF, resultSet);
305 photos.push_back(photo);
306 } while (MedialibrarySubscriber::IsCurrentStatusOn() && resultSet->GoToNextRow() == NativeRdb::E_OK);
307 return photos;
308 }
309
OffsetTimeToSeconds(const std::string & offsetStr,int32_t & offsetTime)310 static int32_t OffsetTimeToSeconds(const std::string &offsetStr, int32_t &offsetTime)
311 {
312 if (offsetStr.size() != OFFSET_STR_SIZE) {
313 MEDIA_WARN_LOG("Invalid offsetStr length: %{public}s", offsetStr.c_str());
314 return E_ERR;
315 }
316 const char sign = offsetStr[0];
317 if (sign != '+' && sign != '-') {
318 MEDIA_WARN_LOG("Invalid sign character: %{public}s", offsetStr.c_str());
319 return E_ERR;
320 }
321 if (offsetStr[COLON_POSITION] != ':') {
322 MEDIA_WARN_LOG("Missing colon at position: %{public}s", offsetStr.c_str());
323 return E_ERR;
324 }
325 int hours = 0;
326 const size_t endHoursIndex = 3;
327 std::from_chars_result result = std::from_chars(&offsetStr[1], &offsetStr[endHoursIndex], hours);
328 if (result.ec != std::errc() || result.ptr != &offsetStr[endHoursIndex]) {
329 MEDIA_WARN_LOG("Invalid offsetStr: %{public}s", offsetStr.c_str());
330 return E_ERR;
331 }
332 int minutes = 0;
333 const size_t startMinutesIndex = 4;
334 const size_t endMinutesIndex = 6;
335 result = std::from_chars(&offsetStr[startMinutesIndex], &offsetStr[endMinutesIndex], minutes);
336 if (result.ec != std::errc() || result.ptr != &offsetStr[endMinutesIndex]) {
337 MEDIA_WARN_LOG("Invalid offsetStr: %{public}s", offsetStr.c_str());
338 return E_ERR;
339 }
340 const int maxHours = 23;
341 if (hours < 0 || hours > maxHours) {
342 MEDIA_WARN_LOG("Hours out of range: %{public}s", offsetStr.c_str());
343 return E_ERR;
344 }
345 const int maxMinutes = 59;
346 if (minutes < 0 || minutes > maxMinutes) {
347 MEDIA_WARN_LOG("Minutes out of range: %{public}s", offsetStr.c_str());
348 return E_ERR;
349 }
350 const int totalSeconds = hours * HOURS_TO_SECOND + minutes * MINUTES_TO_SECOND;
351 offsetTime = (sign == '-') ? totalSeconds : -totalSeconds;
352 MEDIA_DEBUG_LOG("Offset conversion successful: %{public}s -> %{public}d seconds", offsetStr.c_str(), offsetTime);
353 return E_OK;
354 }
355
ConvertTimeStrToTimeStamp(string & timeStr)356 static time_t ConvertTimeStrToTimeStamp(string &timeStr)
357 {
358 struct tm timeinfo;
359 strptime(timeStr.c_str(), PhotoColumn::PHOTO_DETAIL_TIME_FORMAT.c_str(), &timeinfo);
360 time_t timeStamp = mktime(&timeinfo);
361 return timeStamp;
362 }
363
ConvertUTCTimeStrToTimeStamp(string & timeStr)364 static time_t ConvertUTCTimeStrToTimeStamp(string &timeStr)
365 {
366 struct tm timeinfo;
367 strptime(timeStr.c_str(), PhotoColumn::PHOTO_DETAIL_TIME_FORMAT.c_str(), &timeinfo);
368 time_t convertOnceTime = mktime(&timeinfo);
369 time_t convertTwiceTime = mktime(gmtime(&convertOnceTime));
370
371 bool cond = (convertOnceTime == -1 || convertTwiceTime == -1);
372 CHECK_AND_RETURN_RET(!cond, 0);
373
374 time_t offset = convertOnceTime - convertTwiceTime;
375 time_t utcTimeStamp = convertOnceTime + offset;
376 return utcTimeStamp;
377 }
378
JsonSafeGetString(const nlohmann::json & json,const std::string & key)379 std::string JsonSafeGetString(const nlohmann::json &json, const std::string &key)
380 {
381 auto it = json.find(key);
382 if (it == json.end()) {
383 return "";
384 }
385 return it->is_string() ? it->get<std::string>() : "";
386 }
387
SetSubSecondTime(const nlohmann::json & exifJson,int64_t & timeStamp)388 static void SetSubSecondTime(const nlohmann::json &exifJson, int64_t &timeStamp)
389 {
390 string subTimeStr = JsonSafeGetString(exifJson, PHOTO_DATA_IMAGE_SUBSEC_TIME_ORIGINAL);
391 if (subTimeStr.empty()) {
392 return;
393 }
394
395 const size_t millisecondPrecision = 3;
396 const size_t subTimeSize = std::min(millisecondPrecision, subTimeStr.size());
397 int32_t subTime = 0;
398 auto [ptr, ec] = std::from_chars(subTimeStr.data(), subTimeStr.data() + subTimeSize, subTime);
399 if (ec == std::errc() && ptr == subTimeStr.data() + subTimeSize) {
400 MEDIA_DEBUG_LOG("subTime:%{public}d from exif", subTime);
401 timeStamp += subTime;
402 } else {
403 MEDIA_WARN_LOG("Invalid subTime format:%{public}s", subTimeStr.c_str());
404 }
405 }
406
GetShootingTimeStampByExif(const nlohmann::json & exifJson)407 static int64_t GetShootingTimeStampByExif(const nlohmann::json &exifJson)
408 {
409 int64_t timeStamp = 0;
410 string timeString = JsonSafeGetString(exifJson, PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL);
411 if (timeString.empty() || timeString == ZEROTIMESTRING) {
412 return timeStamp;
413 }
414 string offsetString = JsonSafeGetString(exifJson, PHOTO_DATA_IMAGE_OFFSET_TIME_ORIGINAL);
415 int32_t offsetTime = 0;
416 if (!offsetString.empty() && OffsetTimeToSeconds(offsetString, offsetTime) == E_OK) {
417 timeStamp = (ConvertUTCTimeStrToTimeStamp(timeString) + offsetTime) * MSEC_TO_SEC;
418 MEDIA_DEBUG_LOG("Get timeStamp from DateTimeOriginal and OffsetTimeOriginal in exif");
419 } else {
420 timeStamp = (ConvertTimeStrToTimeStamp(timeString)) * MSEC_TO_SEC;
421 MEDIA_DEBUG_LOG("Get timeStamp from DateTimeOriginal in exif");
422 }
423 if (timeStamp > 0) {
424 SetSubSecondTime(exifJson, timeStamp);
425 MEDIA_DEBUG_LOG("OriginalTimeStamp:%{public}ld in exif", static_cast<long>(timeStamp));
426 }
427 return timeStamp;
428 }
429
ExtractDateTime(const std::string & exif)430 std::tuple<int64_t, std::string, std::string, std::string, std::string> ExtractDateTime(const std::string &exif)
431 {
432 if (exif.empty() || !nlohmann::json::accept(exif)) {
433 return std::make_tuple(0, "", "", "", "");
434 }
435 nlohmann::json exifJson = nlohmann::json::parse(exif, nullptr, false);
436 std::string detailTime = JsonSafeGetString(exifJson, PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL);
437 if (detailTime.empty() || detailTime == ZEROTIMESTRING) {
438 return std::make_tuple(0, "", "", "", "");
439 }
440 auto const [dateYear, dateMonth, dateDay] = PhotoFileUtils::ExtractYearMonthDay(detailTime);
441 if (dateDay.empty()) {
442 return std::make_tuple(0, "", "", "", "");
443 }
444 int64_t dateTaken = GetShootingTimeStampByExif(exifJson);
445 if (dateTaken <= 0) {
446 return std::make_tuple(0, "", "", "", "");
447 }
448 return std::make_tuple(dateTaken, detailTime, dateYear, dateMonth, dateDay);
449 }
450
HandleAnomalyDateTakenAndDetailTime(const std::shared_ptr<MediaLibraryRdbStore> rdbStore,const DateAnomalyPhoto & photo)451 void HandleAnomalyDateTakenAndDetailTime(
452 const std::shared_ptr<MediaLibraryRdbStore> rdbStore, const DateAnomalyPhoto &photo)
453 {
454 auto [dateTaken, detailTime, dateYear, dateMonth, dateDay] = ExtractDateTime(photo.exif);
455 if (dateTaken <= 0) {
456 dateTaken = photo.dateModified;
457 detailTime = MediaFileUtils::StrCreateTimeByMilliseconds(PhotoColumn::PHOTO_DETAIL_TIME_FORMAT, dateTaken);
458 auto const [detailYear, detailMonth, detailDay] = PhotoFileUtils::ExtractYearMonthDay(detailTime);
459 dateYear = detailYear;
460 dateMonth = detailMonth;
461 dateDay = detailDay;
462 }
463 ValuesBucket values;
464 values.Put(MediaColumn::MEDIA_DATE_TAKEN, dateTaken);
465 values.Put(PhotoColumn::PHOTO_DETAIL_TIME, detailTime);
466 values.Put(PhotoColumn::PHOTO_DATE_YEAR, dateYear);
467 values.Put(PhotoColumn::PHOTO_DATE_MONTH, dateMonth);
468 values.Put(PhotoColumn::PHOTO_DATE_DAY, dateDay);
469
470 NativeRdb::RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
471 predicates.EqualTo(MediaColumn::MEDIA_ID, photo.fileId);
472
473 int32_t updateCount = 0;
474 int32_t err = rdbStore->Update(updateCount, values, predicates);
475 MEDIA_INFO_LOG("update succeed, file_id = %{public}d, dateTaken = %{public}ld, photo.dateTaken = %{public}ld, "
476 "detailTime = %{public}s, photo.detailTime = %{public}s, err = %{public}d",
477 photo.fileId,
478 static_cast<long>(dateTaken),
479 static_cast<long>(photo.dateTaken),
480 detailTime.c_str(),
481 photo.detailTime.c_str(),
482 err);
483 if (dateTaken != photo.dateTaken) {
484 ThumbnailService::GetInstance()->UpdateAstcWithNewDateTaken(
485 to_string(photo.fileId), to_string(dateTaken), to_string(photo.dateTaken));
486 }
487 }
488
RepairDateAnomalyPhotos(const std::vector<DateAnomalyPhoto> & photos,int32_t & curFileId)489 void PhotoDayMonthYearOperation::RepairDateAnomalyPhotos(
490 const std::vector<DateAnomalyPhoto> &photos, int32_t &curFileId)
491 {
492 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
493 CHECK_AND_RETURN_LOG(rdbStore != nullptr, "rdbStore is nullptr");
494 for (const DateAnomalyPhoto &photo : photos) {
495 CHECK_AND_BREAK_INFO_LOG(MedialibrarySubscriber::IsCurrentStatusOn(), "current status is off, break");
496 curFileId = photo.fileId;
497 if (photo.dateTaken <= 0 || photo.detailTime.empty()) {
498 HandleAnomalyDateTakenAndDetailTime(rdbStore, photo);
499 continue;
500 }
501 auto [dateTaken, detailTime, dateYear, dateMonth, dateDay] = ExtractDateTime(photo.exif);
502 if (!detailTime.empty() && detailTime != photo.detailTime) {
503 NativeRdb::RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
504 predicates.EqualTo(MediaColumn::MEDIA_ID, photo.fileId);
505
506 ValuesBucket values;
507 values.Put(PhotoColumn::PHOTO_DETAIL_TIME, detailTime);
508 values.Put(PhotoColumn::PHOTO_DATE_YEAR, dateYear);
509 values.Put(PhotoColumn::PHOTO_DATE_MONTH, dateMonth);
510 values.Put(PhotoColumn::PHOTO_DATE_DAY, dateDay);
511
512 int32_t updateCount = 0;
513 int32_t err = rdbStore->Update(updateCount, values, predicates);
514 MEDIA_INFO_LOG("update succeed, file_id=%{public}d, photo.detailTime=%{public}s, detailTime=%{public}s, "
515 "err=%{public}d",
516 photo.fileId,
517 photo.detailTime.c_str(),
518 detailTime.c_str(),
519 err);
520 continue;
521 }
522 auto const [detailYear, detailMonth, detailDay] = PhotoFileUtils::ExtractYearMonthDay(photo.detailTime);
523 if (detailDay != photo.dateDay) {
524 NativeRdb::RdbPredicates predicates(PhotoColumn::PHOTOS_TABLE);
525 predicates.EqualTo(MediaColumn::MEDIA_ID, photo.fileId);
526
527 ValuesBucket values;
528 values.Put(PhotoColumn::PHOTO_DATE_YEAR, detailYear);
529 values.Put(PhotoColumn::PHOTO_DATE_MONTH, detailMonth);
530 values.Put(PhotoColumn::PHOTO_DATE_DAY, detailDay);
531
532 int32_t updateCount = 0;
533 int32_t err = rdbStore->Update(updateCount, values, predicates);
534 MEDIA_INFO_LOG("update succeed, file_id=%{public}d, photo.detailTime=%{public}s, photo.dateDay=%{public}s, "
535 "err=%{public}d",
536 photo.fileId,
537 photo.detailTime.c_str(),
538 photo.dateDay.c_str(),
539 err);
540 }
541 }
542 }
543
RepairDateTime()544 int32_t PhotoDayMonthYearOperation::RepairDateTime()
545 {
546 std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
547 CHECK_AND_RETURN_RET_WARN_LOG(lock.try_lock(), E_OK, "Repair date time has started, skipping this operation");
548
549 int32_t errCode = E_OK;
550 std::shared_ptr<NativePreferences::Preferences> prefs =
551 NativePreferences::PreferencesHelper::GetPreferences(REPAIR_DATE_TIME_XML, errCode);
552 CHECK_AND_RETURN_RET_LOG(prefs, E_ERR, "get preferences error: %{public}d", errCode);
553
554 int32_t curFileId = prefs->GetInt(CURRENT_FILE_ID, 0);
555 MEDIA_INFO_LOG("Repair date time start file id: %{public}d", curFileId);
556 do {
557 MEDIA_INFO_LOG("Repair date time curFileId: %{public}d", curFileId);
558 std::vector<DateAnomalyPhoto> photos = QueryDateAnomalyPhotos(curFileId);
559 CHECK_AND_BREAK_INFO_LOG(!photos.empty(), "has no anomaly photo to repair");
560 RepairDateAnomalyPhotos(photos, curFileId);
561 } while (MedialibrarySubscriber::IsCurrentStatusOn());
562
563 prefs->PutInt(CURRENT_FILE_ID, curFileId);
564 prefs->FlushSync();
565 MEDIA_INFO_LOG("Repair date time end file id: %{public}d", curFileId);
566 return E_OK;
567 }
568 } // namespace Media
569 } // namespace OHOS
570