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
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
RestoreGeoKnowledgeInfos()71 void GeoKnowledgeRestore::RestoreGeoKnowledgeInfos()
72 {
73 bool cond = (galleryRdb_ == nullptr || mediaLibraryRdb_ == nullptr);
74 CHECK_AND_RETURN_LOG(!cond, "rdbStore is nullptr");
75 GetGeoKnowledgeInfos();
76 }
77
GetGeoKnowledgeInfos()78 void GeoKnowledgeRestore::GetGeoKnowledgeInfos()
79 {
80 const std::string QUERY_SQL = "SELECT " + LATITUDE + ", " + LONGITUDE + ", " + LOCATION_KEY + ", " + LANGUAGE + ", "
81 + COUNTRY + ", " + ADMIN_AREA + ", " + SUB_ADMIN_AREA + ", " + LOCALITY + ", " + SUB_LOCALITY + ", "
82 + THOROUGHFARE + ", " + SUB_THOROUGHFARE + ", " + FEATURE_NAME + " "
83 "FROM t_geo_knowledge WHERE COALESCE(" + LANGUAGE + ", '') <> ' ' LIMIT ?, ?";
84 int rowCount = 0;
85 int offset = 0;
86 do {
87 std::vector<NativeRdb::ValueObject> params = {offset, PAGE_SIZE};
88 auto resultSet = galleryRdb_->QuerySql(QUERY_SQL, params);
89 if (resultSet == nullptr) {
90 MEDIA_ERR_LOG("resultSet is nullptr");
91 break;
92 }
93 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
94 GeoKnowledgeInfo info;
95 info.latitude = GetDoubleVal(LATITUDE, resultSet);
96 info.longitude = GetDoubleVal(LONGITUDE, resultSet);
97 info.locationKey = GetInt64Val(LOCATION_KEY, resultSet);
98 info.language = GetStringVal(LANGUAGE, resultSet);
99 info.country = GetStringVal(COUNTRY, resultSet);
100 info.adminArea = GetStringVal(ADMIN_AREA, resultSet);
101 info.subAdminArea = GetStringVal(SUB_ADMIN_AREA, resultSet);
102 info.locality = GetStringVal(LOCALITY, resultSet);
103 info.subLocality = GetStringVal(SUB_LOCALITY, resultSet);
104 info.thoroughfare = GetStringVal(THOROUGHFARE, resultSet);
105 info.subThoroughfare = GetStringVal(SUB_THOROUGHFARE, resultSet);
106 info.featureName = GetStringVal(FEATURE_NAME, resultSet);
107 albumInfos_.emplace_back(info);
108 }
109 resultSet->GetRowCount(rowCount);
110 offset += PAGE_SIZE;
111 resultSet->Close();
112 } while (rowCount > 0);
113 }
114
RestoreMaps(std::vector<FileInfo> & fileInfos)115 void GeoKnowledgeRestore::RestoreMaps(std::vector<FileInfo> &fileInfos)
116 {
117 if (mediaLibraryRdb_ == nullptr) {
118 MEDIA_ERR_LOG("rdbStore is nullptr");
119 batchCnt_ = 0;
120 return;
121 }
122 std::vector<std::string> fileIds;
123 std::vector<NativeRdb::ValuesBucket> values;
124 BatchQueryPhoto(fileInfos);
125 for (const auto &fileInfo : fileInfos) {
126 std::string fileIdString = UpdateMapInsertValues(values, fileInfo);
127 if (fileIdString != NOT_MATCH) {
128 fileIds.push_back(fileIdString);
129 }
130 }
131 int64_t rowNum = 0;
132 int32_t errCodeGeo = BatchInsertWithRetry("tab_analysis_geo_knowledge", values, rowNum);
133 if (errCodeGeo != E_OK) {
134 MEDIA_ERR_LOG("GeoKnowledge: RestoreMaps insert fail");
135 ErrorInfo errorInfo(RestoreError::INSERT_FAILED, values.size(),
136 "errCodeGeo: " + std::to_string(errCodeGeo), "GeoKnowledge: RestoreMaps insert fail");
137 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
138 failInsertCnt_ += batchCnt_;
139 batchCnt_ = 0;
140 return;
141 }
142 successInsertCnt_ += batchCnt_;
143 int32_t errCodeUpdate = BatchUpdate("tab_analysis_total", fileIds);
144 if (errCodeUpdate != E_OK) {
145 MEDIA_ERR_LOG("AnalysisTotal: RestoreMaps update fail");
146 ErrorInfo errorInfo(RestoreError::UPDATE_FAILED, values.size(),
147 "errCodeUpdate: " + std::to_string(errCodeUpdate), "AnalysisTotal: RestoreMaps update fail");
148 UpgradeRestoreTaskReport().SetSceneCode(this->sceneCode_).SetTaskId(this->taskId_).ReportError(errorInfo);
149 failUpdateCnt_ += batchCnt_;
150 batchCnt_ = 0;
151 return;
152 }
153 successUpdateCnt_ += batchCnt_;
154 batchCnt_ = 0;
155 }
156
BatchQueryPhoto(std::vector<FileInfo> & fileInfos)157 void GeoKnowledgeRestore::BatchQueryPhoto(std::vector<FileInfo> &fileInfos)
158 {
159 std::stringstream querySql;
160 querySql << "SELECT " + FILE_ID + ", " + DATA + " FROM Photos WHERE " + DATA + " IN (";
161 std::vector<NativeRdb::ValueObject> params;
162 int32_t count = 0;
163 for (const auto &fileInfo : fileInfos) {
164 // no need query or alreay queried
165 if ((fabs(fileInfo.latitude) < DOUBLE_EPSILON && fabs(fileInfo.latitude) < DOUBLE_EPSILON)
166 || fileInfo.fileIdNew > 0) {
167 continue;
168 }
169 querySql << (count++ > 0 ? "," : "");
170 querySql << "?";
171 params.push_back(fileInfo.cloudPath);
172 }
173 querySql << ")";
174 auto resultSet = mediaLibraryRdb_->QuerySql(querySql.str(), params);
175 CHECK_AND_RETURN_LOG(resultSet != nullptr, "resultSet is nullptr");
176
177 std::vector<NativeRdb::ValuesBucket> values;
178 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
179 int32_t fileId = GetInt32Val(FILE_ID, resultSet);
180 std::string data = GetStringVal(DATA, resultSet);
181 auto it = std::find_if(fileInfos.begin(), fileInfos.end(),
182 [data](const FileInfo& info) {
183 return info.cloudPath == data;
184 });
185 if (it == fileInfos.end()) {
186 continue;
187 }
188 it->fileIdNew = fileId;
189 }
190 resultSet->Close();
191 }
192
UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> & values,const FileInfo & fileInfo)193 std::string GeoKnowledgeRestore::UpdateMapInsertValues(std::vector<NativeRdb::ValuesBucket> &values,
194 const FileInfo &fileInfo)
195 {
196 // no need restore or info missing
197 bool cond = ((fabs(fileInfo.latitude) < DOUBLE_EPSILON && fabs(fileInfo.latitude) < DOUBLE_EPSILON)
198 || fileInfo.fileIdNew <= 0);
199 CHECK_AND_RETURN_RET(!cond, NOT_MATCH);
200 return UpdateByGeoLocation(values, fileInfo, fileInfo.latitude, fileInfo.longitude);
201 }
202
UpdateByGeoLocation(std::vector<NativeRdb::ValuesBucket> & values,const FileInfo & fileInfo,const double latitude,const double longitude)203 std::string GeoKnowledgeRestore::UpdateByGeoLocation(
204 std::vector<NativeRdb::ValuesBucket> &values, const FileInfo &fileInfo, const double latitude,
205 const double longitude)
206 {
207 std::string language = systemLanguage_;
208 if (language.empty()) {
209 language = DOUBLE_CH;
210 }
211 if (language == SINGLE_EN) {
212 language = DOUBLE_EN;
213 } else {
214 language = DOUBLE_CH;
215 }
216 auto it = std::find_if(albumInfos_.begin(), albumInfos_.end(),
217 [latitude, longitude, language](const GeoKnowledgeInfo& info) {
218 return std::fabs(info.latitude - latitude) < 0.0001
219 && std::fabs(info.longitude - longitude) < 0.0001 && info.language == language;
220 });
221 if (it == albumInfos_.end()) {
222 return NOT_MATCH;
223 }
224 values.push_back(GetMapInsertValue(it, fileInfo.fileIdNew));
225 batchCnt_++;
226 return std::to_string(fileInfo.fileIdNew);
227 }
228
GetMapInsertValue(std::vector<GeoKnowledgeInfo>::iterator it,int32_t fileId)229 NativeRdb::ValuesBucket GeoKnowledgeRestore::GetMapInsertValue(std::vector<GeoKnowledgeInfo>::iterator it,
230 int32_t fileId)
231 {
232 NativeRdb::ValuesBucket value;
233 value.PutDouble(LATITUDE, it->latitude);
234 value.PutDouble(LONGITUDE, it->longitude);
235 value.PutLong(LOCATION_KEY, it->locationKey);
236 value.PutString(COUNTRY, it->country);
237 value.PutString(ADMIN_AREA, it->adminArea);
238 value.PutString(SUB_ADMIN_AREA, it->subAdminArea);
239 value.PutString(LOCALITY, it->locality);
240 value.PutString(SUB_LOCALITY, it->subLocality);
241 value.PutString(THOROUGHFARE, it->thoroughfare);
242 value.PutString(SUB_THOROUGHFARE, it->subThoroughfare);
243 value.PutString(FEATURE_NAME, it->featureName);
244 value.PutInt(FILE_ID, fileId);
245 if (it->adminArea == it->locality) {
246 value.PutString(ADDRESS_DESCRIPTION, it->locality + it->subLocality + it->thoroughfare
247 + it->subThoroughfare + it->featureName);
248 } else {
249 value.PutString(ADDRESS_DESCRIPTION, it->adminArea + it->subAdminArea + it->locality
250 + it->subLocality + it->thoroughfare + it->subThoroughfare + it->featureName);
251 }
252
253 if (it->language == DOUBLE_CH) {
254 value.PutString(LANGUAGE, SINGLE_CH);
255 return value;
256 }
257
258 value.PutString(LANGUAGE, SINGLE_EN);
259 return value;
260 }
261
BatchInsertWithRetry(const std::string & tableName,std::vector<NativeRdb::ValuesBucket> & values,int64_t & rowNum)262 int32_t GeoKnowledgeRestore::BatchInsertWithRetry(const std::string &tableName,
263 std::vector<NativeRdb::ValuesBucket> &values, int64_t &rowNum)
264 {
265 if (values.empty()) {
266 return E_OK;
267 }
268 int32_t errCode = E_ERR;
269 TransactionOperations trans{ __func__ };
270 trans.SetBackupRdbStore(mediaLibraryRdb_);
271 std::function<int(void)> func = [&]()->int {
272 errCode = trans.BatchInsert(rowNum, tableName, values);
273 CHECK_AND_PRINT_LOG(errCode == E_OK, "InsertSql failed, errCode: %{public}d, rowNum: %{public}ld.",
274 errCode, (long)rowNum);
275 return errCode;
276 };
277 errCode = trans.RetryTrans(func, true);
278 CHECK_AND_PRINT_LOG(errCode == E_OK, "BatchInsertWithRetry: tans finish fail!, ret:%{public}d", errCode);
279 return errCode;
280 }
281
BatchUpdate(const std::string & tableName,std::vector<std::string> & fileIds)282 int32_t GeoKnowledgeRestore::BatchUpdate(const std::string &tableName, std::vector<std::string> &fileIds)
283 {
284 NativeRdb::RdbPredicates updatePredicates(tableName);
285 updatePredicates.In(FILE_ID, fileIds);
286 NativeRdb::ValuesBucket rdbValues;
287 rdbValues.PutInt("geo", UPDATE_GEO);
288 int32_t changedRows = -1;
289 int32_t errCode = E_OK;
290 errCode = mediaLibraryRdb_->Update(changedRows, rdbValues, updatePredicates);
291 CHECK_AND_PRINT_LOG(errCode == E_OK, "Database update failed, errCode = %{public}d", errCode);
292 return errCode;
293 }
294
ReportGeoRestoreTask()295 void GeoKnowledgeRestore::ReportGeoRestoreTask()
296 {
297 MEDIA_INFO_LOG("GeoKnowledge Insert successInsertCnt_: %{public}d, failInsertCnt_: %{public}d, "
298 "AnalysisTotal Update successUpdateCnt_: %{public}d, failUpdateCnt_: %{public}d, "
299 "Current System Language: %{public}s",
300 successInsertCnt_.load(), failInsertCnt_.load(), successUpdateCnt_.load(),
301 failUpdateCnt_.load(), systemLanguage_.c_str());
302 UpgradeRestoreTaskReport().SetSceneCode(sceneCode_).SetTaskId(taskId_)
303 .Report("GeoKnowledge Restore", std::to_string(GEO_STATUS_SUCCESS),
304 "successInsertCnt_: " + std::to_string(successInsertCnt_) +
305 ", failInsertCnt_: " + std::to_string(failInsertCnt_) +
306 ", successUpdateCnt_: " + std::to_string(successUpdateCnt_) +
307 ", failUpdateCnt_: " + std::to_string(failUpdateCnt_) +
308 ", Current System Language: " + systemLanguage_);
309 }
310 } // namespace OHOS::Media