• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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