• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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