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_data_manager_utils.h"
27 #include "medialibrary_db_const.h"
28 #include "medialibrary_errno.h"
29 #include "medialibrary_notify.h"
30 #include "medialibrary_object_utils.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
38 using namespace std;
39 using namespace OHOS::NativeRdb;
40 using namespace OHOS::RdbDataShareAdapter;
41
42 namespace OHOS {
43 namespace Media {
Create(MediaLibraryCommand & cmd)44 int32_t MediaLibraryAudioOperations::Create(MediaLibraryCommand &cmd)
45 {
46 switch (cmd.GetApi()) {
47 case MediaLibraryApi::API_10:
48 return CreateV10(cmd);
49 case MediaLibraryApi::API_OLD:
50 return CreateV9(cmd);
51 default:
52 MEDIA_ERR_LOG("get api failed");
53 return E_FAIL;
54 }
55 }
56
Delete(MediaLibraryCommand & cmd)57 int32_t MediaLibraryAudioOperations::Delete(MediaLibraryCommand& cmd)
58 {
59 string fileId = cmd.GetOprnFileId();
60 vector<string> columns = {
61 AudioColumn::MEDIA_ID,
62 AudioColumn::MEDIA_FILE_PATH,
63 AudioColumn::MEDIA_RELATIVE_PATH,
64 AudioColumn::MEDIA_TYPE
65 };
66 shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(AudioColumn::MEDIA_ID,
67 fileId, cmd.GetOprnObject(), columns);
68 CHECK_AND_RETURN_RET_LOG(fileAsset != nullptr, E_INVALID_FILEID, "Get fileAsset failed, fileId: %{public}s",
69 fileId.c_str());
70
71 int32_t deleteRow = DeleteAudio(fileAsset, cmd.GetApi());
72 CHECK_AND_RETURN_RET_LOG(deleteRow >= 0, deleteRow, "delete audio failed, deleteRow=%{public}d", deleteRow);
73
74 return deleteRow;
75 }
76
Query(MediaLibraryCommand & cmd,const vector<string> & columns)77 std::shared_ptr<NativeRdb::ResultSet> MediaLibraryAudioOperations::Query(
78 MediaLibraryCommand &cmd, const vector<string> &columns)
79 {
80 return MediaLibraryRdbStore::Query(
81 RdbUtils::ToPredicates(cmd.GetDataSharePred(), AudioColumn::AUDIOS_TABLE), columns);
82 }
83
Update(MediaLibraryCommand & cmd)84 int32_t MediaLibraryAudioOperations::Update(MediaLibraryCommand &cmd)
85 {
86 switch (cmd.GetApi()) {
87 case MediaLibraryApi::API_10:
88 return UpdateV10(cmd);
89 case MediaLibraryApi::API_OLD:
90 return UpdateV9(cmd);
91 default:
92 MEDIA_ERR_LOG("get api failed");
93 return E_FAIL;
94 }
95
96 return E_OK;
97 }
98
99 // temp function, delete after MediaFileUri::Getpath is finish
GetPathFromUri(const std::string & uri)100 static string GetPathFromUri(const std::string &uri)
101 {
102 string realTitle = uri;
103 size_t index = uri.rfind('/');
104 if (index == string::npos) {
105 return "";
106 }
107 realTitle = uri.substr(0, index);
108 index = realTitle.rfind('/');
109 if (index == string::npos) {
110 return "";
111 }
112 realTitle = realTitle.substr(index + 1);
113 index = realTitle.rfind('_');
114 if (index == string::npos) {
115 return "";
116 }
117 int32_t fileUniqueId = stoi(realTitle.substr(index + 1));
118 int32_t bucketNum = 0;
119 MediaLibraryAssetOperations::CreateAssetBucket(fileUniqueId, bucketNum);
120 string ext = MediaFileUtils::GetExtensionFromPath(uri);
121 if (ext.empty()) {
122 return "";
123 }
124 string path = ROOT_MEDIA_DIR + AUDIO_BUCKET + "/" + to_string(bucketNum) + "/" + realTitle + "." + ext;
125 if (!MediaFileUtils::IsFileExists(path)) {
126 MEDIA_ERR_LOG("file not exist, path=%{private}s", path.c_str());
127 return "";
128 }
129 return path;
130 }
131
132 const static vector<string> AUDIO_COLUMN_VECTOR = {
133 AudioColumn::MEDIA_FILE_PATH,
134 AudioColumn::MEDIA_TIME_PENDING
135 };
136
Open(MediaLibraryCommand & cmd,const string & mode)137 int32_t MediaLibraryAudioOperations::Open(MediaLibraryCommand &cmd, const string &mode)
138 {
139 string uriString = cmd.GetUriStringWithoutSegment();
140 string id = MediaFileUri(uriString).GetFileId();
141 if (uriString.empty() || (!MediaLibraryDataManagerUtils::IsNumber(id))) {
142 return E_INVALID_URI;
143 }
144
145 shared_ptr<FileAsset> fileAsset = make_shared<FileAsset>();
146 string pendingStatus = cmd.GetQuerySetParam(MediaColumn::MEDIA_TIME_PENDING);
147 MediaFileUri fileUri(uriString);
148 if (pendingStatus.empty() || !fileUri.IsApi10()) {
149 fileAsset = GetFileAssetFromDb(AudioColumn::MEDIA_ID, id, OperationObject::FILESYSTEM_AUDIO,
150 AUDIO_COLUMN_VECTOR);
151 if (fileAsset == nullptr) {
152 MEDIA_ERR_LOG("Failed to obtain path from Database, uri=%{private}s", uriString.c_str());
153 return E_INVALID_URI;
154 }
155 } else {
156 string path = GetPathFromUri(uriString);
157 if (path.empty()) {
158 fileAsset = GetFileAssetFromDb(AudioColumn::MEDIA_ID, id, OperationObject::FILESYSTEM_AUDIO,
159 AUDIO_COLUMN_VECTOR);
160 if (fileAsset == nullptr) {
161 MEDIA_ERR_LOG("Failed to obtain path from Database, uri=%{private}s", uriString.c_str());
162 return E_INVALID_URI;
163 }
164 } else {
165 fileAsset->SetPath(path);
166 int32_t timePending = stoi(pendingStatus);
167 fileAsset->SetTimePending((timePending > 0) ? MediaFileUtils::UTCTimeSeconds() : timePending);
168 }
169 }
170
171 fileAsset->SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
172 fileAsset->SetId(stoi(id));
173 fileAsset->SetUri(uriString);
174
175 if (uriString.find(AudioColumn::AUDIO_URI_PREFIX) != string::npos) {
176 return OpenAsset(fileAsset, mode, MediaLibraryApi::API_10);
177 }
178 return OpenAsset(fileAsset, mode, cmd.GetApi());
179 }
180
Close(MediaLibraryCommand & cmd)181 int32_t MediaLibraryAudioOperations::Close(MediaLibraryCommand &cmd)
182 {
183 const ValuesBucket &values = cmd.GetValueBucket();
184 string uriString;
185 if (!GetStringFromValuesBucket(values, MEDIA_DATA_DB_URI, uriString)) {
186 return E_INVALID_VALUES;
187 }
188 string fileId = MediaLibraryDataManagerUtils::GetIdFromUri(uriString);
189 if (uriString.empty() || (!MediaLibraryDataManagerUtils::IsNumber(fileId))) {
190 return E_INVALID_URI;
191 }
192
193 shared_ptr<FileAsset> fileAsset = make_shared<FileAsset>();
194 string pendingStatus = cmd.GetQuerySetParam(MediaColumn::MEDIA_TIME_PENDING);
195 MediaFileUri fileUri(uriString);
196 if (pendingStatus.empty() || !fileUri.IsApi10()) {
197 fileAsset = GetFileAssetFromDb(AudioColumn::MEDIA_ID, fileId, OperationObject::FILESYSTEM_AUDIO,
198 AUDIO_COLUMN_VECTOR);
199 if (fileAsset == nullptr) {
200 MEDIA_ERR_LOG("Failed to obtain path from Database, uri=%{private}s", uriString.c_str());
201 return E_INVALID_URI;
202 }
203 } else {
204 string path = GetPathFromUri(uriString);
205 if (path.empty()) {
206 fileAsset = GetFileAssetFromDb(AudioColumn::MEDIA_ID, fileId, OperationObject::FILESYSTEM_AUDIO,
207 AUDIO_COLUMN_VECTOR);
208 if (fileAsset == nullptr) {
209 MEDIA_ERR_LOG("Failed to obtain path from Database, uri=%{private}s", uriString.c_str());
210 return E_INVALID_URI;
211 }
212 } else {
213 fileAsset->SetPath(path);
214 int32_t timePending = stoi(pendingStatus);
215 fileAsset->SetTimePending((timePending > 0) ? MediaFileUtils::UTCTimeSeconds() : timePending);
216 }
217 }
218
219 fileAsset->SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
220 fileAsset->SetId(stoi(fileId));
221 fileAsset->SetUri(uriString);
222
223 int32_t isSync = 0;
224 int32_t errCode = 0;
225 if (GetInt32FromValuesBucket(cmd.GetValueBucket(), CLOSE_CREATE_THUMB_STATUS, isSync) &&
226 isSync == CREATE_THUMB_SYNC_STATUS) {
227 errCode = CloseAsset(fileAsset, true);
228 } else {
229 errCode = CloseAsset(fileAsset, false);
230 }
231 return errCode;
232 }
233
CreateV9(MediaLibraryCommand & cmd)234 int32_t MediaLibraryAudioOperations::CreateV9(MediaLibraryCommand& cmd)
235 {
236 FileAsset fileAsset;
237 ValuesBucket &values = cmd.GetValueBucket();
238
239 string displayName;
240 CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, AudioColumn::MEDIA_NAME, displayName),
241 E_HAS_DB_ERROR);
242 fileAsset.SetDisplayName(displayName);
243
244 string relativePath;
245 CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, AudioColumn::MEDIA_RELATIVE_PATH, relativePath),
246 E_HAS_DB_ERROR);
247 fileAsset.SetRelativePath(relativePath);
248 MediaFileUtils::FormatRelativePath(relativePath);
249
250 int32_t mediaType = 0;
251 CHECK_AND_RETURN_RET(GetInt32FromValuesBucket(values, AudioColumn::MEDIA_TYPE, mediaType),
252 E_HAS_DB_ERROR);
253 if (mediaType != MediaType::MEDIA_TYPE_AUDIO) {
254 return E_CHECK_MEDIATYPE_FAIL;
255 }
256 fileAsset.SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
257
258 int32_t errCode = CheckRelativePathWithType(relativePath, MediaType::MEDIA_TYPE_AUDIO);
259 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Failed to Check RelativePath and Extention, "
260 "relativePath=%{private}s, mediaType=%{public}d", relativePath.c_str(), mediaType);
261 errCode = CheckDisplayNameWithType(displayName, MediaType::MEDIA_TYPE_AUDIO);
262 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Failed to Check Dir and Extention, "
263 "displayName=%{private}s, mediaType=%{public}d", displayName.c_str(), mediaType);
264
265 TransactionOperations transactionOprn;
266 errCode = transactionOprn.Start();
267 if (errCode != E_OK) {
268 return errCode;
269 }
270
271 errCode = SetAssetPathInCreate(fileAsset);
272 if (errCode != E_OK) {
273 MEDIA_ERR_LOG("Failed to Solve FileAsset Path and Name, displayName=%{private}s", displayName.c_str());
274 return errCode;
275 }
276
277 int32_t outRow = InsertAssetInDb(cmd, fileAsset);
278 if (outRow <= 0) {
279 MEDIA_ERR_LOG("insert file in db failed, error = %{public}d", outRow);
280 return E_HAS_DB_ERROR;
281 }
282 transactionOprn.Finish();
283 return outRow;
284 }
285
CreateV10(MediaLibraryCommand & cmd)286 int32_t MediaLibraryAudioOperations::CreateV10(MediaLibraryCommand& cmd)
287 {
288 FileAsset fileAsset;
289 ValuesBucket &values = cmd.GetValueBucket();
290 string displayName;
291 string extention;
292 string title;
293 bool isContains = false;
294 bool isNeedGrant = false;
295 if (GetStringFromValuesBucket(values, AudioColumn::MEDIA_NAME, displayName)) {
296 fileAsset.SetDisplayName(displayName);
297 fileAsset.SetTimePending(UNCREATE_FILE_TIMEPENDING);
298 isContains = true;
299 } else {
300 CHECK_AND_RETURN_RET(GetStringFromValuesBucket(values, ASSET_EXTENTION, extention), E_HAS_DB_ERROR);
301 isNeedGrant = true;
302 fileAsset.SetTimePending(UNOPEN_FILE_COMPONENT_TIMEPENDING);
303 if (GetStringFromValuesBucket(values, AudioColumn::MEDIA_TITLE, title)) {
304 displayName = title + "." + extention;
305 fileAsset.SetDisplayName(displayName);
306 isContains = true;
307 }
308 }
309
310 int32_t mediaType = 0;
311 CHECK_AND_RETURN_RET(GetInt32FromValuesBucket(values, AudioColumn::MEDIA_TYPE, mediaType),
312 E_HAS_DB_ERROR);
313 CHECK_AND_RETURN_RET(mediaType == MediaType::MEDIA_TYPE_AUDIO, E_CHECK_MEDIATYPE_FAIL);
314 fileAsset.SetMediaType(MediaType::MEDIA_TYPE_AUDIO);
315
316 // Check rootdir and extention
317 int32_t errCode = CheckWithType(isContains, displayName, extention, MediaType::MEDIA_TYPE_AUDIO);
318 CHECK_AND_RETURN_RET(errCode == E_OK, errCode);
319 TransactionOperations transactionOprn;
320 errCode = transactionOprn.Start();
321 CHECK_AND_RETURN_RET(errCode == E_OK, errCode);
322 errCode = isContains ? SetAssetPathInCreate(fileAsset) : SetAssetPath(fileAsset, extention);
323 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode,
324 "Failed to Solve FileAsset Path and Name, displayName=%{private}s", displayName.c_str());
325
326 int32_t outRow = InsertAssetInDb(cmd, fileAsset);
327 CHECK_AND_RETURN_RET_LOG(outRow > 0, errCode, "insert file in db failed, error = %{public}d", outRow);
328 transactionOprn.Finish();
329 fileAsset.SetId(outRow);
330 string fileUri = CreateExtUriForV10Asset(fileAsset);
331 if (isNeedGrant) {
332 int32_t ret = GrantUriPermission(fileUri, cmd.GetBundleName(), fileAsset.GetPath());
333 CHECK_AND_RETURN_RET(ret == E_OK, ret);
334 }
335 cmd.SetResult(fileUri);
336 return outRow;
337 }
338
DeleteAudio(const shared_ptr<FileAsset> & fileAsset,MediaLibraryApi api)339 int32_t MediaLibraryAudioOperations::DeleteAudio(const shared_ptr<FileAsset> &fileAsset, MediaLibraryApi api)
340 {
341 string filePath = fileAsset->GetPath();
342 CHECK_AND_RETURN_RET_LOG(!filePath.empty(), E_INVALID_PATH, "get file path failed");
343 bool res = MediaFileUtils::DeleteFile(filePath);
344 CHECK_AND_RETURN_RET_LOG(res, E_HAS_FS_ERROR, "Delete audio file failed, errno: %{public}d", errno);
345
346 // delete thumbnail
347 int32_t fileId = fileAsset->GetId();
348 InvalidateThumbnail(to_string(fileId), fileAsset->GetMediaType());
349
350 TransactionOperations transactionOprn;
351 int32_t errCode = transactionOprn.Start();
352 if (errCode != E_OK) {
353 return errCode;
354 }
355 string displayName = fileAsset->GetDisplayName();
356 // delete file in db
357 MediaLibraryCommand cmd(OperationObject::FILESYSTEM_AUDIO, OperationType::DELETE);
358 cmd.GetAbsRdbPredicates()->EqualTo(AudioColumn::MEDIA_ID, to_string(fileId));
359 int32_t deleteRows = DeleteAssetInDb(cmd);
360 if (deleteRows <= 0) {
361 MEDIA_ERR_LOG("Delete audio in database failed, errCode=%{public}d", deleteRows);
362 return E_HAS_DB_ERROR;
363 }
364 transactionOprn.Finish();
365
366 auto watch = MediaLibraryNotify::GetInstance();
367 string notifyUri = MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileId),
368 (api == MediaLibraryApi::API_10 ? MediaFileUtils::GetExtraUri(displayName, filePath) : ""));
369
370 watch->Notify(notifyUri, NotifyType::NOTIFY_REMOVE);
371 return deleteRows;
372 }
373
UpdateV10(MediaLibraryCommand & cmd)374 int32_t MediaLibraryAudioOperations::UpdateV10(MediaLibraryCommand &cmd)
375 {
376 if (cmd.GetOprnType() == OperationType::UPDATE_PENDING) {
377 return SetPendingStatus(cmd);
378 }
379 vector<string> columns = {
380 AudioColumn::MEDIA_ID,
381 AudioColumn::MEDIA_FILE_PATH,
382 AudioColumn::MEDIA_TYPE,
383 AudioColumn::MEDIA_NAME
384 };
385 shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
386 OperationObject::FILESYSTEM_AUDIO, columns);
387 if (fileAsset == nullptr) {
388 return E_INVALID_VALUES;
389 }
390
391 // Update if FileAsset.title or FileAsset.displayName is modified
392 bool isNameChanged = false;
393 int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
394 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
395 fileAsset->GetDisplayName().c_str());
396
397 TransactionOperations transactionOprn;
398 errCode = transactionOprn.Start();
399 if (errCode != E_OK) {
400 return errCode;
401 }
402
403 int32_t rowId = UpdateFileInDb(cmd);
404 if (rowId < 0) {
405 MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
406 return rowId;
407 }
408 transactionOprn.Finish();
409
410 string extraUri = MediaFileUtils::GetExtraUri(fileAsset->GetDisplayName(), fileAsset->GetPath());
411 errCode = SendTrashNotify(cmd, fileAsset->GetId(), extraUri);
412 if (errCode == E_OK) {
413 return rowId;
414 }
415
416 // Audio has no favorite album, do not send favorite notify
417 auto watch = MediaLibraryNotify::GetInstance();
418 watch->Notify(MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileAsset->GetId()),
419 extraUri), NotifyType::NOTIFY_UPDATE);
420 return rowId;
421 }
422
UpdateV9(MediaLibraryCommand & cmd)423 int32_t MediaLibraryAudioOperations::UpdateV9(MediaLibraryCommand &cmd)
424 {
425 vector<string> columns = {
426 AudioColumn::MEDIA_ID,
427 AudioColumn::MEDIA_FILE_PATH,
428 AudioColumn::MEDIA_TYPE,
429 AudioColumn::MEDIA_NAME,
430 AudioColumn::MEDIA_RELATIVE_PATH
431 };
432 shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
433 OperationObject::FILESYSTEM_AUDIO, columns);
434 if (fileAsset == nullptr) {
435 return E_INVALID_VALUES;
436 }
437
438 // Update if FileAsset.title or FileAsset.displayName is modified
439 bool isNameChanged = false;
440 int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
441 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
442 fileAsset->GetDisplayName().c_str());
443 errCode = UpdateRelativePath(cmd, fileAsset, isNameChanged);
444 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio RelativePath failed, relativePath=%{private}s",
445 fileAsset->GetRelativePath().c_str());
446 if (isNameChanged) {
447 UpdateVirtualPath(cmd, fileAsset);
448 }
449
450 TransactionOperations transactionOprn;
451 errCode = transactionOprn.Start();
452 if (errCode != E_OK) {
453 return errCode;
454 }
455
456 int32_t rowId = UpdateFileInDb(cmd);
457 if (rowId < 0) {
458 MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
459 return rowId;
460 }
461 transactionOprn.Finish();
462
463 errCode = SendTrashNotify(cmd, fileAsset->GetId());
464 if (errCode == E_OK) {
465 return rowId;
466 }
467
468 // Audio has no favorite album, do not send favorite notify
469 auto watch = MediaLibraryNotify::GetInstance();
470 watch->Notify(MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileAsset->GetId())),
471 NotifyType::NOTIFY_UPDATE);
472 return rowId;
473 }
474
TrashAging()475 int32_t MediaLibraryAudioOperations::TrashAging()
476 {
477 auto time = MediaFileUtils::UTCTimeSeconds();
478 RdbPredicates predicates(AudioColumn::AUDIOS_TABLE);
479 predicates.GreaterThan(MediaColumn::MEDIA_DATE_TRASHED, to_string(0));
480 predicates.And()->LessThanOrEqualTo(MediaColumn::MEDIA_DATE_TRASHED, to_string(time - AGING_TIME));
481 int32_t deletedRows = MediaLibraryRdbStore::DeleteFromDisk(predicates);
482 if (deletedRows < 0) {
483 return deletedRows;
484 }
485 return E_OK;
486 }
487 } // namespace Media
488 } // namespace OHOS