1 /*
2 * Copyright (C) 2021-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 #include "medialibrary_asset_operations.h"
16 #define MLOG_TAG "Distributed"
17 #include "medialibrary_sync_operation.h"
18 #include "datashare_helper.h"
19 #include "device_manager.h"
20 #include "media_column.h"
21 #include "media_app_uri_permission_column.h"
22 #include "media_device_column.h"
23 #include "media_log.h"
24 #include "medialibrary_async_worker.h"
25 #include "medialibrary_errno.h"
26 #include "medialibrary_tracer.h"
27 #include "result_set_utils.h"
28
29 namespace OHOS {
30 namespace Media {
31 using namespace std;
32 using namespace OHOS::AppExecFwk;
33 using namespace OHOS::DistributedKv;
34
35 namespace {
36 static constexpr int RETRY_COUNT = 3;
37 static constexpr int32_t WAIT_FOR_MS = 1000;
38 static constexpr int32_t ALBUM_THUMBNAIL_MAX_COUNT = 50;
39 static vector<string> table_arr = {
40 MEDIALIBRARY_TABLE, PhotoColumn::PHOTOS_TABLE, AudioColumn::AUDIOS_TABLE,
41 SMARTALBUM_TABLE, SMARTALBUM_MAP_TABLE, CATEGORY_SMARTALBUM_MAP_TABLE,
42 AppUriPermissionColumn::APP_URI_PERMISSION_TABLE };
43
44 class DistributedAsyncTaskData : public AsyncTaskData {
45 public:
46 DistributedAsyncTaskData() = default;
47 virtual ~DistributedAsyncTaskData() = default;
48 MediaLibrarySyncOpts syncOpts_;
49 vector<string> networkIds_;
50 string sqlStatement_;
51 };
52 }
53
SyncCompleted(const map<string,Status> & results)54 void MediaLibrarySyncCallback::SyncCompleted(const map<string, Status> &results)
55 {
56 for (auto &item : results) {
57 if (item.second == Status::SUCCESS) {
58 unique_lock<mutex> lock(status_.mtx_);
59 status_.isSyncComplete_ = true;
60 break;
61 }
62 }
63 status_.cond_.notify_one();
64 }
65
WaitFor()66 bool MediaLibrarySyncCallback::WaitFor()
67 {
68 unique_lock<mutex> lock(status_.mtx_);
69 bool ret =
70 status_.cond_.wait_for(lock, chrono::milliseconds(WAIT_FOR_MS), [this]() { return status_.isSyncComplete_; });
71 return ret;
72 }
73
SyncPullTableByNetworkId(AsyncTaskData * data)74 static void SyncPullTableByNetworkId(AsyncTaskData* data)
75 {
76 DistributedAsyncTaskData* taskData = static_cast<DistributedAsyncTaskData*>(data);
77 MediaLibrarySyncOperation::SyncPullTable(taskData->syncOpts_, taskData->networkIds_);
78 }
79
SyncPullAllTableByNetworkId(MediaLibrarySyncOpts & syncOpts,vector<string> & devices)80 bool MediaLibrarySyncOperation::SyncPullAllTableByNetworkId(MediaLibrarySyncOpts &syncOpts, vector<string> &devices)
81 {
82 if (syncOpts.rdbStore == nullptr) {
83 MEDIA_ERR_LOG("MediaLibrarySyncOperation SyncPullAllTable rdbStore is null");
84 return false;
85 }
86
87 for (auto &table_name : table_arr) {
88 shared_ptr<MediaLibraryAsyncWorker> asyncWorker = MediaLibraryAsyncWorker::GetInstance();
89 if (asyncWorker == nullptr) {
90 continue;
91 }
92 DistributedAsyncTaskData *taskData = new (nothrow) DistributedAsyncTaskData();
93 if (taskData == nullptr) {
94 continue;
95 }
96 syncOpts.table = table_name;
97 taskData->syncOpts_ = syncOpts;
98 taskData->networkIds_ = devices;
99 auto distributedAsyncTask = make_shared<MediaLibraryAsyncTask>(SyncPullTableByNetworkId, taskData);
100 asyncWorker->AddTask(distributedAsyncTask, false);
101 }
102 return true;
103 }
104
GetDeviceUdidByNetworkId(const shared_ptr<MediaLibraryRdbStore> rdbStore,const string & networkId)105 static string GetDeviceUdidByNetworkId(const shared_ptr<MediaLibraryRdbStore> rdbStore, const string &networkId)
106 {
107 vector<string> columns;
108 AbsRdbPredicates absPredDevice(DEVICE_TABLE);
109 absPredDevice.EqualTo(DEVICE_DB_NETWORK_ID, networkId);
110 auto queryResultSet = rdbStore->QueryByStep(absPredDevice, columns);
111 auto count = 0;
112 auto ret = queryResultSet->GetRowCount(count);
113 if (ret != NativeRdb::E_OK) {
114 return "";
115 }
116
117 if (count <= 0) {
118 return "";
119 }
120
121 ret = queryResultSet->GoToFirstRow();
122 if (ret != NativeRdb::E_OK) {
123 return "";
124 }
125
126 return get<string>(ResultSetUtils::GetValFromColumn(DEVICE_DB_UDID, queryResultSet, TYPE_STRING));
127 }
128
UpdateDeviceSyncStatus(const shared_ptr<MediaLibraryRdbStore> rdbStore,const string & networkId,int32_t syncStatus)129 static int32_t UpdateDeviceSyncStatus(const shared_ptr<MediaLibraryRdbStore> rdbStore, const string &networkId,
130 int32_t syncStatus)
131 {
132 string deviceUdid = GetDeviceUdidByNetworkId(rdbStore, networkId);
133 if (deviceUdid.empty()) {
134 return E_FAIL;
135 }
136
137 vector<string> columns;
138 AbsRdbPredicates absPredDevice(DEVICE_TABLE);
139 absPredDevice.EqualTo(DEVICE_DB_UDID, deviceUdid);
140 auto queryResultSet = rdbStore->QueryByStep(absPredDevice, columns);
141
142 auto count = 0;
143 int32_t ret = queryResultSet->GetRowCount(count);
144 if (ret != NativeRdb::E_OK) {
145 return ret;
146 }
147 if (count <= 0) {
148 return E_HAS_DB_ERROR;
149 }
150
151 ValuesBucket valuesBucket;
152 valuesBucket.PutString(DEVICE_DB_UDID, deviceUdid);
153 valuesBucket.PutInt(DEVICE_DB_SYNC_STATUS, syncStatus);
154 int32_t updatedRows(0);
155 vector<string> whereArgs = {deviceUdid};
156 ret = rdbStore->Update(updatedRows, DEVICE_TABLE, valuesBucket, DEVICE_DB_UDID + " = ?", whereArgs);
157 if (ret != E_OK) {
158 return ret;
159 }
160 return (updatedRows > 0) ? E_OK : E_FAIL;
161 }
162
GetDistributedTableName(const shared_ptr<MediaLibraryRdbStore> rdbStore,const string & networkId)163 static string GetDistributedTableName(const shared_ptr<MediaLibraryRdbStore> rdbStore, const string &networkId)
164 {
165 string distributedTableName;
166 int errCode = E_ERR;
167 if (!networkId.empty()) {
168 distributedTableName = rdbStore->ObtainDistributedTableName(networkId, MEDIALIBRARY_TABLE, errCode);
169 }
170 return distributedTableName;
171 }
172
GetAlbumCoverThumbnailKeys(const shared_ptr<NativeRdb::RdbStore> & rdbStore,const string & sqlStatement,vector<string> & keys)173 static int32_t GetAlbumCoverThumbnailKeys(const shared_ptr<NativeRdb::RdbStore> &rdbStore,
174 const string &sqlStatement, vector<string> &keys)
175 {
176 shared_ptr<NativeRdb::ResultSet> rdbResultSet = rdbStore->QuerySql(sqlStatement);
177 auto count = 0;
178 int32_t ret = rdbResultSet->GetRowCount(count);
179 if (ret != NativeRdb::E_OK) {
180 return ret;
181 }
182
183 if (count == 0) {
184 return E_FAIL;
185 }
186
187 int32_t queryBucketId = -1;
188 while (rdbResultSet->GoToNextRow() == NativeRdb::E_OK) {
189 int32_t bucketId =
190 get<int32_t>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_BUCKET_ID, rdbResultSet, TYPE_INT32));
191 if (bucketId == 0) {
192 continue;
193 }
194
195 if (queryBucketId == bucketId) {
196 continue;
197 }
198
199 string thumbnailKey =
200 get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_THUMBNAIL, rdbResultSet, TYPE_STRING));
201 keys.push_back(thumbnailKey);
202 queryBucketId = bucketId;
203 }
204 return E_SUCCESS;
205 }
206
SyncPullAlbumCoverThumbnailKeys(AsyncTaskData * data)207 static void SyncPullAlbumCoverThumbnailKeys(AsyncTaskData* data)
208 {
209 DistributedAsyncTaskData* taskData = static_cast<DistributedAsyncTaskData*>(data);
210 vector<string> thumbnailKeys;
211 GetAlbumCoverThumbnailKeys(taskData->syncOpts_.rdbStore, taskData->sqlStatement_, thumbnailKeys);
212 MediaLibrarySyncOperation::SyncPullKvstore(taskData->syncOpts_.kvStore, thumbnailKeys, taskData->networkIds_[0]);
213 }
214
SyncPullAlbumCover(const MediaLibrarySyncOpts & syncOpts,const string & networkId)215 static void SyncPullAlbumCover(const MediaLibrarySyncOpts &syncOpts, const string &networkId)
216 {
217 shared_ptr<MediaLibraryAsyncWorker> asyncWorker = MediaLibraryAsyncWorker::GetInstance();
218 if (asyncWorker == nullptr) {
219 return;
220 }
221 DistributedAsyncTaskData* taskData = new (nothrow)DistributedAsyncTaskData();
222 if (taskData == nullptr) {
223 return;
224 }
225 taskData->syncOpts_ = syncOpts;
226 taskData->networkIds_ = {networkId};
227 string distributedTableName = GetDistributedTableName(syncOpts.rdbStore, networkId);
228 taskData->sqlStatement_ = "SELECT " + MEDIA_DATA_DB_BUCKET_ID + ", " + "max(" + MEDIA_DATA_DB_DATE_ADDED + "), " +
229 MEDIA_DATA_DB_THUMBNAIL + " FROM " + distributedTableName + " WHERE " +
230 MEDIA_DATA_DB_MEDIA_TYPE + " <> " + to_string(MEDIA_TYPE_FILE) + " AND " +
231 MEDIA_DATA_DB_MEDIA_TYPE + " <> " + to_string(MEDIA_TYPE_ALBUM) + " GROUP BY " +
232 MEDIA_DATA_DB_BUCKET_ID + " , " + MEDIA_DATA_DB_THUMBNAIL + " ORDER BY " +
233 MEDIA_DATA_DB_DATE_ADDED + " DESC";
234 auto distributedAsyncTask = make_shared<MediaLibraryAsyncTask>(SyncPullAlbumCoverThumbnailKeys, taskData);
235 asyncWorker->AddTask(distributedAsyncTask, false);
236 }
237
SyncPullTableCallbackExec(const MediaLibrarySyncOpts & syncOpts,const string & networkId,int syncResult)238 static bool SyncPullTableCallbackExec(const MediaLibrarySyncOpts &syncOpts, const string &networkId, int syncResult)
239 {
240 if (networkId.empty()) {
241 MEDIA_ERR_LOG("SyncPullTable networkId is empty");
242 return false;
243 }
244 if (syncResult != 0) {
245 MEDIA_ERR_LOG("SyncPullTable tableName = %{private}s device = %{private}s syncResult = %{private}d",
246 syncOpts.table.c_str(), networkId.c_str(), syncResult);
247 return false;
248 }
249 if (syncOpts.table == MEDIALIBRARY_TABLE) {
250 UpdateDeviceSyncStatus(syncOpts.rdbStore, networkId, DEVICE_SYNCSTATUS_COMPLETE);
251 if (syncOpts.row.empty()) {
252 SyncPullAlbumCover(syncOpts, networkId);
253 }
254 }
255 return true;
256 }
257
SyncPullTable(MediaLibrarySyncOpts & syncOpts,vector<string> & devices)258 bool MediaLibrarySyncOperation::SyncPullTable(MediaLibrarySyncOpts &syncOpts, vector<string> &devices)
259 {
260 CHECK_AND_RETURN_RET_LOG(syncOpts.rdbStore != nullptr, false, "Rdb Store is not initialized");
261 DistributedRdb::SyncOption option;
262 option.mode = DistributedRdb::SyncMode::PULL;
263 option.isBlock = true;
264
265 vector<string> onlineDevices;
266 GetOnlineDevices(syncOpts.bundleName, devices, onlineDevices);
267 if (onlineDevices.size() == 0) {
268 MEDIA_ERR_LOG("SyncPullTable there is no online device");
269 return false;
270 }
271 NativeRdb::AbsRdbPredicates predicate(syncOpts.table);
272 predicate.InDevices(onlineDevices);
273 if (syncOpts.table == MEDIALIBRARY_TABLE && !syncOpts.row.empty()) {
274 predicate.EqualTo(MEDIA_DATA_DB_TIME_PENDING, to_string(0))->And()->EqualTo(MEDIA_DATA_DB_ID, syncOpts.row);
275 } else if (syncOpts.table == MEDIALIBRARY_TABLE && syncOpts.row.empty()) {
276 predicate.EqualTo(MEDIA_DATA_DB_TIME_PENDING, to_string(0));
277 } else if (!syncOpts.row.empty()) {
278 predicate.EqualTo(MEDIA_DATA_DB_ID, syncOpts.row);
279 }
280
281 DistributedRdb::SyncCallback callback = [syncOpts](const DistributedRdb::SyncResult &syncResult) {
282 for (auto iter = syncResult.begin(); iter != syncResult.end(); iter++) {
283 SyncPullTableCallbackExec(syncOpts, iter->first, iter->second);
284 }
285 };
286
287 uint32_t count = 0;
288 int ret = -1;
289 while (count++ < RETRY_COUNT && ret != E_OK) {
290 MediaLibraryTracer tracer;
291 tracer.Start("abilityHelper->Query");
292 ret = syncOpts.rdbStore->Sync(option, predicate, callback);
293 }
294 return ret == E_OK;
295 }
296
GetCameraThumbnailKeys(const shared_ptr<NativeRdb::RdbStore> & rdbStore,const string & sqlStatement,vector<string> & keys)297 static void GetCameraThumbnailKeys(const shared_ptr<NativeRdb::RdbStore> &rdbStore,
298 const string &sqlStatement, vector<string> &keys)
299 {
300 shared_ptr<NativeRdb::ResultSet> rdbResultSet = rdbStore->QuerySql(sqlStatement);
301 auto count = 0;
302 auto ret = rdbResultSet->GetRowCount(count);
303 if (ret != NativeRdb::E_OK) {
304 return;
305 }
306 if (count != 1) {
307 return;
308 }
309
310 while (rdbResultSet->GoToNextRow() == NativeRdb::E_OK) {
311 string relativePath =
312 get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_RELATIVE_PATH, rdbResultSet, TYPE_STRING));
313 if (relativePath != CAMERA_PATH) {
314 MEDIA_ERR_LOG("This sync is not for camera");
315 return;
316 }
317 string thumbnailKey =
318 get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_THUMBNAIL, rdbResultSet, TYPE_STRING));
319 keys.push_back(thumbnailKey);
320 string lcdKey = get<string>(ResultSetUtils::GetValFromColumn(MEDIA_DATA_DB_LCD, rdbResultSet, TYPE_STRING));
321 keys.push_back(lcdKey);
322 }
323 }
324
SyncPushCameraThumbnailKeys(AsyncTaskData * data)325 static void SyncPushCameraThumbnailKeys(AsyncTaskData* data)
326 {
327 DistributedAsyncTaskData* taskData = static_cast<DistributedAsyncTaskData*>(data);
328 vector<string> thumbnailKeys;
329 GetCameraThumbnailKeys(taskData->syncOpts_.rdbStore, taskData->sqlStatement_, thumbnailKeys);
330 MediaLibrarySyncOperation::SyncPushKvstore(taskData->syncOpts_.kvStore, thumbnailKeys, taskData->networkIds_[0]);
331 }
332
SyncPushCameraThumbnail(const MediaLibrarySyncOpts & syncOpts,const string & networkId)333 static void SyncPushCameraThumbnail(const MediaLibrarySyncOpts &syncOpts, const string &networkId)
334 {
335 shared_ptr<MediaLibraryAsyncWorker> asyncWorker = MediaLibraryAsyncWorker::GetInstance();
336 if (asyncWorker == nullptr) {
337 return;
338 }
339 DistributedAsyncTaskData* taskData = new (nothrow)DistributedAsyncTaskData();
340 if (taskData == nullptr) {
341 return;
342 }
343 taskData->syncOpts_ = syncOpts;
344 taskData->networkIds_ = {networkId};
345 taskData->sqlStatement_ = "SELECT " + MEDIA_DATA_DB_ID + ", " + MEDIA_DATA_DB_THUMBNAIL + ", " + MEDIA_DATA_DB_LCD +
346 ", " + MEDIA_DATA_DB_RELATIVE_PATH + " FROM " + syncOpts.table + " WHERE " +
347 MEDIA_DATA_DB_ID + " = " + syncOpts.row;
348 auto distributedAsyncTask = make_shared<MediaLibraryAsyncTask>(SyncPushCameraThumbnailKeys, taskData);
349 asyncWorker->AddTask(distributedAsyncTask, false);
350 }
351
SyncPushTableCallbackExec(const MediaLibrarySyncOpts & syncOpts,const string & networkId,int syncResult)352 static bool SyncPushTableCallbackExec(const MediaLibrarySyncOpts &syncOpts, const string &networkId, int syncResult)
353 {
354 if (networkId.empty()) {
355 return false;
356 }
357 if (syncResult != 0) {
358 MEDIA_ERR_LOG("SyncPushTable tableName = %{private}s device = %{private}s syncResult = %{private}d",
359 syncOpts.table.c_str(), networkId.c_str(), syncResult);
360 return false;
361 }
362
363 if (syncOpts.table == MEDIALIBRARY_TABLE) {
364 SyncPushCameraThumbnail(syncOpts, networkId);
365 }
366 return true;
367 }
368
SyncPushTable(MediaLibrarySyncOpts & syncOpts,vector<string> & devices,bool isBlock)369 bool MediaLibrarySyncOperation::SyncPushTable(MediaLibrarySyncOpts &syncOpts, vector<string> &devices, bool isBlock)
370 {
371 CHECK_AND_RETURN_RET_LOG(syncOpts.rdbStore != nullptr, false, "Rdb Store is not initialized");
372 DistributedRdb::SyncOption option;
373 option.mode = DistributedRdb::SyncMode::PUSH;
374 option.isBlock = isBlock;
375
376 vector<string> onlineDevices;
377 GetOnlineDevices(syncOpts.bundleName, devices, onlineDevices);
378 if (onlineDevices.size() == 0) {
379 return false;
380 }
381 NativeRdb::AbsRdbPredicates predicate(syncOpts.table);
382 predicate.InDevices(onlineDevices);
383 if (syncOpts.table == MEDIALIBRARY_TABLE && !syncOpts.row.empty()) {
384 predicate.EqualTo(MEDIA_DATA_DB_TIME_PENDING, to_string(0))->And()->EqualTo(MEDIA_DATA_DB_ID, syncOpts.row);
385 } else if (syncOpts.table == MEDIALIBRARY_TABLE && syncOpts.row.empty()) {
386 predicate.EqualTo(MEDIA_DATA_DB_TIME_PENDING, to_string(0));
387 } else if (!syncOpts.row.empty()) {
388 predicate.EqualTo(MEDIA_DATA_DB_ID, syncOpts.row);
389 }
390
391 DistributedRdb::SyncCallback callback = [syncOpts](const DistributedRdb::SyncResult& syncResult) {
392 for (auto iter = syncResult.begin(); iter != syncResult.end(); iter++) {
393 SyncPushTableCallbackExec(syncOpts, iter->first, iter->second);
394 }
395 };
396
397 MediaLibraryTracer tracer;
398 tracer.Start("SyncPushTable rdbStore->Sync");
399 return syncOpts.rdbStore->Sync(option, predicate, callback) == E_OK;
400 }
401
GetOnlineDevices(const string & bundleName,const vector<string> & originalDevices,vector<string> & onlineDevices)402 void MediaLibrarySyncOperation::GetOnlineDevices(const string &bundleName, const vector<string> &originalDevices,
403 vector<string> &onlineDevices)
404 {
405 vector<OHOS::DistributedHardware::DmDeviceInfo> deviceList;
406 string extra = "";
407 auto &deviceManager = OHOS::DistributedHardware::DeviceManager::GetInstance();
408 int32_t ret = deviceManager.GetTrustedDeviceList(bundleName, extra, deviceList);
409 if (ret != 0) {
410 MEDIA_ERR_LOG("get trusted device list failed, ret %{public}d", ret);
411 return;
412 }
413
414 for (auto &device : originalDevices) {
415 for (auto &deviceInfo : deviceList) {
416 string networkId = deviceInfo.networkId;
417 if (networkId.compare(device) == 0) {
418 onlineDevices.push_back(device);
419 }
420 }
421 }
422 }
423
SyncPullKvstore(const shared_ptr<SingleKvStore> & kvStore,const vector<string> & keys,const string & networkId)424 Status MediaLibrarySyncOperation::SyncPullKvstore(const shared_ptr<SingleKvStore> &kvStore,
425 const vector<string> &keys, const string &networkId)
426 {
427 if (kvStore == nullptr) {
428 return Status::ERROR;
429 }
430 if (networkId.empty()) {
431 return Status::ERROR;
432 }
433
434 if (keys.empty()) {
435 return Status::ERROR;
436 }
437 DataQuery dataQuery;
438 dataQuery.InKeys(keys);
439 dataQuery.Limit(ALBUM_THUMBNAIL_MAX_COUNT, 0);
440 vector<string> devices = {networkId};
441 MediaLibraryTracer tracer;
442 tracer.Start("SyncPullKvstore kvStore->SyncPull");
443 auto callback = make_shared<MediaLibrarySyncCallback>();
444 Status status = kvStore->Sync(devices, OHOS::DistributedKv::SyncMode::PULL, dataQuery, callback);
445 if (!callback->WaitFor()) {
446 MEDIA_DEBUG_LOG("wait_for timeout");
447 status = Status::ERROR;
448 }
449 return status;
450 }
451
SyncPushKvstore(const shared_ptr<SingleKvStore> & kvStore,const vector<string> & keys,const string & networkId)452 Status MediaLibrarySyncOperation::SyncPushKvstore(const shared_ptr<SingleKvStore> &kvStore,
453 const vector<string> &keys, const string &networkId)
454 {
455 if (kvStore == nullptr) {
456 return Status::ERROR;
457 }
458 if (networkId.empty()) {
459 return Status::ERROR;
460 }
461 if (keys.empty()) {
462 return Status::ERROR;
463 }
464 DataQuery dataQuery;
465 dataQuery.InKeys(keys);
466 dataQuery.Limit(ALBUM_THUMBNAIL_MAX_COUNT, 0);
467 vector<string> devices = { networkId };
468 MediaLibraryTracer tracer;
469 tracer.Start("SyncPushKvstore kvStore->SyncPush");
470 return kvStore->Sync(devices, OHOS::DistributedKv::SyncMode::PUSH, dataQuery);
471 }
472 } // namespace Media
473 } // namespace OHOS
474