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