1 /*
2 * Copyright (C) 2023 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 "FileNotify"
16 #include "medialibrary_notify.h"
17 #include "data_ability_helper_impl.h"
18 #include "media_file_utils.h"
19 #include "media_log.h"
20 #include "medialibrary_command.h"
21 #include "medialibrary_data_manager_utils.h"
22 #include "medialibrary_db_const.h"
23 #include "medialibrary_errno.h"
24 #include "medialibrary_object_utils.h"
25 #include "medialibrary_unistore_manager.h"
26 #include "photo_album_column.h"
27 #include "photo_map_column.h"
28 #include "result_set_utils.h"
29 #include "uri.h"
30
31 using namespace std;
32
33 namespace OHOS::Media {
34 using ChangeType = AAFwk::ChangeInfo::ChangeType;
35 using NotifyDataMap = unordered_map<NotifyType, list<Uri>>;
36 shared_ptr<MediaLibraryNotify> MediaLibraryNotify::instance_;
37 mutex MediaLibraryNotify::mutex_;
38 unordered_map<string, NotifyDataMap> MediaLibraryNotify::nfListMap_ = {};
39 Utils::Timer MediaLibraryNotify::timer_("on_notify");
40 uint32_t MediaLibraryNotify::timerId_ = 0;
41
GetInstance()42 shared_ptr<MediaLibraryNotify> MediaLibraryNotify::GetInstance()
43 {
44 if (instance_ != nullptr) {
45 return instance_;
46 }
47 lock_guard<mutex> lock(mutex_);
48 if (instance_ == nullptr) {
49 instance_ = shared_ptr<MediaLibraryNotify>(new MediaLibraryNotify());
50 if (instance_ == nullptr) {
51 MEDIA_ERR_LOG("GetInstance nullptr");
52 return instance_;
53 }
54 instance_->Init();
55 }
56 return instance_;
57 }
58 MediaLibraryNotify::MediaLibraryNotify() = default;
59
~MediaLibraryNotify()60 MediaLibraryNotify::~MediaLibraryNotify()
61 {
62 timer_.Shutdown();
63 timer_.Unregister(timerId_);
64 };
65
SolveUris(const list<Uri> & uris,Parcel & parcel)66 static bool SolveUris(const list<Uri> &uris, Parcel &parcel)
67 {
68 if (uris.size() > numeric_limits<uint32_t>::max() ||
69 !parcel.WriteUint32(static_cast<uint32_t>(uris.size()))) {
70 MEDIA_ERR_LOG("Failed to write uri list length, list size = %{private}zu", uris.size());
71 return false;
72 }
73 for (auto const &uri : uris) {
74 if (!parcel.WriteString(uri.ToString())) {
75 MEDIA_ERR_LOG("Failed to write strUri uri = %{private}s", uri.ToString().c_str());
76 return false;
77 }
78 }
79 return true;
80 }
81
SendAlbumSub(const Uri & notifyUri,NotifyType type,list<Uri> & uris)82 static int SendAlbumSub(const Uri ¬ifyUri, NotifyType type, list<Uri> &uris)
83 {
84 Parcel parcel;
85 CHECK_AND_RETURN_RET_LOG(SolveUris(uris, parcel), E_SOLVE_URIS_FAILED, "SolveUris failed");
86 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
87 uintptr_t buf = parcel.GetData();
88 if (parcel.GetDataSize() == 0) {
89 MEDIA_ERR_LOG("NotifyChangeExt parcel.GetDataSize failed");
90 return E_PARCEL_GET_SIZE_FAILED;
91 }
92 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
93 int ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
94 if (ret != 0) {
95 MEDIA_ERR_LOG("Parcel data copy failed, err = %{public}d", ret);
96 }
97 ChangeType changeType;
98 if (type == NotifyType::NOTIFY_ALBUM_ADD_ASSERT) {
99 changeType = ChangeType::INSERT;
100 } else {
101 changeType = ChangeType::DELETE;
102 }
103 return obsMgrClient->NotifyChangeExt({changeType, {notifyUri}, uBuf, parcel.GetDataSize()});
104 }
105
SolveAlbumUri(const Uri & notifyUri,NotifyType type,list<Uri> & uris)106 static int SolveAlbumUri(const Uri ¬ifyUri, NotifyType type, list<Uri> &uris)
107 {
108 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
109 if ((type == NotifyType::NOTIFY_ALBUM_ADD_ASSERT) || (type == NotifyType::NOTIFY_ALBUM_REMOVE_ASSET)) {
110 return SendAlbumSub(notifyUri, type, uris);
111 } else {
112 return obsMgrClient->NotifyChangeExt({static_cast<ChangeType>(type), uris});
113 }
114 }
115
PushNotifyDataMap(const string & uri,NotifyDataMap notifyDataMap)116 static void PushNotifyDataMap(const string &uri, NotifyDataMap notifyDataMap)
117 {
118 int ret;
119 for (auto &[type, uris] : notifyDataMap) {
120 if (uri.find(PhotoAlbumColumns::ALBUM_URI_PREFIX) != string::npos) {
121 Uri notifyUri = Uri(uri);
122 ret = SolveAlbumUri(notifyUri, type, uris);
123 } else {
124 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
125 ret = obsMgrClient->NotifyChangeExt({static_cast<ChangeType>(type), uris});
126 }
127 if (ret != E_OK) {
128 MEDIA_ERR_LOG("PushNotification failed, errorCode = %{public}d", ret);
129 }
130 }
131 return;
132 }
133
PushNotification()134 static void PushNotification()
135 {
136 unordered_map<string, NotifyDataMap> tmpNfListMap;
137 {
138 lock_guard<mutex> lock(MediaLibraryNotify::mutex_);
139 if (MediaLibraryNotify::nfListMap_.empty()) {
140 return;
141 }
142 MediaLibraryNotify::nfListMap_.swap(tmpNfListMap);
143 MediaLibraryNotify::nfListMap_.clear();
144 }
145 for (auto &[uri, notifyDataMap] : tmpNfListMap) {
146 if (notifyDataMap.empty()) {
147 continue;
148 }
149 PushNotifyDataMap(uri, notifyDataMap);
150 }
151 }
152
AddNotify(const string & srcUri,const string & keyUri,NotifyTaskData * taskData)153 static void AddNotify(const string &srcUri, const string &keyUri, NotifyTaskData* taskData)
154 {
155 NotifyDataMap notifyDataMap;
156 list<Uri> sendUris;
157 Uri uri(srcUri);
158 MEDIA_DEBUG_LOG("AddNotify ,keyUri = %{private}s, uri = %{private}s, "
159 "notifyType = %{private}d", keyUri.c_str(), uri.ToString().c_str(), taskData->notifyType_);
160 lock_guard<mutex> lock(MediaLibraryNotify::mutex_);
161 if (MediaLibraryNotify::nfListMap_.count(keyUri) == 0) {
162 sendUris.emplace_back(uri);
163 notifyDataMap.insert(make_pair(taskData->notifyType_, sendUris));
164 MediaLibraryNotify::nfListMap_.insert(make_pair(keyUri, notifyDataMap));
165 } else {
166 auto iter = MediaLibraryNotify::nfListMap_.find(keyUri);
167 if (iter->second.count(taskData->notifyType_) == 0) {
168 sendUris.emplace_back(uri);
169 iter->second.insert(make_pair(taskData->notifyType_, sendUris));
170 } else {
171 auto haveIter = find_if(
172 iter->second.at(taskData->notifyType_).begin(),
173 iter->second.at(taskData->notifyType_).end(),
174 [uri](const Uri &listUri) { return uri.Equals(listUri); });
175 if (haveIter == iter->second.at(taskData->notifyType_).end()) {
176 iter->second.find(taskData->notifyType_)->second.emplace_back(uri);
177 }
178 }
179 }
180 }
181
GetAlbumUrisById(const string & fileId,list<string> & albumUriList)182 static int32_t GetAlbumUrisById(const string &fileId, list<string> &albumUriList)
183 {
184 auto uniStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
185 MediaLibraryCommand queryAlbumMapCmd(OperationObject::PHOTO_MAP, OperationType::QUERY);
186 queryAlbumMapCmd.GetAbsRdbPredicates()->EqualTo(PhotoMap::ASSET_ID, fileId);
187 auto resultSet = uniStore->Query(queryAlbumMapCmd, {PhotoMap::ALBUM_ID});
188 if (resultSet == nullptr) {
189 MEDIA_ERR_LOG("GetAlbumUrisById failed");
190 return E_INVALID_FILEID;
191 }
192 int32_t count = -1;
193 int32_t ret = resultSet->GetRowCount(count);
194 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to get count");
195 ret = resultSet->GoToFirstRow();
196 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to GoToFirstRow");
197 do {
198 int32_t albumId = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoMap::ALBUM_ID, resultSet,
199 TYPE_INT32));
200 string albumUri = PhotoAlbumColumns::ALBUM_URI_PREFIX + to_string(albumId);
201 albumUriList.emplace_back(albumUri);
202 } while (!resultSet->GoToNextRow());
203 return E_OK;
204 }
205
AddNfListMap(AsyncTaskData * data)206 static void AddNfListMap(AsyncTaskData *data)
207 {
208 if (data == nullptr) {
209 return;
210 }
211 auto* taskData = static_cast<NotifyTaskData*>(data);
212 if ((taskData->notifyType_ == NotifyType::NOTIFY_ALBUM_ADD_ASSERT) ||
213 (taskData->notifyType_ == NotifyType::NOTIFY_ALBUM_REMOVE_ASSET)) {
214 if (taskData->albumId_ > 0) {
215 AddNotify(taskData->uri_,
216 PhotoAlbumColumns::ALBUM_URI_PREFIX + to_string(taskData->albumId_), taskData);
217 } else {
218 list<string> albumUriList;
219 string id = MediaLibraryDataManagerUtils::GetIdFromUri(taskData->uri_);
220 int err = GetAlbumUrisById(id, albumUriList);
221 CHECK_AND_RETURN_LOG(err == E_OK, "Fail to get albumId");
222 for (string uri : albumUriList) {
223 AddNotify(taskData->uri_, uri, taskData);
224 }
225 }
226 } else {
227 string typeUri = MediaLibraryDataManagerUtils::GetTypeUriByUri(taskData->uri_);
228 AddNotify(taskData->uri_, typeUri, taskData);
229 }
230 }
231
Init()232 int32_t MediaLibraryNotify::Init()
233 {
234 MediaLibraryNotify::timerId_ = MediaLibraryNotify::timer_.Register(PushNotification, MNOTIFY_TIME_INTERVAL);
235 MediaLibraryNotify::timer_.Setup();
236 return E_OK;
237 }
238
Notify(const string & uri,const NotifyType notifyType,const int albumId)239 int32_t MediaLibraryNotify::Notify(const string &uri, const NotifyType notifyType, const int albumId)
240 {
241 if (MediaLibraryNotify::nfListMap_.size() > MAX_NOTIFY_LIST_SIZE) {
242 MediaLibraryNotify::timer_.Shutdown();
243 PushNotification();
244 MediaLibraryNotify::timer_.Register(PushNotification, MNOTIFY_TIME_INTERVAL);
245 MediaLibraryNotify::timer_.Setup();
246 }
247 shared_ptr<MediaLibraryAsyncWorker> asyncWorker = MediaLibraryAsyncWorker::GetInstance();
248 CHECK_AND_RETURN_RET_LOG(asyncWorker != nullptr, E_ASYNC_WORKER_IS_NULL, "AsyncWorker is null");
249 auto *taskData = new (nothrow) NotifyTaskData(uri, notifyType, albumId);
250 CHECK_AND_RETURN_RET_LOG(taskData != nullptr, E_NOTIFY_TASK_DATA_IS_NULL, "taskData is null");
251 MEDIA_DEBUG_LOG("Notify ,uri = %{private}s, notifyType = %{private}d, albumId = %{private}d",
252 uri.c_str(), notifyType, albumId);
253 shared_ptr<MediaLibraryAsyncTask> notifyAsyncTask = make_shared<MediaLibraryAsyncTask>(AddNfListMap, taskData);
254 if (notifyAsyncTask != nullptr) {
255 asyncWorker->AddTask(notifyAsyncTask, false);
256 }
257 return E_OK;
258 }
259
Notify(const shared_ptr<FileAsset> & closeAsset)260 int32_t MediaLibraryNotify::Notify(const shared_ptr<FileAsset> &closeAsset)
261 {
262 bool isCreateFile = false;
263 if (closeAsset->GetDateModified() == 0) {
264 isCreateFile = true;
265 }
266 if (closeAsset->GetMediaType() == MediaType::MEDIA_TYPE_IMAGE ||
267 closeAsset->GetMediaType() == MediaType::MEDIA_TYPE_VIDEO) {
268 if (isCreateFile) {
269 return Notify(PhotoColumn::PHOTO_URI_PREFIX + to_string(closeAsset->GetId()), NotifyType::NOTIFY_ADD);
270 }
271 return Notify(PhotoColumn::PHOTO_URI_PREFIX + to_string(closeAsset->GetId()), NotifyType::NOTIFY_UPDATE);
272 } else if (closeAsset->GetMediaType() == MediaType::MEDIA_TYPE_AUDIO) {
273 if (isCreateFile) {
274 return Notify(AudioColumn::AUDIO_URI_PREFIX + to_string(closeAsset->GetId()), NotifyType::NOTIFY_ADD);
275 }
276 return Notify(AudioColumn::AUDIO_URI_PREFIX + to_string(closeAsset->GetId()), NotifyType::NOTIFY_UPDATE);
277 } else {
278 return E_CHECK_MEDIATYPE_FAIL;
279 }
280 }
281
GetDefaultAlbums(std::unordered_map<PhotoAlbumSubType,int> & outAlbums)282 int32_t MediaLibraryNotify::GetDefaultAlbums(std::unordered_map<PhotoAlbumSubType, int> &outAlbums)
283 {
284 auto uniStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
285 MediaLibraryCommand queryAlbumMapCmd(OperationObject::PHOTO_ALBUM, OperationType::QUERY);
286 queryAlbumMapCmd.GetAbsRdbPredicates()->EqualTo(PhotoAlbumColumns::ALBUM_TYPE, to_string(PhotoAlbumType::SYSTEM));
287 CHECK_AND_RETURN_RET_LOG(uniStore != nullptr, E_HAS_DB_ERROR, "UniStore is nullptr!");
288 auto resultSet = uniStore->Query(queryAlbumMapCmd,
289 {PhotoAlbumColumns::ALBUM_ID, PhotoAlbumColumns::ALBUM_SUBTYPE});
290 if (resultSet == nullptr) {
291 return E_HAS_DB_ERROR;
292 }
293 int32_t count = -1;
294 int32_t ret = resultSet->GetRowCount(count);
295 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to get count");
296 ret = resultSet->GoToFirstRow();
297 CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "Failed to GoToFirstRow");
298 do {
299 int32_t albumId = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_ID, resultSet,
300 TYPE_INT32));
301 int32_t albumSubType = get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_SUBTYPE,
302 resultSet, TYPE_INT32));
303 MEDIA_INFO_LOG("GetDefaultAlbums albumId: %{public}d, albumSubType: %{public}d", albumId, albumSubType);
304 outAlbums.insert(make_pair(static_cast<PhotoAlbumSubType>(albumSubType), albumId));
305 } while (!resultSet->GoToNextRow());
306 return E_OK;
307 }
308
GetAlbumIdBySubType(const PhotoAlbumSubType subType)309 int32_t MediaLibraryNotify::GetAlbumIdBySubType(const PhotoAlbumSubType subType)
310 {
311 int errCode = E_OK;
312 if (defaultAlbums_.size() == 0) {
313 errCode = GetDefaultAlbums(defaultAlbums_);
314 }
315 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Failed to GetDefaultAlbums");
316 if (defaultAlbums_.count(subType) == 0) {
317 return E_ERR;
318 }
319 return defaultAlbums_.find(subType)->second;
320 }
321
GetNotifyUri(shared_ptr<NativeRdb::ResultSet> & resultSet,vector<string> & notifyUris)322 static void GetNotifyUri(shared_ptr<NativeRdb::ResultSet> &resultSet, vector<string> ¬ifyUris)
323 {
324 int32_t fileId = MediaLibraryRdbStore::GetInt(resultSet, PhotoColumn::MEDIA_ID);
325 string path = MediaLibraryRdbStore::GetString(resultSet, PhotoColumn::MEDIA_FILE_PATH);
326 string displayName = MediaLibraryRdbStore::GetString(resultSet, PhotoColumn::MEDIA_NAME);
327 string notifyUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX, to_string(fileId),
328 MediaFileUtils::GetExtraUri(displayName, path));
329 notifyUris.push_back(notifyUri);
330 }
331
GetNotifyUris(const RdbPredicates & predicates,vector<string> & notifyUris)332 void MediaLibraryNotify::GetNotifyUris(const RdbPredicates &predicates, vector<string> ¬ifyUris)
333 {
334 auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStoreRaw();
335 if (rdbStore == nullptr) {
336 return;
337 }
338 auto resultSet = rdbStore->Query(predicates, {
339 PhotoColumn::MEDIA_ID,
340 PhotoColumn::MEDIA_FILE_PATH,
341 PhotoColumn::MEDIA_NAME
342 });
343 if (resultSet == nullptr) {
344 return;
345 }
346
347 int32_t count = 0;
348 int32_t err = resultSet->GetRowCount(count);
349 if (err != E_OK || count <= 0) {
350 MEDIA_WARN_LOG("Failed to get row count: %{public}d", err);
351 return;
352 }
353 err = resultSet->GoToFirstRow();
354 if (err != E_OK) {
355 MEDIA_WARN_LOG("Failed to go to first row: %{public}d", err);
356 return;
357 }
358 do {
359 GetNotifyUri(resultSet, notifyUris);
360 count--;
361 if (count > 0) {
362 err = resultSet->GoToNextRow();
363 if (err < 0) {
364 MEDIA_WARN_LOG("Failed to go to next row err: %{public}d", err);
365 return;
366 }
367 }
368 } while (count > 0);
369 }
370 } // namespace OHOS::Media
371