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 #define MLOG_TAG "ForeGroundAnalysisMeta"
16 #include "foreground_analysis_meta.h"
17
18 #include "ffrt.h"
19 #include "ffrt_inner.h"
20 #include "media_analysis_helper.h"
21 #include "media_column.h"
22 #include "media_file_utils.h"
23 #include "media_log.h"
24 #include "medialibrary_data_manager.h"
25 #include "medialibrary_data_manager_utils.h"
26 #include "medialibrary_db_const.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_unistore_manager.h"
29 #include "rdb_utils.h"
30 #include "search_column.h"
31 #include "user_photography_info_column.h"
32 #include "vision_column.h"
33 #include "vision_total_column.h"
34
35 using namespace OHOS::NativeRdb;
36 using Uri = OHOS::Uri;
37 using namespace OHOS::DataShare;
38
39 namespace OHOS {
40 namespace Media {
ForegroundAnalysisMeta(std::shared_ptr<NativeRdb::ResultSet> result)41 ForegroundAnalysisMeta::ForegroundAnalysisMeta(std::shared_ptr<NativeRdb::ResultSet> result)
42 {
43 if (result == nullptr) {
44 return;
45 }
46 if (result->GoToNextRow() != NativeRdb::E_OK) {
47 return;
48 }
49 int colIndex = 0;
50 int frontIndexLimit = 0;
51 result->GetColumnIndex(FRONT_INDEX_LIMIT, colIndex);
52 result->GetInt(colIndex, frontIndexLimit);
53 if (frontIndexLimit > 0) {
54 frontIndexLimit_ = frontIndexLimit;
55 }
56 result->GetColumnIndex(FRONT_INDEX_MODIFIED, colIndex);
57 result->GetLong(colIndex, frontIndexModified_);
58 result->GetColumnIndex(FRONT_INDEX_COUNT, colIndex);
59 result->GetInt(colIndex, frontIndexCount_);
60 result->GetColumnIndex(FRONT_CV_MODIFIED, colIndex);
61 result->GetLong(colIndex, frontCvModified_);
62 result->GetColumnIndex(FRONT_CV_COUNT, colIndex);
63 result->GetInt(colIndex, frontCvCount_);
64 isInit_ = true;
65 }
66
~ForegroundAnalysisMeta()67 ForegroundAnalysisMeta::~ForegroundAnalysisMeta() {}
68
GenerateOpType(MediaLibraryCommand & cmd)69 int32_t ForegroundAnalysisMeta::GenerateOpType(MediaLibraryCommand &cmd)
70 {
71 int errCode = E_OK;
72 if (IsMetaDirtyed()) {
73 errCode = RefreshMeta();
74 if (errCode != E_OK) {
75 MEDIA_ERR_LOG("refresh err:%{public}d", errCode);
76 return errCode;
77 }
78 }
79 errCode = CheckCvAnalysisCondition(cmd);
80 if (errCode != E_OK) {
81 MEDIA_ERR_LOG("chk cv err:%{public}d", errCode);
82 return errCode;
83 }
84 errCode = CheckIndexAnalysisCondition(cmd);
85 if (errCode != E_OK) {
86 MEDIA_ERR_LOG("chk index err:%{public}d", errCode);
87 return errCode;
88 }
89 taskId_ = GetCurTaskId(cmd);
90 return E_OK;
91 }
92
IsMetaDirtyed()93 bool ForegroundAnalysisMeta::IsMetaDirtyed()
94 {
95 std::time_t cvTime = frontCvModified_ / 1000;
96 std::time_t indexTime = frontIndexModified_ / 1000;
97 std::time_t curTime = MediaFileUtils::UTCTimeMilliSeconds() / 1000;
98 std::tm cvTm;
99 localtime_r(&cvTime, &cvTm);
100 std::tm indexTm;
101 localtime_r(&indexTime, &indexTm);
102 std::tm curTm;
103 localtime_r(&curTime, &curTm);
104 return (cvTm.tm_year != curTm.tm_year) || (cvTm.tm_mon != curTm.tm_mon) || (cvTm.tm_mday != curTm.tm_mday) ||
105 (indexTm.tm_year != curTm.tm_year) || (indexTm.tm_mon != curTm.tm_mon) || (indexTm.tm_mday != curTm.tm_mday) ||
106 !isInit_;
107 }
108
RefreshMeta()109 int32_t ForegroundAnalysisMeta::RefreshMeta()
110 {
111 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
112 if (rdbStore == nullptr) {
113 return E_ERR;
114 }
115 int64_t curMoified = MediaFileUtils::UTCTimeMilliSeconds();
116 frontIndexModified_ = curMoified;
117 frontCvModified_ = curMoified;
118 frontIndexCount_ = 0;
119 frontCvCount_ = 0;
120 isInit_ = true;
121 return E_OK;
122 }
123
CheckCvAnalysisCondition(MediaLibraryCommand & cmd)124 int32_t ForegroundAnalysisMeta::CheckCvAnalysisCondition(MediaLibraryCommand &cmd)
125 {
126 if (frontCvCount_ >= FRONT_CV_MAX_LIMIT) {
127 return E_OK;
128 }
129 std::vector<std::string> fileIds;
130 int32_t errCode = QueryPendingAnalyzeFileIds(cmd, fileIds);
131 if (errCode != E_OK) {
132 return errCode;
133 }
134 if (!fileIds.empty()) {
135 fileIds_ = std::move(fileIds);
136 opType_ |= ForegroundAnalysisOpType::OCR_AND_LABEL;
137 if (frontIndexCount_ < frontIndexLimit_) {
138 opType_ |= ForegroundAnalysisOpType::SEARCH_INDEX;
139 }
140 }
141 return E_OK;
142 }
143
CheckIndexAnalysisCondition(MediaLibraryCommand & cmd)144 int32_t ForegroundAnalysisMeta::CheckIndexAnalysisCondition(MediaLibraryCommand &cmd)
145 {
146 if ((frontIndexCount_ >= frontIndexLimit_) || (opType_ & ForegroundAnalysisOpType::SEARCH_INDEX)) {
147 return E_OK;
148 }
149 int32_t pengdIndexCount = 0;
150 int32_t errCode = QueryPendingIndexCount(cmd, pengdIndexCount);
151 if (errCode != E_OK) {
152 return errCode;
153 }
154 if (pengdIndexCount > 0) {
155 opType_ |= ForegroundAnalysisOpType::SEARCH_INDEX;
156 }
157 return E_OK;
158 }
159
StartAnalysisService()160 void ForegroundAnalysisMeta::StartAnalysisService()
161 {
162 if (opType_ == ForegroundAnalysisOpType::FOREGROUND_NOT_HANDLE) {
163 return;
164 }
165 static std::once_flag onceFlag;
166 std::call_once(onceFlag, []() {
167 ffrt_set_cpu_worker_max_num(ffrt::qos_utility, FRONT_THREAD_NUM);
168 });
169 ffrt::submit(
170 [taskId = taskId_, opType = opType_, fileIds = fileIds_]() {
171 MEDIA_INFO_LOG("prepare submit taskId:%{public}d, opType:%{public}d, size:%{public}zu", taskId, opType,
172 fileIds.size());
173 ForegroundAnalysisMeta::StartServiceByOpType(opType, fileIds, taskId);
174 },
175 {},
176 {},
177 ffrt::task_attr().qos(static_cast<int32_t>(ffrt::qos_utility)));
178 }
179
QueryPendingAnalyzeFileIds(MediaLibraryCommand & cmd,std::vector<std::string> & fileIds)180 int32_t ForegroundAnalysisMeta::QueryPendingAnalyzeFileIds(MediaLibraryCommand &cmd, std::vector<std::string> &fileIds)
181 {
182 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
183 if (rdbStore == nullptr) {
184 MEDIA_ERR_LOG("db is null");
185 return E_ERR;
186 }
187 std::string onClause = VISION_TOTAL_TABLE + "." + MediaColumn::MEDIA_ID + " = " + PhotoColumn::PHOTOS_TABLE + "." +
188 MediaColumn::MEDIA_ID;
189 std::string colmun = VISION_TOTAL_TABLE + "." + MediaColumn::MEDIA_ID;
190 std::string whereClause = cmd.GetAbsRdbPredicates()->GetWhereClause();
191 int32_t analysisType = AnalysisType::ANALYSIS_SEARCH_INDEX;
192 std::string analysisTypeParam = cmd.GetQuerySetParam(FOREGROUND_ANALYSIS_TYPE);
193 if (MediaLibraryDataManagerUtils::IsNumber(analysisTypeParam)) {
194 analysisType = std::atoi(analysisTypeParam.c_str());
195 }
196 AppendAnalysisTypeOnWhereClause(analysisType, whereClause);
197 std::string orderBy = " ORDER BY " + PhotoColumn::PHOTOS_TABLE + "." + MediaColumn::MEDIA_DATE_MODIFIED;
198 std::string limit = " LIMIT " + std::to_string(std::max(0, FRONT_CV_MAX_LIMIT - frontCvCount_));
199 std::string sql = "SELECT " + colmun + " FROM " + VISION_TOTAL_TABLE + " INNER JOIN " + PhotoColumn::PHOTOS_TABLE +
200 " ON " + onClause + " WHERE " + whereClause + orderBy + limit;
201 auto result = rdbStore->QuerySql(sql, cmd.GetAbsRdbPredicates()->GetWhereArgs());
202 if (result == nullptr) {
203 MEDIA_ERR_LOG("query err");
204 return E_ERR;
205 }
206 while (result->GoToNextRow() == NativeRdb::E_OK) {
207 int fileId;
208 int colIndex = 0;
209 result->GetColumnIndex(MediaColumn::MEDIA_ID, colIndex);
210 result->GetInt(colIndex, fileId);
211 fileIds.push_back(std::to_string(fileId));
212 }
213 result->Close();
214 if (fileIds.empty()) {
215 MEDIA_INFO_LOG("no fileId match");
216 } else {
217 MEDIA_INFO_LOG("cv match cnt:%{public}zu", fileIds.size());
218 }
219 return E_OK;
220 }
221
AppendAnalysisTypeOnWhereClause(int32_t type,std::string & whereClause)222 void ForegroundAnalysisMeta::AppendAnalysisTypeOnWhereClause(int32_t type, std::string &whereClause)
223 {
224 if (!whereClause.empty()) {
225 whereClause.append(" AND ");
226 }
227 static const std::map<int32_t, std::string> FRONT_ANALYSIS_WHERE_CLAUSE_MAP = {
228 { ANALYSIS_SEARCH_INDEX, VISION_TOTAL_TABLE + "." + STATUS + " = 0" + " AND (" + VISION_TOTAL_TABLE + "." +
229 OCR + " = 0 OR " + VISION_TOTAL_TABLE + "." + LABEL + " = 0) AND " + PhotoColumn::PHOTOS_TABLE +
230 "." + MediaColumn::MEDIA_TYPE + " IN (" + std::to_string(MediaType::MEDIA_TYPE_IMAGE) + ") " },
231 };
232 std::string analysisTypeClause;
233 auto it = FRONT_ANALYSIS_WHERE_CLAUSE_MAP.find(type);
234 if (it != FRONT_ANALYSIS_WHERE_CLAUSE_MAP.end()) {
235 analysisTypeClause = it->second;
236 }
237 if (analysisTypeClause.empty()) {
238 whereClause.append(" 1 = 1 ");
239 } else {
240 whereClause.append(" (" + analysisTypeClause + ") ");
241 }
242 }
243
QueryPendingIndexCount(MediaLibraryCommand & cmd,int32_t & count)244 int32_t ForegroundAnalysisMeta::QueryPendingIndexCount(MediaLibraryCommand &cmd, int32_t &count)
245 {
246 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
247 if (rdbStore == nullptr) {
248 return E_ERR;
249 }
250 std::string onClause = SEARCH_TOTAL_TABLE + "." + MediaColumn::MEDIA_ID + " = " + PhotoColumn::PHOTOS_TABLE + "." +
251 MediaColumn::MEDIA_ID;
252 std::string colmun = "COUNT(1)";
253 std::string whereClause = SEARCH_TOTAL_TABLE + "." + TBL_SEARCH_PHOTO_STATUS + " in (0, 2) AND " +
254 PhotoColumn::PHOTOS_TABLE + "." + MediaColumn::MEDIA_TYPE + " IN (" +
255 std::to_string(MediaType::MEDIA_TYPE_IMAGE) + "," + std::to_string(MediaType::MEDIA_TYPE_VIDEO) + ") ";
256 std::string sql = "SELECT " + colmun + " FROM " + SEARCH_TOTAL_TABLE + " INNER JOIN " + PhotoColumn::PHOTOS_TABLE +
257 " ON " + onClause + " WHERE " + whereClause;
258 auto result = rdbStore->QuerySql(sql);
259 if (result == nullptr) {
260 MEDIA_ERR_LOG("query err");
261 return E_ERR;
262 }
263 while (result->GoToNextRow() == NativeRdb::E_OK) {
264 int colIndex = 0;
265 result->GetInt(colIndex, count);
266 }
267 MEDIA_INFO_LOG("index match cnt:%{public}d", count);
268 result->Close();
269 return E_OK;
270 }
271
GetCurTaskId(MediaLibraryCommand & cmd)272 int32_t ForegroundAnalysisMeta::GetCurTaskId(MediaLibraryCommand &cmd)
273 {
274 int32_t curTaskId = -1;
275 std::string idParam = cmd.GetQuerySetParam(FOREGROUND_ANALYSIS_TASK_ID);
276 if (MediaLibraryDataManagerUtils::IsNumber(idParam)) {
277 curTaskId = std::atoi(idParam.c_str());
278 }
279 return curTaskId;
280 }
281
QueryByErrorCode(int32_t errCode)282 std::shared_ptr<NativeRdb::ResultSet> ForegroundAnalysisMeta::QueryByErrorCode(int32_t errCode)
283 {
284 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
285 if (rdbStore == nullptr) {
286 return nullptr;
287 }
288 std::string sql = "SELECT " + std::to_string(errCode);
289 return rdbStore->QuerySql(sql);
290 }
291
StartServiceByOpType(uint32_t opType,const std::vector<std::string> & fileIds,int32_t taskId)292 void ForegroundAnalysisMeta::StartServiceByOpType(uint32_t opType,
293 const std::vector<std::string> &fileIds, int32_t taskId)
294 {
295 if (opType & ForegroundAnalysisOpType::OCR_AND_LABEL) {
296 MediaAnalysisHelper::StartForegroundAnalysisServiceSync(
297 IMediaAnalysisService::ActivateServiceType::START_FOREGROUND_OCR, fileIds, taskId);
298 }
299 if (opType & ForegroundAnalysisOpType::SEARCH_INDEX) {
300 MediaAnalysisHelper::StartForegroundAnalysisServiceSync(
301 IMediaAnalysisService::ActivateServiceType::START_FOREGROUND_INDEX, {}, taskId);
302 }
303 }
304
GetTrigger()305 DelayBatchTrigger& DelayBatchTrigger::GetTrigger()
306 {
307 static DelayBatchTrigger trigger([](const std::map<int32_t, std::set<std::string>> &requests) {
308 for (const auto &[analysisType, fileIdSet] : requests) {
309 std::string uriStr = PAH_QUERY_ANA_FOREGROUND;
310 MediaFileUtils::UriAppendKeyValue(uriStr, FOREGROUND_ANALYSIS_TYPE,
311 std::to_string(AnalysisType::ANALYSIS_SEARCH_INDEX));
312 MediaFileUtils::UriAppendKeyValue(uriStr, FOREGROUND_ANALYSIS_TASK_ID,
313 std::to_string(ForegroundAnalysisMeta::GetIncTaskId()));
314 Uri uri(uriStr);
315 MediaLibraryCommand cmd(uri);
316 DataShare::DataSharePredicates predicates;
317 std::vector<std::string> fileIds(fileIdSet.begin(), fileIdSet.end());
318 predicates.In(PhotoColumn::PHOTOS_TABLE + "." + PhotoColumn::MEDIA_ID, fileIds);
319 std::vector<string> columns;
320 int errCode = E_OK;
321 MediaLibraryDataManager::GetInstance()->Query(cmd, columns, predicates, errCode);
322 }
323 }, DELAY_BATCH_TRIGGER_TIME_MS);
324 return trigger;
325 }
326
DelayBatchTrigger(std::function<void (const std::map<int32_t,std::set<std::string>> &)> callback,int delayMs)327 DelayBatchTrigger::DelayBatchTrigger(std::function<void(const std::map<int32_t, std::set<std::string>> &)> callback,
328 int delayMs) : callback_(callback), delayMs_(delayMs), timerRunning_(false)
329 {
330 }
331
Push(const std::vector<std::string> & fileIds,int32_t analysisType)332 void DelayBatchTrigger::Push(const std::vector<std::string> &fileIds, int32_t analysisType)
333 {
334 {
335 std::lock_guard<std::mutex> lock(mutex_);
336 auto it = requestMap_.find(analysisType);
337 if (it != requestMap_.end()) {
338 it->second.insert(fileIds.begin(), fileIds.end());
339 } else {
340 requestMap_[analysisType] = std::set<std::string>(fileIds.begin(), fileIds.end());
341 }
342 }
343 StartTimer();
344 }
345
StartTimer()346 void DelayBatchTrigger::StartTimer()
347 {
348 bool expected = false;
349 if (timerRunning_.compare_exchange_strong(expected, true)) {
350 ffrt::submit(
351 [this]() {
352 std::this_thread::sleep_for(std::chrono::milliseconds(delayMs_));
353 std::map<int32_t, std::set<std::string>> requests;
354 {
355 std::lock_guard<std::mutex> lock(mutex_);
356 requests.swap(requestMap_);
357 timerRunning_ = false;
358 }
359
360 if (!requests.empty() && callback_) {
361 callback_(requests);
362 }
363 },
364 {},
365 {},
366 ffrt::task_attr().qos(static_cast<int32_t>(ffrt::qos_utility)));
367 }
368 }
369 }
370 }