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