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