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 
16 #include "medialibrary_audio_operations.h"
17 
18 #include "abs_shared_result_set.h"
19 #include "file_asset.h"
20 #include "media_column.h"
21 #include "media_file_uri.h"
22 #include "media_file_utils.h"
23 #include "media_log.h"
24 #include "medialibrary_asset_operations.h"
25 #include "medialibrary_command.h"
26 #include "medialibrary_db_const.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_notify.h"
29 #include "medialibrary_object_utils.h"
30 #include "medialibrary_rdb_transaction.h"
31 #include "medialibrary_rdbstore.h"
32 #include "medialibrary_type_const.h"
33 #include "medialibrary_uripermission_operations.h"
34 #include "thumbnail_const.h"
35 #include "userfile_manager_types.h"
36 #include "value_object.h"
37 #include "hi_audit.h"
38 
39 using namespace std;
40 using namespace OHOS::NativeRdb;
41 using namespace OHOS::RdbDataShareAdapter;
42 
43 namespace OHOS {
44 namespace Media {
45 const string MUSIC_DIR = "/storage/media/local/files/Docs/Music/";
46 const string CLOUD_AUDIO_DIR = "/storage/cloud/files/Audio/";
47 const string LOCAL_AUDIO_DIR = "/storage/media/local/files/Audio/";
48 
Create(MediaLibraryCommand & cmd)49 int32_t MediaLibraryAudioOperations::Create(MediaLibraryCommand &cmd)
50 {
51     switch (cmd.GetApi()) {
52         case MediaLibraryApi::API_10:
53             return CreateV10(cmd);
54         case MediaLibraryApi::API_OLD:
55             return CreateV9(cmd);
56         default:
57             MEDIA_ERR_LOG("get api failed");
58             return E_FAIL;
59     }
60 }
61 
Delete(MediaLibraryCommand & cmd)62 int32_t MediaLibraryAudioOperations::Delete(MediaLibraryCommand& cmd)
63 {
64     string fileId = cmd.GetOprnFileId();
65     vector<string> columns = {
66         AudioColumn::MEDIA_ID,
67         AudioColumn::MEDIA_FILE_PATH,
68         AudioColumn::MEDIA_RELATIVE_PATH,
69         AudioColumn::MEDIA_TYPE
70     };
71     shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
72         cmd.GetOprnObject(), columns);
73     CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, E_INVALID_FILEID, "Get fileAsset failed, fileId: %{private}s",
74         fileId.c_str());
75 
76     int32_t deleteRow = DeleteAudio(fileAsset, cmd.GetApi());
77     CHECK_AND_RETURN_RET_LOG(deleteRow >= 0, deleteRow, "delete audio failed, deleteRow=%{public}d", deleteRow);
78 
79     return deleteRow;
80 }
81 
Query(MediaLibraryCommand & cmd,const vector<string> & columns)82 std::shared_ptr<NativeRdb::ResultSet> MediaLibraryAudioOperations::Query(
83     MediaLibraryCommand &cmd, const vector<string> &columns)
84 {
85     return MediaLibraryRdbStore::QueryWithFilter(
86         RdbUtils::ToPredicates(cmd.GetDataSharePred(), AudioColumn::AUDIOS_TABLE), columns);
87 }
88 
Update(MediaLibraryCommand & cmd)89 int32_t MediaLibraryAudioOperations::Update(MediaLibraryCommand &cmd)
90 {
91     switch (cmd.GetApi()) {
92         case MediaLibraryApi::API_10:
93             return UpdateV10(cmd);
94         case MediaLibraryApi::API_OLD:
95             return UpdateV9(cmd);
96         default:
97             MEDIA_ERR_LOG("get api failed");
98             return E_FAIL;
99     }
100 
101     return E_OK;
102 }
103 
104 const static vector<string> AUDIO_COLUMN_VECTOR = {
105     AudioColumn::MEDIA_FILE_PATH,
106     AudioColumn::MEDIA_TIME_PENDING
107 };
108 
Open(MediaLibraryCommand & cmd,const string & mode)109 int32_t MediaLibraryAudioOperations::Open(MediaLibraryCommand &cmd, const string &mode)
110 {
111     string uriString = cmd.GetUriStringWithoutSegment();
112     string pendingStatus = cmd.GetQuerySetParam(MediaColumn::MEDIA_TIME_PENDING);
113 
114     shared_ptr<FileAsset> fileAsset = GetFileAssetByUri(uriString, false, AUDIO_COLUMN_VECTOR, pendingStatus);
115     if (fileAsset == nullptr) {
116         MEDIA_ERR_LOG("Get FileAsset From Uri Failed, uri:%{public}s", uriString.c_str());
117         return E_URI_INVALID;
118     }
119 
120     if (uriString.find(AudioColumn::AUDIO_URI_PREFIX) != string::npos) {
121         return OpenAsset(fileAsset, mode, MediaLibraryApi::API_10);
122     }
123     return OpenAsset(fileAsset, mode, cmd.GetApi());
124 }
125 
Close(MediaLibraryCommand & cmd)126 int32_t MediaLibraryAudioOperations::Close(MediaLibraryCommand &cmd)
127 {
128     const ValuesBucket &values = cmd.GetValueBucket();
129     string uriString;
130     if (!GetStringFromValuesBucket(values, MEDIA_DATA_DB_URI, uriString)) {
131         return E_INVALID_VALUES;
132     }
133     string pendingStatus = cmd.GetQuerySetParam(MediaColumn::MEDIA_TIME_PENDING);
134 
135     shared_ptr<FileAsset> fileAsset = GetFileAssetByUri(uriString, false, AUDIO_COLUMN_VECTOR, pendingStatus);
136     if (fileAsset == nullptr) {
137         MEDIA_ERR_LOG("Get FileAsset From Uri Failed, uri:%{public}s", uriString.c_str());
138         return E_INVALID_URI;
139     }
140 
141     int32_t isSync = 0;
142     int32_t errCode = 0;
143     if (GetInt32FromValuesBucket(cmd.GetValueBucket(), CLOSE_CREATE_THUMB_STATUS, isSync) &&
144         isSync == CREATE_THUMB_SYNC_STATUS) {
145         errCode = CloseAsset(fileAsset, true);
146     } else {
147         errCode = CloseAsset(fileAsset, false);
148     }
149     return errCode;
150 }
151 
CreateV9(MediaLibraryCommand & cmd)152 int32_t MediaLibraryAudioOperations::CreateV9(MediaLibraryCommand& cmd)
153 {
154     FileAsset fileAsset;
155     ValuesBucket &values = cmd.GetValueBucket();
156 
157     string displayName;
158     CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, AudioColumn::MEDIA_NAME, displayName),
159         E_HAS_DB_ERROR);
160     fileAsset.SetDisplayName(displayName);
161 
162     string relativePath;
163     CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, AudioColumn::MEDIA_RELATIVE_PATH, relativePath),
164         E_HAS_DB_ERROR);
165     fileAsset.SetRelativePath(relativePath);
166     MediaFileUtils::FormatRelativePath(relativePath);
167 
168     int32_t mediaType = 0;
169     CHECK_AND_RETURN_RET(GetInt32FromValuesBucket(values, AudioColumn::MEDIA_TYPE, mediaType),
170         E_HAS_DB_ERROR);
171     if (mediaType != MediaType::MEDIA_TYPE_AUDIO) {
172         return E_CHECK_MEDIATYPE_FAIL;
173     }
174     fileAsset.SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
175 
176     int32_t errCode = CheckRelativePathWithType(relativePath, MediaType::MEDIA_TYPE_AUDIO);
177     CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Failed to Check RelativePath and Extention, "
178         "relativePath=%{private}s, mediaType=%{public}d", relativePath.c_str(), mediaType);
179     errCode = CheckDisplayNameWithType(displayName, MediaType::MEDIA_TYPE_AUDIO);
180     CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Failed to Check Dir and Extention, "
181         "displayName=%{private}s, mediaType=%{public}d", displayName.c_str(), mediaType);
182 
183     std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>(__func__);
184     int32_t outRow = -1;
185     std::function<int(void)> func = [&]()->int {
186         errCode = SetAssetPathInCreate(fileAsset, trans);
187         if (errCode != E_OK) {
188             MEDIA_ERR_LOG("Failed to Solve FileAsset Path and Name, displayName=%{private}s", displayName.c_str());
189             return errCode;
190         }
191 
192         outRow = InsertAssetInDb(trans, cmd, fileAsset);
193         if (outRow <= 0) {
194             MEDIA_ERR_LOG("insert file in db failed, error = %{public}d", outRow);
195             return E_HAS_DB_ERROR;
196         }
197         return errCode;
198     };
199     errCode = trans->RetryTrans(func);
200     if (errCode != E_OK) {
201         MEDIA_ERR_LOG("CreateV9: trans retry fail!, ret:%{public}d", errCode);
202     }
203     return outRow;
204 }
205 
CreateV10(MediaLibraryCommand & cmd)206 int32_t MediaLibraryAudioOperations::CreateV10(MediaLibraryCommand& cmd)
207 {
208     FileAsset fileAsset;
209     ValuesBucket &values = cmd.GetValueBucket();
210     string displayName;
211     string extention;
212     string title;
213     bool isContains = false;
214     bool isNeedGrant = false;
215     if (GetStringFromValuesBucket(values, AudioColumn::MEDIA_NAME, displayName)) {
216         fileAsset.SetDisplayName(displayName);
217         fileAsset.SetTimePending(UNCREATE_FILE_TIMEPENDING);
218         isContains = true;
219     } else {
220         CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, ASSET_EXTENTION, extention), E_HAS_DB_ERROR);
221         isNeedGrant = true;
222         fileAsset.SetTimePending(UNOPEN_FILE_COMPONENT_TIMEPENDING);
223         if (GetStringFromValuesBucket(values, AudioColumn::MEDIA_TITLE, title)) {
224             displayName = title + "." + extention;
225             fileAsset.SetDisplayName(displayName);
226             isContains = true;
227         }
228     }
229 
230     int32_t mediaType = 0;
231     CHECK_AND_RETURN_RET(GetInt32FromValuesBucket(values, AudioColumn::MEDIA_TYPE, mediaType),
232         E_HAS_DB_ERROR);
233     CHECK_AND_RETURN_RET(mediaType == MediaType::MEDIA_TYPE_AUDIO, E_CHECK_MEDIATYPE_FAIL);
234     fileAsset.SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
235 
236     // Check rootdir and extention
237     int32_t errCode = CheckWithType(isContains, displayName, extention, MediaType::MEDIA_TYPE_AUDIO);
238     CHECK_AND_RETURN_RET(errCode == E_OK, errCode);
239     std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>(__func__);
240     int32_t outRow = -1;
241     std::function<int(void)> func = [&]()->int {
242         CHECK_AND_RETURN_RET((errCode == E_OK), errCode);
243         errCode = isContains ? SetAssetPathInCreate(fileAsset, trans) :
244             SetAssetPath(fileAsset, extention, trans);
245         CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode,
246             "Failed to Solve FileAsset Path and Name, displayName=%{private}s", displayName.c_str());
247 
248         outRow = InsertAssetInDb(trans, cmd, fileAsset);
249         AuditLog auditLog = { true, "USER BEHAVIOR", "ADD", "io", 1, "running", "ok" };
250         HiAudit::GetInstance().Write(auditLog);
251         CHECK_AND_RETURN_RET_LOG(outRow > 0, E_HAS_DB_ERROR, "insert file in db failed, error = %{public}d", outRow);
252         return errCode;
253     };
254     errCode = trans->RetryTrans(func);
255     if (errCode != E_OK) {
256         MEDIA_ERR_LOG("CreateV10: trans retry fail!, ret:%{public}d", errCode);
257     }
258     fileAsset.SetId(outRow);
259     string fileUri = CreateExtUriForV10Asset(fileAsset);
260     if (isNeedGrant) {
261         int32_t ret = GrantUriPermission(fileUri, cmd.GetBundleName(), fileAsset.GetPath());
262         CHECK_AND_RETURN_RET(ret == E_OK, ret);
263     }
264     cmd.SetResult(fileUri);
265     return outRow;
266 }
267 
DeleteAudio(const shared_ptr<FileAsset> & fileAsset,MediaLibraryApi api)268 int32_t MediaLibraryAudioOperations::DeleteAudio(const shared_ptr<FileAsset> &fileAsset, MediaLibraryApi api)
269 {
270     string filePath = fileAsset->GetPath();
271     CHECK_AND_RETURN_RET_LOG(!filePath.empty(), E_INVALID_PATH, "get file path failed");
272     bool res = MediaFileUtils::DeleteFile(filePath);
273     CHECK_AND_RETURN_RET_LOG(res, E_HAS_FS_ERROR, "Delete audio file failed, errno: %{public}d", errno);
274 
275     // delete thumbnail
276     int32_t fileId = fileAsset->GetId();
277     InvalidateThumbnail(to_string(fileId), fileAsset->GetMediaType());
278 
279     string displayName = fileAsset->GetDisplayName();
280     // delete file in db
281     MediaLibraryCommand cmd(OperationObject::FILESYSTEM_AUDIO, OperationType::DELETE);
282     cmd.GetAbsRdbPredicates()->EqualTo(AudioColumn::MEDIA_ID, to_string(fileId));
283     int32_t deleteRows = DeleteAssetInDb(cmd);
284     if (deleteRows <= 0) {
285         MEDIA_ERR_LOG("Delete audio in database failed, errCode=%{public}d", deleteRows);
286         return E_HAS_DB_ERROR;
287     }
288 
289     auto watch = MediaLibraryNotify::GetInstance();
290     string notifyUri = MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileId),
291         (api == MediaLibraryApi::API_10 ? MediaFileUtils::GetExtraUri(displayName, filePath) : ""));
292 
293     watch->Notify(notifyUri, NotifyType::NOTIFY_REMOVE);
294     return deleteRows;
295 }
296 
UpdateV10(MediaLibraryCommand & cmd)297 int32_t MediaLibraryAudioOperations::UpdateV10(MediaLibraryCommand &cmd)
298 {
299     if (cmd.GetOprnType() == OperationType::UPDATE_PENDING) {
300         return SetPendingStatus(cmd);
301     }
302     vector<string> columns = {
303         AudioColumn::MEDIA_ID,
304         AudioColumn::MEDIA_FILE_PATH,
305         AudioColumn::MEDIA_TYPE,
306         AudioColumn::MEDIA_NAME
307     };
308     shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
309         OperationObject::FILESYSTEM_AUDIO, columns);
310     if (fileAsset == nullptr) {
311         return E_INVALID_VALUES;
312     }
313 
314     // Update if FileAsset.title or FileAsset.displayName is modified
315     bool isNameChanged = false;
316     int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
317     CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
318         fileAsset->GetDisplayName().c_str());
319 
320     int32_t rowId = UpdateFileInDb(cmd);
321     if (rowId < 0) {
322         MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
323         return rowId;
324     }
325 
326     string extraUri = MediaFileUtils::GetExtraUri(fileAsset->GetDisplayName(), fileAsset->GetPath());
327     errCode = SendTrashNotify(cmd, fileAsset->GetId(), extraUri);
328     if (errCode == E_OK) {
329         return rowId;
330     }
331 
332     // Audio has no favorite album, do not send favorite notify
333     auto watch = MediaLibraryNotify::GetInstance();
334     watch->Notify(MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileAsset->GetId()),
335         extraUri), NotifyType::NOTIFY_UPDATE);
336     return rowId;
337 }
338 
UpdateV9(MediaLibraryCommand & cmd)339 int32_t MediaLibraryAudioOperations::UpdateV9(MediaLibraryCommand &cmd)
340 {
341     vector<string> columns = {
342         AudioColumn::MEDIA_ID,
343         AudioColumn::MEDIA_FILE_PATH,
344         AudioColumn::MEDIA_TYPE,
345         AudioColumn::MEDIA_NAME,
346         AudioColumn::MEDIA_RELATIVE_PATH
347     };
348     shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
349         OperationObject::FILESYSTEM_AUDIO, columns);
350     if (fileAsset == nullptr) {
351         return E_INVALID_VALUES;
352     }
353 
354     // Update if FileAsset.title or FileAsset.displayName is modified
355     bool isNameChanged = false;
356     int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
357     CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
358         fileAsset->GetDisplayName().c_str());
359     errCode = UpdateRelativePath(cmd, fileAsset, isNameChanged);
360     CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio RelativePath failed, relativePath=%{private}s",
361         fileAsset->GetRelativePath().c_str());
362     if (isNameChanged) {
363         UpdateVirtualPath(cmd, fileAsset);
364     }
365 
366     int32_t rowId = UpdateFileInDb(cmd);
367     if (rowId < 0) {
368         MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
369         return rowId;
370     }
371 
372     errCode = SendTrashNotify(cmd, fileAsset->GetId());
373     if (errCode == E_OK) {
374         return rowId;
375     }
376 
377     // Audio has no favorite album, do not send favorite notify
378     auto watch = MediaLibraryNotify::GetInstance();
379     watch->Notify(MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileAsset->GetId())),
380         NotifyType::NOTIFY_UPDATE);
381     return rowId;
382 }
383 
TrashAging(shared_ptr<int> countPtr)384 int32_t MediaLibraryAudioOperations::TrashAging(shared_ptr<int> countPtr)
385 {
386     auto time = MediaFileUtils::UTCTimeMilliSeconds();
387     RdbPredicates predicates(AudioColumn::AUDIOS_TABLE);
388     predicates.GreaterThan(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
389     predicates.And()->LessThanOrEqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(time - AGING_TIME));
390     int32_t deletedRows = DeleteFromDisk(predicates, true);
391     if (deletedRows < 0) {
392         return deletedRows;
393     }
394     if (countPtr != nullptr) {
395         *countPtr = deletedRows;
396     }
397     return E_OK;
398 }
399 
MoveToMusic()400 void MediaLibraryAudioOperations::MoveToMusic()
401 {
402     RdbPredicates predicates(AudioColumn::AUDIOS_TABLE);
403     vector<string> columns = {AudioColumn::MEDIA_NAME, MediaColumn::MEDIA_FILE_PATH};
404     auto resultSet = MediaLibraryRdbStore::QueryWithFilter(predicates, columns);
405     if (resultSet == nullptr) {
406         MEDIA_ERR_LOG("result is nullptr or count is zero");
407         return;
408     }
409     if (!MediaFileUtils::IsFileExists(MUSIC_DIR)) {
410         MEDIA_INFO_LOG("music dir is not exists!!!");
411         MediaFileUtils::CreateDirectory(MUSIC_DIR);
412     }
413     int32_t num = 0;
414     while (resultSet->GoToNextRow() == NativeRdb::E_OK) {
415         string path = MediaLibraryRdbStore::GetString(resultSet, PhotoColumn::MEDIA_FILE_PATH);
416         string localPath = path.replace(0, CLOUD_AUDIO_DIR.length(), LOCAL_AUDIO_DIR);
417         string displayName = MediaLibraryRdbStore::GetString(resultSet, AudioColumn::MEDIA_NAME);
418         if (!MediaFileUtils::ModifyAsset(localPath, MUSIC_DIR + displayName)) {
419             num++;
420         } else {
421             MEDIA_ERR_LOG("move %{private}s to music fail!", displayName.c_str());
422         }
423     }
424     MEDIA_INFO_LOG("%{public}d audios move to music success", num);
425 }
426 } // namespace Media
427 } // namespace OHOS