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