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 #include <libxml/parser.h>
16 #include <libxml/tree.h>
17
18 #include "geo_knowledge_restore.h"
19
20 #include "backup_database_utils.h"
21 #include "medialibrary_data_manager_utils.h"
22 #include "media_file_utils.h"
23 #include "media_log.h"
24 #include "result_set_utils.h"
25 #include "locale_config.h"
26 #include "upgrade_restore_task_report.h"
27 #include "medialibrary_unistore_manager.h"
28
29 namespace OHOS::Media {
30 const int32_t PAGE_SIZE = 200;
31 const int32_t UPDATE_GEO = 3;
32 const string NOT_MATCH = "NOT MATCH";
33 const string LATITUDE = "latitude";
34 const string LONGITUDE = "longitude";
35 const string LOCATION_KEY = "location_key";
36 const string LANGUAGE = "language";
37 const string COUNTRY = "country";
38 const string ADMIN_AREA = "admin_area";
39 const string SUB_ADMIN_AREA = "sub_admin_area";
40 const string LOCALITY = "locality";
41 const string SUB_LOCALITY = "sub_locality";
42 const string THOROUGHFARE = "thoroughfare";
43 const string SUB_THOROUGHFARE = "sub_thoroughfare";
44 const string FEATURE_NAME = "feature_name";
45 const string ADDRESS_DESCRIPTION = "address_description";
46 const string FILE_ID = "file_id";
47 const string DATA = "data";
48 const string SINGLE_CH = "zh-Hans";
49 const string SINGLE_EN = "en-Latn-US";
50 const string DOUBLE_CH = "zh";
51 const string DOUBLE_EN = "en";
52 const int32_t GEO_STATUS_SUCCESS = 1;
53 constexpr double DOUBLE_EPSILON = 1e-15;
54
55 // LCOV_EXCL_START
Init(int32_t sceneCode,std::string taskId,std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb,std::shared_ptr<NativeRdb::RdbStore> galleryRdb)56 void GeoKnowledgeRestore::Init(int32_t sceneCode, std::string taskId,
57 std::shared_ptr<NativeRdb::RdbStore> mediaLibraryRdb, std::shared_ptr<NativeRdb::RdbStore> galleryRdb)
58 {
59 sceneCode_ = sceneCode;
60 taskId_ = taskId;
61 mediaLibraryRdb_ = mediaLibraryRdb;
62 galleryRdb_ = galleryRdb;
63 batchCnt_ = 0;
64 successInsertCnt_ = 0;
65 successUpdateCnt_ = 0;
66 failInsertCnt_ = 0;
67 failUpdateCnt_ = 0;
68 systemLanguage_ = Global::I18n::LocaleConfig::GetSystemLanguage();
69 }
70
RestoreGeo(const std::unordered_map<int32_t,PhotoInfo> & photoInfoMap)71 void GeoKnowledgeRestore::RestoreGeo(const std::unordered_map<int32_t, PhotoInfo> &photoInfoMap)
72 {
73 CHECK_AND_RETURN_LOG(galleryRdb_ != nullptr && mediaLibraryRdb_ != nullptr, "rdbStore is nullptr");
74 GetGeoKnowledgeInfos();
75 RestoreMaps(photoInfoMap);
76 ReportGeoRestoreTask();
77 }
78
GetGeoKnowledgeInfos()79 void GeoKnowledgeRestore::GetGeoKnowledgeInfos()
80 {
81 const std::string QUERY_SQL = "SELECT " + LATITUDE + ", " + LONGITUDE + ", " + LOCATION_KEY + ", " + LANGUAGE + ", "
82 + COUNTRY + ", " + ADMIN_AREA + ", " + SUB_ADMIN_AREA + ", " + LOCALITY + ", " + SUB_LOCALITY + ", "
83 + THOROUGHFARE + ", " + SUB_THOROUGHFARE + ", " + FEATURE_NAME + " "
84 "FROM t_geo_knowledge WHERE COALESCE(" + LANGUAGE + ", '') <> ' ' AND rowid > ? ORDER BY rowid LIMIT ?";
85 int rowCount = 0;
86 int offset = 0;
87 do {
88 std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
89 auto resultSet = galleryRdb_->QuerySql(QUERY_SQL, params);
90 CHECK_AND_BREAK_ERR_LOG(resultSet != nullptr, "resultSet is nullptr");
91 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
92 GeoKnowledgeInfo info;
93 info.latitude = GetDoubleVal(LATITUDE, resultSet);
94 info.longitude = GetDoubleVal(LONGITUDE, resultSet);
95 info.locationKey = GetInt64Val(LOCATION_KEY, resultSet);
96 info.language = GetStringVal(LANGUAGE, resultSet);
97 info.country = GetStringVal(COUNTRY, resultSet);
98 info.adminArea = GetStringVal(ADMIN_AREA, resultSet);
99 info.subAdminArea = GetStringVal(SUB_ADMIN_AREA, resultSet);
100 info.locality = GetStringVal(LOCALITY, resultSet);
101 info.subLocality = GetStringVal(SUB_LOCALITY, resultSet);
102 info.thoroughfare = GetStringVal(THOROUGHFARE, resultSet);
103 info.subThoroughfare = GetStringVal(SUB_THOROUGHFARE, resultSet);
104 info.featureName = GetStringVal(FEATURE_NAME, resultSet);
105 albumInfos_.emplace_back(info);
106 }
107 resultSet->GetRowCount(rowCount);
108 offset += PAGE_SIZE;
109 resultSet->Close();
110 } while (rowCount > 0);
111 }
112
RestoreMaps(const std::unordered_map<int32_t,PhotoInfo> & photoInfoMap)113 void GeoKnowledgeRestore::RestoreMaps(const std::unordered_map<int32_t, PhotoInfo> &photoInfoMap)
114 {
115 if (mediaLibraryRdb_ == nullptr || galleryRdb_ == nullptr) {
116 MEDIA_ERR_LOG("rdbStore is nullptr");
117 batchCnt_ = 0;
118 return;
119 }
120 std::string querySql = "SELECT _id, latitude, longitude FROM gallery_media "
121 "WHERE (ABS(latitude) >= 1e-15 OR ABS(longitude) >= 1e-15) AND _id > ? ORDER BY _id ASC LIMIT ?;";
122 int rowCount = 0;
123 int offset = 0;
124 do {
125 std::vector<std::string> fileIds;
126 std::vector<NativeRdb::ValuesBucket> values;
127 std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
128 auto resultSet = BackupDatabaseUtils::QuerySql(galleryRdb_, querySql, params);
129 CHECK_AND_RETURN(resultSet != nullptr);
130 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
131 GeoMapInfo geoMapInfo;
132 geoMapInfo.fileIdOld = GetInt32Val("_id", resultSet);
133 offset = geoMapInfo.fileIdOld;
134 CHECK_AND_CONTINUE(photoInfoMap.find(geoMapInfo.fileIdOld) != photoInfoMap.end());
135 geoMapInfo.photoInfo = photoInfoMap.at(geoMapInfo.fileIdOld);
136 geoMapInfo.latitude = GetDoubleVal("latitude", resultSet);
137 geoMapInfo.longitude = GetDoubleVal("longitude", resultSet);
138 std::string fileIdString = UpdateMapInsertValues(values, geoMapInfo);
139 CHECK_AND_EXECUTE(fileIdString == NOT_MATCH, fileIds.push_back(fileIdString));
140 }
141 resultSet->GetRowCount(rowCount);
142 resultSet->Close();
143 UpdateMaps(values, fileIds);
144 } while (rowCount == PAGE_SIZE);
145 }
146
UpdateMaps(std::vector<NativeRdb::ValuesBucket> & values,std::vector<std::string> & fileIds)147 void GeoKnowledgeRestore::UpdateMaps(std::vector<NativeRdb::ValuesBucket> &values, std::vector<std::string> &fileIds)
148 {
149 int64_t rowNum = 0;
150 int32_t errCodeGeo = BatchInsertWithRetry("tab_analysis_geo_knowledge", values, rowNum);
151 if (errCodeGeo != E_OK) {
152 MEDIA_ERR_LOG("GeoKnowledge: RestoreMaps insert fail");
153 ErrorInfo errorInfo(RestoreError::INSERT_FAILED, values.size(),
154 "errCodeGeo: " + std::to_string(errCodeGeo), "GeoKnowledge: RestoreMaps insert fail");
155 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
156 failInsertCnt_ += batchCnt_;
157 batchCnt_ = 0;
158 return;
159 }
160 successInsertCnt_ += batchCnt_;
161 int32_t errCodeUpdate = BatchUpdate("tab_analysis_total", fileIds);
162 if (errCodeUpdate != E_OK) {
163 MEDIA_ERR_LOG("AnalysisTotal: RestoreMaps update fail");
164 ErrorInfo errorInfo(RestoreError::UPDATE_FAILED, values.size(),
165 "errCodeUpdate: " + std::to_string(errCodeUpdate), "AnalysisTotal: RestoreMaps update fail");
166 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
167 failUpdateCnt_ += batchCnt_;
168 batchCnt_ = 0;
169 return;
170 }
171 successUpdateCnt_ += batchCnt_;
172 batchCnt_ = 0;
173 }
174
UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> & values,const GeoMapInfo & geoMapInfo)175 std::string GeoKnowledgeRestore::UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> &values,
176 const GeoMapInfo &geoMapInfo)
177 {
178 // no need restore or info missing
179 CHECK_AND_RETURN_RET(geoMapInfo.photoInfo.fileIdNew > 0, NOT_MATCH);
180 return UpdateByGeoLocation(values, geoMapInfo);
181 }
182
UpdateByGeoLocation(std::vector<NativeRdb::ValuesBucket> & values,const GeoMapInfo & geoMapInfo)183 std::string GeoKnowledgeRestore::UpdateByGeoLocation(
184 std::vector<NativeRdb::ValuesBucket> &values, const GeoMapInfo &geoMapInfo)
185 {
186 std::string language = systemLanguage_;
187 CHECK_AND_EXECUTE(!language.empty(), language = DOUBLE_CH);
188 language = (language == SINGLE_EN) ? DOUBLE_EN : DOUBLE_CH;
189
190 auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
191 [geoMapInfo, language](const GeoKnowledgeInfo& info) {
192 return std::fabs(info.latitude - geoMapInfo.latitude) < 0.0001
193 && std::fabs(info.longitude - geoMapInfo.longitude) < 0.0001 && info.language == language;
194 });
195 CHECK_AND_RETURN_RET(it != albumInfos_.end(), NOT_MATCH);
196 values.push_back(GetMapInsertValue(it, geoMapInfo.photoInfo.fileIdNew));
197 batchCnt_++;
198 return std::to_string(geoMapInfo.photoInfo.fileIdNew);
199 }
200
GetMapInsertValue(std::vector<GeoKnowledgeInfo>::iterator it,int32_t fileId)201 NativeRdb::ValuesBucket GeoKnowledgeRestore::GetMapInsertValue(std::vector<GeoKnowledgeInfo>::iterator it,
202 int32_t fileId)
203 {
204 NativeRdb::ValuesBucket value;
205 value.PutDouble(LATITUDE, it->latitude);
206 value.PutDouble(LONGITUDE, it->longitude);
207 value.PutLong(LOCATION_KEY, it->locationKey);
208 value.PutString(COUNTRY, it->country);
209 value.PutString(ADMIN_AREA, it->adminArea);
210 value.PutString(SUB_ADMIN_AREA, it->subAdminArea);
211 value.PutString(LOCALITY, it->locality);
212 value.PutString(SUB_LOCALITY, it->subLocality);
213 value.PutString(THOROUGHFARE, it->thoroughfare);
214 value.PutString(SUB_THOROUGHFARE, it->subThoroughfare);
215 value.PutString(FEATURE_NAME, it->featureName);
216 value.PutInt(FILE_ID, fileId);
217 if (it->adminArea == it->locality) {
218 value.PutString(ADDRESS_DESCRIPTION, it->locality + it->subLocality + it->thoroughfare
219 + it->subThoroughfare + it->featureName);
220 } else {
221 value.PutString(ADDRESS_DESCRIPTION, it->adminArea + it->subAdminArea + it->locality
222 + it->subLocality + it->thoroughfare + it->subThoroughfare + it->featureName);
223 }
224
225 if (it->language == DOUBLE_CH) {
226 value.PutString(LANGUAGE, SINGLE_CH);
227 return value;
228 }
229
230 value.PutString(LANGUAGE, SINGLE_EN);
231 return value;
232 }
233
BatchInsertWithRetry(const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & values,int64_t & rowNum)234 int32_t GeoKnowledgeRestore::BatchInsertWithRetry(const std::string &tableName,
235 std::vector<NativeRdb::ValuesBucket> &values, int64_t &rowNum)
236 {
237 CHECK_AND_RETURN_RET(!values.empty(), E_OK);
238 int32_t errCode = E_ERR;
239 TransactionOperations trans{ __func__ };
240 trans.SetBackupRdbStore(mediaLibraryRdb_);
241 std::function<int(void)> func = [&]()->int {
242 errCode = trans.BatchInsert(rowNum, tableName, values);
243 CHECK_AND_PRINT_LOG(errCode == E_OK, "InsertSql failed, errCode: %{public}d, rowNum: %{public}ld.",
244 errCode, (long)rowNum);
245 return errCode;
246 };
247 errCode = trans.RetryTrans(func, true);
248 CHECK_AND_PRINT_LOG(errCode == E_OK, "BatchInsertWithRetry: tans finish fail!, ret:%{public}d", errCode);
249 return errCode;
250 }
251
BatchUpdate(const std::string & tableName,std::vector<std::string> & fileIds)252 int32_t GeoKnowledgeRestore::BatchUpdate(const std::string &tableName, std::vector<std::string> &fileIds)
253 {
254 NativeRdb::RdbPredicates updatePredicates(tableName);
255 updatePredicates.In(FILE_ID, fileIds);
256 NativeRdb::ValuesBucket rdbValues;
257 rdbValues.PutInt("geo", UPDATE_GEO);
258 int32_t changedRows = -1;
259 int32_t errCode = E_OK;
260 errCode = mediaLibraryRdb_->Update(changedRows, rdbValues, updatePredicates);
261 CHECK_AND_PRINT_LOG(errCode == E_OK, "Database update failed, errCode = %{public}d", errCode);
262 return errCode;
263 }
264
ReportGeoRestoreTask()265 void GeoKnowledgeRestore::ReportGeoRestoreTask()
266 {
267 MEDIA_INFO_LOG("GeoKnowledge Insert successInsertCnt_: %{public}d, failInsertCnt_: %{public}d, "
268 "AnalysisTotal Update successUpdateCnt_: %{public}d, failUpdateCnt_: %{public}d, "
269 "Current System Language: %{public}s",
270 successInsertCnt_.load(), failInsertCnt_.load(), successUpdateCnt_.load(),
271 failUpdateCnt_.load(), systemLanguage_.c_str());
272 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_)
273 .Report("GeoKnowledge Restore", std::to_string(GEO_STATUS_SUCCESS),
274 "successInsertCnt_: " + std::to_string(successInsertCnt_) +
275 ", failInsertCnt_: " + std::to_string(failInsertCnt_) +
276 ", successUpdateCnt_: " + std::to_string(successUpdateCnt_) +
277 ", failUpdateCnt_: " + std::to_string(failUpdateCnt_) +
278 ", Current System Language: " + systemLanguage_);
279 }
280 // LCOV_EXCL_STOP
281 } // namespace OHOS::Media