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