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 #include "ptp_media_sync_observer.h"
17
18 #include <chrono>
19 #include <cctype>
20 #include <cstdint>
21 #include <securec.h>
22
23 #include "album_operation_uri.h"
24 #include "datashare_predicates.h"
25 #include "datashare_abs_result_set.h"
26 #include "media_file_uri.h"
27 #include "media_log.h"
28 #include "mtp_data_utils.h"
29 #include "mtp_dfx_reporter.h"
30 #include "mtp_manager.h"
31 #include "photo_album_column.h"
32 #include "ptp_album_handles.h"
33 #include "ptp_special_handles.h"
34 #include "result_set_utils.h"
35 using namespace std;
36
37 namespace OHOS {
38 namespace Media {
39 constexpr int32_t RESERVE_ALBUM = 10;
40 constexpr int32_t PARENT_ID = 0;
41 constexpr int32_t PARENT_ID_IN_MTP = 500000000;
42 constexpr int32_t DELETE_LIMIT_TIME = 5000;
43 constexpr int32_t ERR_NUM = -1;
44 constexpr int32_t MAX_PARCEL_LEN_LIMIT = 5000;
45 const string IS_LOCAL = "2";
46 const std::string HIDDEN_ALBUM = ".hiddenAlbum";
47 const string INVALID_FILE_ID = "-1";
48 constexpr uint64_t DELAY_FOR_MOVING_MS = 5000;
49 constexpr uint64_t DELAY_FOR_BURST_MS = 12000;
50 // LCOV_EXCL_START
startsWith(const std::string & str,const std::string & prefix)51 bool startsWith(const std::string& str, const std::string& prefix)
52 {
53 bool cond = (prefix.size() > str.size() || prefix.empty() || str.empty());
54 CHECK_AND_RETURN_RET_LOG(!cond, false, "MtpMediaLibrary::StartsWith prefix size error");
55
56 for (size_t i = 0; i < prefix.size(); ++i) {
57 CHECK_AND_RETURN_RET(str[i] == prefix[i], false);
58 }
59 return true;
60 }
61
GetParentId()62 static inline int32_t GetParentId()
63 {
64 return MtpManager::GetInstance().IsMtpMode() ? PARENT_ID_IN_MTP : PARENT_ID;
65 }
66
FindRealHandle(const uint32_t realHandle)67 static bool FindRealHandle(const uint32_t realHandle)
68 {
69 auto specialHandles = PtpSpecialHandles::GetInstance();
70 CHECK_AND_RETURN_RET_LOG(specialHandles != nullptr, false, "specialHandles is nullptr");
71 return specialHandles->FindRealHandle(realHandle);
72 }
SendEventPackets(uint32_t objectHandle,uint16_t eventCode)73 void MediaSyncObserver::SendEventPackets(uint32_t objectHandle, uint16_t eventCode)
74 {
75 EventMtp event;
76 event.length = MTP_CONTAINER_HEADER_SIZE + sizeof(objectHandle);
77 vector<uint8_t> outBuffer;
78 MtpPacketTool::PutUInt32(outBuffer, event.length);
79 MtpPacketTool::PutUInt16(outBuffer, EVENT_CONTAINER_TYPE);
80 MtpPacketTool::PutUInt16(outBuffer, eventCode);
81 CHECK_AND_RETURN_LOG(context_ != nullptr, "Mtp Ptp context is nullptr");
82 MtpPacketTool::PutUInt32(outBuffer, context_->transactionID);
83 MtpPacketTool::PutUInt32(outBuffer, objectHandle);
84 MEDIA_DEBUG_LOG("MtpMediaLibrary album [%{public}d]", objectHandle);
85
86 event.data = outBuffer;
87 CHECK_AND_RETURN_LOG(context_->mtpDriver != nullptr, "Mtp Ptp mtpDriver is nullptr");
88 auto startTime = std::chrono::high_resolution_clock::now();
89 int32_t result = context_->mtpDriver->WriteEvent(event);
90 auto endTime = std::chrono::high_resolution_clock::now();
91 std::chrono::duration<uint16_t, std::milli> duration =
92 std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
93 MtpDfxReporter::GetInstance().DoSendResponseResultDfxReporter(eventCode, result,
94 duration.count(), OperateMode::writemode);
95 }
96
SendEventPacketAlbum(uint32_t objectHandle,uint16_t eventCode)97 void MediaSyncObserver::SendEventPacketAlbum(uint32_t objectHandle, uint16_t eventCode)
98 {
99 EventMtp event;
100 event.length = MTP_CONTAINER_HEADER_SIZE + sizeof(objectHandle);
101 vector<uint8_t> outBuffer;
102 MtpPacketTool::PutUInt32(outBuffer, event.length);
103 MtpPacketTool::PutUInt16(outBuffer, EVENT_CONTAINER_TYPE);
104 MtpPacketTool::PutUInt16(outBuffer, eventCode);
105 MtpPacketTool::PutUInt32(outBuffer, context_->transactionID);
106 MtpPacketTool::PutUInt32(outBuffer, objectHandle);
107
108 event.data = outBuffer;
109 MEDIA_DEBUG_LOG("MtpMediaLibrary album [%{public}d]", objectHandle);
110 CHECK_AND_RETURN_LOG(context_ != nullptr, "Mtp Ptp context is nullptr");
111 CHECK_AND_RETURN_LOG(context_->mtpDriver != nullptr, "Mtp Ptp mtpDriver is nullptr");
112 auto startTime = std::chrono::high_resolution_clock::now();
113 int32_t result = context_->mtpDriver->WriteEvent(event);
114 auto endTime = std::chrono::high_resolution_clock::now();
115 std::chrono::duration<uint16_t, std::milli> duration =
116 std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
117 MtpDfxReporter::GetInstance().DoSendResponseResultDfxReporter(eventCode, result,
118 duration.count(), OperateMode::writemode);
119 }
120
IsNumber(const string & str)121 static bool IsNumber(const string& str)
122 {
123 CHECK_AND_RETURN_RET_LOG(!str.empty(), false, "IsNumber input is empty");
124 for (char const& c : str) {
125 CHECK_AND_RETURN_RET(isdigit(c) != 0, false);
126 }
127 return true;
128 }
129
GetHandlesFromPhotosInfoBurstKeys(vector<std::string> & handles)130 vector<int32_t> MediaSyncObserver::GetHandlesFromPhotosInfoBurstKeys(vector<std::string> &handles)
131 {
132 vector<int32_t> handlesResult;
133 CHECK_AND_RETURN_RET_LOG(dataShareHelper_ != nullptr, handlesResult,
134 "Mtp GetHandlesFromPhotosInfoBurstKeys fail to get datasharehelper");
135 CHECK_AND_RETURN_RET_LOG(!handles.empty(), handlesResult, "Mtp handles have no elements!");
136 Uri uri(PAH_QUERY_PHOTO);
137 vector<string> columns;
138 columns.push_back(PhotoColumn::PHOTO_BURST_KEY);
139 DataShare::DataSharePredicates predicates;
140 predicates.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
141 to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
142 predicates.IsNotNull(PhotoColumn::PHOTO_BURST_KEY);
143 predicates.In(PhotoColumn::MEDIA_ID, handles);
144 shared_ptr<DataShare::DataShareResultSet> resultSet = dataShareHelper_->Query(uri, predicates, columns);
145
146 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr,
147 handlesResult, "Mtp GetHandlesFromPhotosInfoBurstKeys fail to get PHOTO_BURST_KEY");
148 CHECK_AND_RETURN_RET_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK,
149 handlesResult, "Mtp GetHandlesFromPhotosInfoBurstKeys have no PHOTO_BURST_KEY");
150 vector<string> burstKey;
151 do {
152 burstKey.push_back(GetStringVal(PhotoColumn::PHOTO_BURST_KEY, resultSet));
153 } while (resultSet->GoToNextRow() == NativeRdb::E_OK);
154
155 CHECK_AND_RETURN_RET_LOG(!burstKey.empty(), handlesResult,
156 "Mtp GetHandlesFromPhotosInfoBurstKeys burstKey is empty");
157
158 columns.clear();
159 columns.push_back(PhotoColumn::MEDIA_ID);
160 DataShare::DataSharePredicates predicatesHandles;
161 predicatesHandles.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
162 to_string(static_cast<int32_t>(BurstCoverLevelType::MEMBER)));
163 predicatesHandles.IsNotNull(PhotoColumn::PHOTO_BURST_KEY);
164 predicatesHandles.In(PhotoColumn::PHOTO_BURST_KEY, burstKey);
165 resultSet = dataShareHelper_->Query(uri, predicatesHandles, columns);
166 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr,
167 handlesResult, "Mtp GetHandlesFromPhotosInfoBurstKeys fail to get handles");
168 CHECK_AND_RETURN_RET_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK,
169 handlesResult, "Mtp GetHandlesFromPhotosInfoBurstKeys have no handles");
170 do {
171 string file_id = GetStringVal(PhotoColumn::MEDIA_ID, resultSet);
172 if (!IsNumber(file_id)) {
173 MEDIA_ERR_LOG("Mtp GetHandlesFromPhotosInfoBurstKeys id is incorrect ");
174 continue;
175 }
176 handlesResult.push_back(atoi(file_id.c_str()));
177 } while (resultSet->GoToNextRow() == NativeRdb::E_OK);
178 return handlesResult;
179 }
180
GetAllDeleteHandles()181 vector<string> MediaSyncObserver::GetAllDeleteHandles()
182 {
183 vector<string> handlesResult;
184 CHECK_AND_RETURN_RET_LOG(dataShareHelper_ != nullptr, handlesResult,
185 "Mtp GetAllDeleteHandles fail to get datasharehelper");
186 Uri uri(PAH_QUERY_PHOTO);
187 vector<string> columns;
188 columns.push_back(MediaColumn::MEDIA_ID);
189 DataShare::DataSharePredicates predicates;
190 auto now = std::chrono::system_clock::now();
191 auto now_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
192 auto new_timestamp = now_milliseconds - DELETE_LIMIT_TIME;
193 predicates.GreaterThan(MediaColumn::MEDIA_DATE_TRASHED, to_string(new_timestamp));
194 shared_ptr<DataShare::DataShareResultSet> resultSet = dataShareHelper_->Query(uri, predicates, columns);
195
196 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr,
197 handlesResult, "Mtp GetAllDeleteHandles fail to get PHOTO_ALL_DELETE_KEY");
198 CHECK_AND_RETURN_RET_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK,
199 handlesResult, "Mtp GetAllDeleteHandles have no PHOTO_ALL_DELETE_KEY");
200 do {
201 string file_id = GetStringVal(PhotoColumn::MEDIA_ID, resultSet);
202 handlesResult.push_back(file_id);
203 } while (resultSet->GoToNextRow() == NativeRdb::E_OK);
204 return handlesResult;
205 }
206
StartDelayInfoThread()207 void MediaSyncObserver::StartDelayInfoThread()
208 {
209 CHECK_AND_RETURN_LOG(!isRunningDelay_.load(), "MediaSyncObserver delay thread is already running");
210 isRunningDelay_.store(true);
211 delayThread_ = std::thread([&] { DelayInfoThread(); });
212 }
213
StopDelayInfoThread()214 void MediaSyncObserver::StopDelayInfoThread()
215 {
216 isRunningDelay_.store(false);
217 cvDelay_.notify_all();
218 if (delayThread_.joinable()) {
219 delayThread_.join();
220 }
221 }
222
DelayInfoThread()223 void MediaSyncObserver::DelayInfoThread()
224 {
225 while (isRunningDelay_.load()) {
226 DelayInfo delayInfo;
227 {
228 std::unique_lock<std::mutex> lock(mutexDelay_);
229 cvDelay_.wait(lock, [&] { return !delayQueue_.empty() || !isRunningDelay_.load(); });
230 if (!isRunningDelay_.load()) {
231 std::queue<DelayInfo>().swap(delayQueue_);
232 MEDIA_INFO_LOG("delay thread is stopped");
233 break;
234 }
235 delayInfo = delayQueue_.front();
236 delayQueue_.pop();
237 }
238 auto now = std::chrono::steady_clock::now();
239 if (now < delayInfo.tp) {
240 std::this_thread::sleep_until(delayInfo.tp);
241 }
242 if (delayInfo.objectHandle > COMMON_MOVING_OFFSET) {
243 SendEventPackets(delayInfo.objectHandle, delayInfo.eventCode);
244 } else {
245 AddBurstPhotoHandle(delayInfo.burstKey);
246 }
247 SendEventPacketAlbum(delayInfo.objectHandleAlbum, delayInfo.eventCodeAlbum);
248 }
249 }
250
AddBurstPhotoHandle(string burstKey)251 void MediaSyncObserver::AddBurstPhotoHandle(string burstKey)
252 {
253 if (needAddMemberBurstKeys_.find(burstKey) == needAddMemberBurstKeys_.end()) {
254 return;
255 }
256 Uri uri(PAH_QUERY_PHOTO);
257 vector<string> columns;
258 DataShare::DataSharePredicates predicates;
259 shared_ptr<DataShare::DataShareResultSet> resultSet;
260 columns.push_back(MediaColumn::MEDIA_ID);
261 predicates.EqualTo(PhotoColumn::PHOTO_BURST_KEY, burstKey);
262 predicates.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
263 to_string(static_cast<int32_t>(BurstCoverLevelType::MEMBER)));
264 predicates.NotEqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
265 predicates.EqualTo(MediaColumn::MEDIA_DATE_TRASHED, "0");
266 predicates.EqualTo(MediaColumn::MEDIA_TIME_PENDING, "0");
267 predicates.EqualTo(MediaColumn::MEDIA_HIDDEN, "0");
268 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp AddPhotoHandle dataShareHelper_ is nullptr");
269 resultSet = dataShareHelper_->Query(uri, predicates, columns);
270 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp AddPhotoHandle fail to get handles");
271 CHECK_AND_RETURN_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK,
272 "Mtp AddBurstPhotoHandle failed to get resultSet");
273 do {
274 int32_t fileId = GetInt32Val(MediaColumn::MEDIA_ID, resultSet);
275 SendEventPackets(fileId + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
276 } while (resultSet->GoToNextRow() == NativeRdb::E_OK);
277 needAddMemberBurstKeys_.erase(burstKey);
278 }
279
AddDelayInfo(uint32_t handle,uint32_t ownerAlbumId,const string & burstKey,uint64_t delayMs)280 void MediaSyncObserver::AddDelayInfo(uint32_t handle, uint32_t ownerAlbumId, const string &burstKey, uint64_t delayMs)
281 {
282 DelayInfo delayInfo = {
283 .objectHandle = handle,
284 .eventCode = MTP_EVENT_OBJECT_ADDED_CODE,
285 .objectHandleAlbum = ownerAlbumId,
286 .eventCodeAlbum = MTP_EVENT_OBJECT_INFO_CHANGED_CODE,
287 .burstKey = burstKey,
288 .tp = std::chrono::steady_clock::now() + std::chrono::milliseconds(delayMs)
289 };
290 {
291 std::lock_guard<std::mutex> lock(mutexDelay_);
292 if (isRunningDelay_.load()) {
293 delayQueue_.push(delayInfo);
294 }
295 }
296 cvDelay_.notify_all();
297 }
298
AddPhotoHandle(int32_t handle)299 void MediaSyncObserver::AddPhotoHandle(int32_t handle)
300 {
301 if (FindRealHandle(handle + COMMON_PHOTOS_OFFSET)) {
302 return;
303 }
304 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp AddPhotoHandle fail to get datasharehelper");
305 Uri uri(PAH_QUERY_PHOTO);
306 vector<string> columns;
307 DataShare::DataSharePredicates predicates;
308 shared_ptr<DataShare::DataShareResultSet> resultSet;
309 columns.push_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
310 columns.push_back(PhotoColumn::PHOTO_SUBTYPE);
311 columns.push_back(PhotoColumn::MOVING_PHOTO_EFFECT_MODE);
312 columns.push_back(PhotoColumn::PHOTO_BURST_KEY);
313 predicates.EqualTo(MediaColumn::MEDIA_ID, to_string(handle));
314 predicates.NotEqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
315 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp AddPhotoHandle dataShareHelper_ is nullptr");
316 resultSet = dataShareHelper_->Query(uri, predicates, columns);
317 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp AddPhotoHandle fail to get handles");
318 CHECK_AND_RETURN_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK, "Mtp AddPhotoHandle failed to get resultSet");
319 SendEventPackets(handle + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
320 int32_t ownerAlbumId = GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet);
321 int32_t subtype = GetInt32Val(PhotoColumn::PHOTO_SUBTYPE, resultSet);
322 int32_t effectMode = GetInt32Val(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet);
323 if (MtpDataUtils::IsMtpMovingPhoto(subtype, effectMode)) {
324 AddDelayInfo(handle + COMMON_MOVING_OFFSET, ownerAlbumId, "", DELAY_FOR_MOVING_MS);
325 } else if (subtype == static_cast<int32_t>(PhotoSubType::BURST)) {
326 string burstKey = GetStringVal(PhotoColumn::PHOTO_BURST_KEY, resultSet);
327 needAddMemberBurstKeys_.insert(burstKey);
328 AddBurstPhotoHandle(previousAddedBurstKey_);
329 previousAddedBurstKey_ = burstKey;
330 AddDelayInfo(handle + COMMON_PHOTOS_OFFSET, ownerAlbumId, burstKey, DELAY_FOR_BURST_MS);
331 }
332 auto albumHandles = PtpAlbumHandles::GetInstance();
333 if (!albumHandles->FindHandle(ownerAlbumId)) {
334 albumHandles->AddHandle(ownerAlbumId);
335 SendEventPacketAlbum(ownerAlbumId, MTP_EVENT_OBJECT_ADDED_CODE);
336 SendEventPacketAlbum(ownerAlbumId, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
337 SendEventPacketAlbum(GetParentId(), MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
338 }
339 }
340
GetAddEditPhotoHandles(int32_t handle)341 void MediaSyncObserver::GetAddEditPhotoHandles(int32_t handle)
342 {
343 auto specialHandles = PtpSpecialHandles::GetInstance();
344 CHECK_AND_RETURN_LOG(specialHandles != nullptr, "specialHandles is nullptr");
345 if (FindRealHandle(handle + COMMON_PHOTOS_OFFSET)) {
346 uint32_t actualHandle = specialHandles->HandleConvertToDeleted(handle + COMMON_PHOTOS_OFFSET);
347 SendEventPackets(actualHandle, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
348 return;
349 }
350 vector<int32_t> handlesResult;
351 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp GetAddEditPhotoHandles fail to get datasharehelper");
352 Uri uri(PAH_QUERY_PHOTO);
353 vector<string> columns;
354 DataShare::DataSharePredicates predicates;
355 columns.push_back(PhotoColumn::PHOTO_SUBTYPE);
356 columns.push_back(PhotoColumn::MOVING_PHOTO_EFFECT_MODE);
357 columns.push_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
358 predicates.EqualTo(MediaColumn::MEDIA_ID, to_string(handle));
359 predicates.NotEqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
360 predicates.EqualTo(MediaColumn::MEDIA_DATE_TRASHED, "0");
361 predicates.EqualTo(MediaColumn::MEDIA_TIME_PENDING, "0");
362 predicates.EqualTo(MediaColumn::MEDIA_HIDDEN, "0");
363 predicates.EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(false));
364 shared_ptr<DataShare::DataShareResultSet> resultSet = dataShareHelper_->Query(uri, predicates, columns);
365 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp GetAddEditPhotoHandles fail to get updated photo");
366 CHECK_AND_RETURN_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK, "Mtp GetAddEditPhotoHandles have no photo");
367 SendEventPackets(handle + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
368 int32_t ownerAlbumId = GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet);
369 int32_t subType = GetInt32Val(PhotoColumn::PHOTO_SUBTYPE, resultSet);
370 int32_t effectMode = GetInt32Val(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet);
371 if (MtpDataUtils::IsMtpMovingPhoto(subType, effectMode)) {
372 SendEventPackets(handle + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
373 SendEventPackets(handle + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
374 } else if (subType == static_cast<int32_t>(PhotoSubType::DEFAULT)) {
375 SendEventPackets(handle + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
376 }
377 auto albumHandles = PtpAlbumHandles::GetInstance();
378 if (!albumHandles->FindHandle(ownerAlbumId)) {
379 albumHandles->AddHandle(ownerAlbumId);
380 SendEventPacketAlbum(ownerAlbumId, MTP_EVENT_OBJECT_ADDED_CODE);
381 SendEventPacketAlbum(ownerAlbumId, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
382 SendEventPacketAlbum(GetParentId(), MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
383 }
384 }
385
GetAddEditAlbumHandle(int32_t handle)386 int32_t MediaSyncObserver::GetAddEditAlbumHandle(int32_t handle)
387 {
388 Uri uri(PAH_QUERY_PHOTO);
389 vector<string> columns;
390 DataShare::DataSharePredicates predicates;
391 columns.push_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
392 predicates.EqualTo(MediaColumn::MEDIA_ID, to_string(handle));
393 CHECK_AND_RETURN_RET_LOG(dataShareHelper_ != nullptr,
394 ERR_NUM, "Mtp GetAddEditAlbumHandle dataShareHelper_ is nullptr");
395 shared_ptr<DataShare::DataShareResultSet> resultSet = dataShareHelper_->Query(uri, predicates, columns);
396 CHECK_AND_RETURN_RET_LOG(resultSet != nullptr,
397 ERR_NUM, "Mtp GetAddEditAlbumHandle fail to get album id");
398 CHECK_AND_RETURN_RET_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK,
399 ERR_NUM, "Mtp GetAddEditAlbumHandle have no row");
400 int32_t album_id = GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet);
401 return album_id;
402 }
403
SendPhotoRemoveEvent(std::string & suffixString)404 void MediaSyncObserver::SendPhotoRemoveEvent(std::string &suffixString)
405 {
406 vector<string> allDeletedHandles;
407 vector<int32_t> handles;
408 auto specialHandles = PtpSpecialHandles::GetInstance();
409 CHECK_AND_RETURN_LOG(specialHandles != nullptr, "specialHandles is nullptr");
410
411 if (suffixString.empty()) {
412 allDeletedHandles = GetAllDeleteHandles();
413 for (auto deleteHandle : allDeletedHandles) {
414 if (!IsNumber(deleteHandle)) {
415 MEDIA_ERR_LOG("Mtp SendPhotoRemoveEvent deleteHandle is incorrect ");
416 continue;
417 }
418 uint32_t fileId = static_cast<uint32_t>(atoi(deleteHandle.c_str()));
419 if (FindRealHandle(fileId + COMMON_PHOTOS_OFFSET)) {
420 uint32_t actualHandle = specialHandles->HandleConvertToDeleted(fileId + COMMON_PHOTOS_OFFSET);
421 SendEventPackets(actualHandle, MTP_EVENT_OBJECT_REMOVED_CODE);
422 continue;
423 }
424 SendEventPackets(atoi(deleteHandle.c_str()) + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
425 SendEventPackets(atoi(deleteHandle.c_str()) + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
426 }
427 } else {
428 CHECK_AND_RETURN_LOG(IsNumber(suffixString), "Mtp SendPhotoRemoveEvent deleteHandle is incorrect ");
429 uint32_t fileId = static_cast<uint32_t>(atoi(suffixString.c_str()));
430 if (FindRealHandle(fileId + COMMON_PHOTOS_OFFSET)) {
431 uint32_t actualHandle = specialHandles->HandleConvertToDeleted(fileId + COMMON_PHOTOS_OFFSET);
432 SendEventPackets(actualHandle, MTP_EVENT_OBJECT_REMOVED_CODE);
433 return;
434 }
435 bool photoDeleted = specialHandles->FindDeletedHandle(fileId + COMMON_PHOTOS_OFFSET);
436 bool movingDeleted = specialHandles->FindDeletedHandle(fileId + COMMON_MOVING_OFFSET);
437 if (!photoDeleted) {
438 SendEventPackets(fileId + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
439 }
440 if (!movingDeleted) {
441 SendEventPackets(fileId + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
442 }
443 if (photoDeleted || movingDeleted) {
444 return;
445 }
446 allDeletedHandles.push_back(suffixString);
447 }
448 handles = GetHandlesFromPhotosInfoBurstKeys(allDeletedHandles);
449 for (const auto handle : handles) {
450 SendEventPackets(handle + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
451 }
452 }
453
SendPhotoEvent(ChangeType changeType,string suffixString)454 void MediaSyncObserver::SendPhotoEvent(ChangeType changeType, string suffixString)
455 {
456 if (!suffixString.empty() && !std::isdigit(suffixString[0])) {
457 return;
458 }
459 if (!suffixString.empty() && atoi(suffixString.c_str()) <= 0) {
460 return;
461 }
462 switch (changeType) {
463 case static_cast<int32_t>(NotifyType::NOTIFY_ADD):
464 MEDIA_DEBUG_LOG("MtpMediaLibrary PHOTO ADD");
465 AddPhotoHandle(atoi(suffixString.c_str()));
466 break;
467 case static_cast<int32_t>(NotifyType::NOTIFY_UPDATE):
468 MEDIA_DEBUG_LOG("MtpMediaLibrary PHOTO UPDATE");
469 SendEventPackets(atoi(suffixString.c_str()) + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
470 GetAddEditPhotoHandles(atoi(suffixString.c_str()));
471 break;
472 case static_cast<int32_t>(NotifyType::NOTIFY_REMOVE):
473 MEDIA_DEBUG_LOG("MtpMediaLibrary PHOTO REMOVE");
474 SendPhotoRemoveEvent(suffixString);
475 break;
476 default:
477 break;
478 }
479 }
480
GetAlbumIdList(std::set<int32_t> & albumIds)481 void MediaSyncObserver::GetAlbumIdList(std::set<int32_t> &albumIds)
482 {
483 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp GetAlbumIdList dataShareHelper_ is nullptr");
484 DataShare::DataSharePredicates predicates;
485 Uri uri(PAH_QUERY_PHOTO_ALBUM);
486 vector<string> columns;
487 columns.push_back(PhotoAlbumColumns::ALBUM_ID);
488 predicates.IsNotNull(MEDIA_DATA_DB_ALBUM_NAME);
489 predicates.NotEqualTo(MEDIA_DATA_DB_ALBUM_NAME, HIDDEN_ALBUM);
490 predicates.BeginWrap();
491 predicates.NotEqualTo(MEDIA_DATA_DB_IS_LOCAL, IS_LOCAL);
492 predicates.Or();
493 predicates.IsNull(MEDIA_DATA_DB_IS_LOCAL);
494 predicates.EndWrap();
495 auto resultSet = dataShareHelper_->Query(uri, predicates, columns);
496 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp GetAlbumIdList Query fail");
497 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
498 albumIds.insert(GetInt32Val(PhotoAlbumColumns::ALBUM_ID, resultSet));
499 }
500 }
501
GetOwnerAlbumIdList(std::set<int32_t> & albumIds)502 void MediaSyncObserver::GetOwnerAlbumIdList(std::set<int32_t> &albumIds)
503 {
504 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp GetOwnerAlbumIdList dataShareHelper_ is nullptr");
505 Uri uri(PAH_QUERY_PHOTO);
506 vector<string> columns;
507 columns.push_back(PhotoColumn::PHOTO_OWNER_ALBUM_ID);
508 DataShare::DataSharePredicates predicates;
509 predicates.NotEqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
510 predicates.EqualTo(MediaColumn::MEDIA_DATE_TRASHED, "0");
511 predicates.EqualTo(MediaColumn::MEDIA_TIME_PENDING, "0");
512 predicates.EqualTo(MediaColumn::MEDIA_HIDDEN, "0");
513 predicates.Distinct();
514 auto resultSet = dataShareHelper_->Query(uri, predicates, columns);
515 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp GetOwnerAlbumIdList Query fail");
516 while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
517 albumIds.insert(GetInt32Val(PhotoColumn::PHOTO_OWNER_ALBUM_ID, resultSet));
518 }
519 }
520
SendEventToPTP(ChangeType changeType,const std::vector<int32_t> & albumIds)521 void MediaSyncObserver::SendEventToPTP(ChangeType changeType, const std::vector<int32_t> &albumIds)
522 {
523 std::vector<int32_t> removeIds;
524 std::set<int32_t> localAlbumIds;
525 auto albumHandles = PtpAlbumHandles::GetInstance();
526 auto specialHandles = PtpSpecialHandles::GetInstance();
527 CHECK_AND_RETURN_LOG(albumHandles != nullptr, "albumHandles is nullptr");
528 CHECK_AND_RETURN_LOG(specialHandles != nullptr, "specialHandles is nullptr");
529 switch (changeType) {
530 case static_cast<int32_t>(NotifyType::NOTIFY_ADD):
531 case static_cast<int32_t>(NotifyType::NOTIFY_UPDATE):
532 MEDIA_DEBUG_LOG("MtpMediaLibrary ALBUM ADD OR UPDATE");
533 GetAlbumIdList(localAlbumIds);
534 GetOwnerAlbumIdList(localAlbumIds);
535 albumHandles->UpdateHandle(localAlbumIds, removeIds);
536 for (auto removeId : removeIds) {
537 if (specialHandles->FindDeletedHandle(removeId)) {
538 continue;
539 }
540 albumHandles->RemoveHandle(removeId);
541 SendEventPacketAlbum(removeId, MTP_EVENT_OBJECT_REMOVED_CODE);
542 }
543 for (auto albumId : albumIds) {
544 if (localAlbumIds.count(albumId) == 0) {
545 MEDIA_DEBUG_LOG("MtpMediaLibrary ignore cloud albumId:%{public}d", albumId);
546 continue;
547 }
548 if (!albumHandles->FindHandle(albumId) && !FindRealHandle(albumId)) {
549 albumHandles->AddHandle(albumId);
550 SendEventPacketAlbum(albumId, MTP_EVENT_OBJECT_ADDED_CODE);
551 }
552 SendEventPacketAlbum(albumId, MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
553 }
554 SendEventPacketAlbum(GetParentId(), MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
555 break;
556 case static_cast<int32_t>(NotifyType::NOTIFY_REMOVE):
557 MEDIA_DEBUG_LOG("MtpMediaLibrary ALBUM REMOVE");
558 for (auto albumId : albumIds) {
559 if (specialHandles->FindDeletedHandle(albumId)) {
560 continue;
561 }
562 albumHandles->RemoveHandle(specialHandles->HandleConvertToDeleted(albumId));
563 SendEventPacketAlbum(specialHandles->HandleConvertToDeleted(albumId), MTP_EVENT_OBJECT_REMOVED_CODE);
564 }
565 SendEventPacketAlbum(GetParentId(), MTP_EVENT_OBJECT_INFO_CHANGED_CODE);
566 break;
567 default:
568 break;
569 }
570 }
571
ParseNotifyData(const ChangeInfo & changeInfo,vector<string> & fileIds)572 bool MediaSyncObserver::ParseNotifyData(const ChangeInfo &changeInfo, vector<string> &fileIds)
573 {
574 if (changeInfo.data_ == nullptr || changeInfo.size_ <= 0 ||
575 changeInfo.size_ > std::numeric_limits<uint32_t>::max()) {
576 MEDIA_DEBUG_LOG("changeInfo.data_ is null or changeInfo.size_ is invalid");
577 return false;
578 }
579 MEDIA_DEBUG_LOG("changeInfo.size_ is %{public}d.", changeInfo.size_);
580 uint8_t *parcelData = static_cast<uint8_t *>(malloc(changeInfo.size_));
581 CHECK_AND_RETURN_RET_LOG(parcelData != nullptr, false, "parcelData malloc failed");
582 if (memcpy_s(parcelData, changeInfo.size_, changeInfo.data_, changeInfo.size_) != 0) {
583 MEDIA_ERR_LOG("parcelData copy parcel data failed");
584 free(parcelData);
585 return false;
586 }
587 shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
588 // parcel析构函数中会free掉parcelData,成功调用ParseFrom后不可进行free(parcelData)
589 if (!parcel->ParseFrom(reinterpret_cast<uintptr_t>(parcelData), changeInfo.size_)) {
590 MEDIA_ERR_LOG("Parse parcelData failed");
591 free(parcelData);
592 return false;
593 }
594 uint32_t len = 0;
595 CHECK_AND_RETURN_RET_LOG(!(!parcel->ReadUint32(len)), false, "Failed to read sub uri list length");
596 MEDIA_DEBUG_LOG("read sub uri list length: %{public}u .", len);
597 CHECK_AND_RETURN_RET_LOG(len <= MAX_PARCEL_LEN_LIMIT, false, "len length exceed the limit.");
598 for (uint32_t i = 0; i < len; i++) {
599 string subUri = parcel->ReadString();
600 CHECK_AND_RETURN_RET_LOG(!subUri.empty(), false, "Failed to read sub uri");
601 MEDIA_DEBUG_LOG("notify data subUri string %{public}s.", subUri.c_str());
602 MediaFileUri fileUri(subUri);
603 string fileId = fileUri.GetFileId();
604 if (!IsNumber(fileId)) {
605 MEDIA_ERR_LOG("Failed to read sub uri fileId");
606 continue;
607 }
608 fileIds.push_back(fileId);
609 }
610 return true;
611 }
612
HandleMovePhotoEvent(const ChangeInfo & changeInfo)613 void MediaSyncObserver::HandleMovePhotoEvent(const ChangeInfo &changeInfo)
614 {
615 if (changeInfo.changeType_ != static_cast<int32_t>(NotifyType::NOTIFY_ADD)) {
616 return;
617 }
618 vector<string> fileIds;
619 bool errCode = ParseNotifyData(changeInfo, fileIds);
620 if (!errCode || fileIds.empty()) {
621 MEDIA_DEBUG_LOG("parse changInfo data failed or have no fileId");
622 return;
623 }
624 Uri uri(PAH_QUERY_PHOTO);
625 vector<string> columns;
626 DataShare::DataSharePredicates predicates;
627 shared_ptr<DataShare::DataShareResultSet> resultSet;
628 columns.push_back(MediaColumn::MEDIA_ID);
629 columns.push_back(PhotoColumn::PHOTO_SUBTYPE);
630 columns.push_back(PhotoColumn::MOVING_PHOTO_EFFECT_MODE);
631 predicates.In(MediaColumn::MEDIA_ID, fileIds);
632 predicates.NotEqualTo(PhotoColumn::PHOTO_POSITION, to_string(static_cast<int32_t>(PhotoPositionType::CLOUD)));
633 CHECK_AND_RETURN_LOG(dataShareHelper_ != nullptr, "Mtp dataShareHelper_ is nullptr");
634 resultSet = dataShareHelper_->Query(uri, predicates, columns);
635 CHECK_AND_RETURN_LOG(resultSet != nullptr, "Mtp get handles failed");
636 CHECK_AND_RETURN_LOG(resultSet->GoToFirstRow() == NativeRdb::E_OK, "Mtp get resultSet failed");
637 do {
638 int32_t fileId = GetInt32Val(MediaColumn::MEDIA_ID, resultSet);
639 int32_t subtype = GetInt32Val(PhotoColumn::PHOTO_SUBTYPE, resultSet);
640 int32_t effectMode = GetInt32Val(PhotoColumn::MOVING_PHOTO_EFFECT_MODE, resultSet);
641 if (FindRealHandle(fileId + COMMON_PHOTOS_OFFSET)) {
642 continue;
643 }
644 SendEventPackets(fileId + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
645 SendEventPackets(fileId + COMMON_PHOTOS_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
646 if (MtpDataUtils::IsMtpMovingPhoto(subtype, effectMode)) {
647 SendEventPackets(fileId + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_REMOVED_CODE);
648 SendEventPackets(fileId + COMMON_MOVING_OFFSET, MTP_EVENT_OBJECT_ADDED_CODE);
649 }
650 } while (resultSet->GoToNextRow() == NativeRdb::E_OK);
651 }
652
OnChangeEx(const ChangeInfo & changeInfo)653 void MediaSyncObserver::OnChangeEx(const ChangeInfo &changeInfo)
654 {
655 std::vector<int32_t> albumIds;
656 std::string PhotoPrefix = PhotoColumn::PHOTO_URI_PREFIX;
657 std::string PhotoAlbumPrefix = PhotoAlbumColumns::ALBUM_URI_PREFIX;
658 MEDIA_DEBUG_LOG("MtpMediaLibrary changeType [%{public}d]", changeInfo.changeType_);
659 for (const auto& it : changeInfo.uris_) {
660 std::string uri = it.ToString();
661 MediaFileUri fileUri(uri);
662 MEDIA_DEBUG_LOG("MtpMediaLibrary uris [%{public}s]", uri.c_str());
663 if (startsWith(uri, PhotoPrefix)) {
664 std::string fileId = fileUri.GetFileId();
665 if (fileId.compare(INVALID_FILE_ID) == 0) {
666 fileId = "";
667 }
668 if (fileId.empty() && changeInfo.changeType_ != static_cast<int32_t>(NotifyType::NOTIFY_REMOVE)) {
669 MEDIA_DEBUG_LOG("MtpMediaLibrary suffixString is empty");
670 continue;
671 }
672 MEDIA_DEBUG_LOG("MtpMediaLibrary suffixString [%{public}s]", fileId.c_str());
673 SendPhotoEvent(changeInfo.changeType_, fileId);
674 } else if (startsWith(uri, PhotoAlbumPrefix)) {
675 std::string albumId = fileUri.GetFileId();
676 MEDIA_DEBUG_LOG("MtpMediaLibrary suffixString [%{public}s]", albumId.c_str());
677 if (!IsNumber(albumId)) {
678 continue;
679 }
680 int32_t albumIdNum = atoi(albumId.c_str());
681 if (albumIdNum <= RESERVE_ALBUM) {
682 continue;
683 }
684 albumIds.push_back(albumIdNum);
685 }
686 }
687
688 if (!albumIds.empty()) {
689 HandleMovePhotoEvent(changeInfo);
690 SendEventToPTP(changeInfo.changeType_, albumIds);
691 }
692 }
693
OnChange(const ChangeInfo & changeInfo)694 void MediaSyncObserver::OnChange(const ChangeInfo &changeInfo)
695 {
696 CHECK_AND_RETURN_LOG(isRunning_.load(), "MediaSyncObserver::OnChange thread is not running");
697 {
698 std::lock_guard<std::mutex> lock(mutex_);
699 ChangeInfo changeInfoCopy = changeInfo;
700 if (changeInfo.data_ != nullptr && changeInfo.size_ > 0 &&
701 changeInfo.size_ <= std::numeric_limits<uint32_t>::max()) {
702 changeInfoCopy.data_ = malloc(changeInfo.size_);
703 CHECK_AND_RETURN_LOG(changeInfoCopy.data_ != nullptr, "changeInfoCopy.data_ is nullptr.");
704 if (memcpy_s(const_cast<void*>(changeInfoCopy.data_),
705 changeInfo.size_, changeInfo.data_, changeInfo.size_) != 0) {
706 MEDIA_ERR_LOG("changeInfoCopy copy data failed");
707 free(const_cast<void*>(changeInfoCopy.data_));
708 changeInfoCopy.data_ = nullptr;
709 return;
710 }
711 }
712 changeInfoQueue_.push(changeInfoCopy);
713 }
714 cv_.notify_one();
715 }
716
StartNotifyThread()717 void MediaSyncObserver::StartNotifyThread()
718 {
719 MEDIA_INFO_LOG("start notify thread");
720 CHECK_AND_RETURN_LOG(!isRunning_.load(), "MediaSyncObserver notify thread is already running");
721 isRunning_.store(true);
722 notifythread_ = std::thread([this] {this->ChangeNotifyThread();});
723 StartDelayInfoThread();
724 }
725
StopNotifyThread()726 void MediaSyncObserver::StopNotifyThread()
727 {
728 MEDIA_INFO_LOG("stop notify thread");
729 StopDelayInfoThread();
730 isRunning_.store(false);
731 cv_.notify_all();
732 {
733 std::lock_guard<std::mutex> lock(mutex_);
734 while (!changeInfoQueue_.empty()) {
735 ChangeInfo changeInfo = changeInfoQueue_.front();
736 changeInfoQueue_.pop();
737 if (changeInfo.data_ != nullptr) {
738 free(const_cast<void*>(changeInfo.data_));
739 changeInfo.data_ = nullptr;
740 }
741 }
742 }
743 if (notifythread_.joinable()) {
744 notifythread_.join();
745 }
746 }
747
ChangeNotifyThread()748 void MediaSyncObserver::ChangeNotifyThread()
749 {
750 while (isRunning_.load()) {
751 ChangeInfo changeInfo;
752 {
753 std::unique_lock<std::mutex> lock(mutex_);
754 cv_.wait(lock, [this] { return !changeInfoQueue_.empty() || !isRunning_.load(); });
755 if (!isRunning_.load()) {
756 MEDIA_INFO_LOG("notify thread is stopped");
757 break;
758 }
759 changeInfo = changeInfoQueue_.front();
760 changeInfoQueue_.pop();
761 }
762 OnChangeEx(changeInfo);
763 if (changeInfo.data_ != nullptr) {
764 free(const_cast<void*>(changeInfo.data_));
765 changeInfo.data_ = nullptr;
766 }
767 }
768 }
769 // LCOV_EXCL_STOP
770 } // namespace Media
771 } // namespace OHOS