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 return errCode;
203 }
204 return outRow;
205 }
206
CreateV10(MediaLibraryCommand & cmd)207 int32_t MediaLibraryAudioOperations::CreateV10(MediaLibraryCommand &cmd)
208 {
209 FileAsset fileAsset;
210 ValuesBucket &values = cmd.GetValueBucket();
211 string displayName;
212 string extention;
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 string title;
224 if (GetStringFromValuesBucket(values, AudioColumn::MEDIA_TITLE, title)) {
225 displayName = title + "." + extention;
226 fileAsset.SetDisplayName(displayName);
227 isContains = true;
228 }
229 }
230
231 int32_t mediaType = 0;
232 CHECK_AND_RETURN_RET(GetInt32FromValuesBucket(values, AudioColumn::MEDIA_TYPE, mediaType), 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) : SetAssetPath(fileAsset, extention, trans);
244 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode,
245 "Failed to Solve FileAsset Path and Name, displayName=%{private}s", displayName.c_str());
246
247 outRow = InsertAssetInDb(trans, cmd, fileAsset);
248 AuditLog auditLog = { true, "USER BEHAVIOR", "ADD", "io", 1, "running", "ok" };
249 HiAudit::GetInstance().Write(auditLog);
250 CHECK_AND_RETURN_RET_LOG(outRow > 0, E_HAS_DB_ERROR, "insert file in db failed, error = %{public}d", outRow);
251 return errCode;
252 };
253 errCode = trans->RetryTrans(func);
254 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "CreateV10: trans retry fail!, ret:%{public}d", errCode);
255 fileAsset.SetId(outRow);
256 string fileUri = CreateExtUriForV10Asset(fileAsset);
257 if (isNeedGrant) {
258 int32_t ret = GrantUriPermission(fileUri, cmd.GetBundleName(), fileAsset.GetPath());
259 CHECK_AND_RETURN_RET(ret == E_OK, ret);
260 }
261 cmd.SetResult(fileUri);
262 return outRow;
263 }
264
DeleteAudio(const shared_ptr<FileAsset> & fileAsset,MediaLibraryApi api)265 int32_t MediaLibraryAudioOperations::DeleteAudio(const shared_ptr<FileAsset> &fileAsset, MediaLibraryApi api)
266 {
267 string filePath = fileAsset->GetPath();
268 CHECK_AND_RETURN_RET_LOG(!filePath.empty(), E_INVALID_PATH, "get file path failed");
269 bool res = MediaFileUtils::DeleteFile(filePath);
270 CHECK_AND_RETURN_RET_LOG(res, E_HAS_FS_ERROR, "Delete audio file failed, errno: %{public}d", errno);
271
272 // delete thumbnail
273 int32_t fileId = fileAsset->GetId();
274 InvalidateThumbnail(to_string(fileId), fileAsset->GetMediaType());
275
276 string displayName = fileAsset->GetDisplayName();
277 // delete file in db
278 MediaLibraryCommand cmd(OperationObject::FILESYSTEM_AUDIO, OperationType::DELETE);
279 cmd.GetAbsRdbPredicates()->EqualTo(AudioColumn::MEDIA_ID, to_string(fileId));
280 int32_t deleteRows = DeleteAssetInDb(cmd);
281 if (deleteRows <= 0) {
282 MEDIA_ERR_LOG("Delete audio in database failed, errCode=%{public}d", deleteRows);
283 return E_HAS_DB_ERROR;
284 }
285
286 auto watch = MediaLibraryNotify::GetInstance();
287 CHECK_AND_RETURN_RET_LOG(watch != nullptr, E_ERR, "Can not get MediaLibraryNotify Instance");
288 string notifyUri = MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileId),
289 (api == MediaLibraryApi::API_10 ? MediaFileUtils::GetExtraUri(displayName, filePath) : ""));
290
291 watch->Notify(notifyUri, NotifyType::NOTIFY_REMOVE);
292 return deleteRows;
293 }
294
UpdateV10(MediaLibraryCommand & cmd)295 int32_t MediaLibraryAudioOperations::UpdateV10(MediaLibraryCommand &cmd)
296 {
297 if (cmd.GetOprnType() == OperationType::UPDATE_PENDING) {
298 return SetPendingStatus(cmd);
299 }
300 vector<string> columns = {
301 AudioColumn::MEDIA_ID,
302 AudioColumn::MEDIA_FILE_PATH,
303 AudioColumn::MEDIA_TYPE,
304 AudioColumn::MEDIA_NAME
305 };
306 shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
307 OperationObject::FILESYSTEM_AUDIO, columns);
308 if (fileAsset == nullptr) {
309 return E_INVALID_VALUES;
310 }
311
312 // Update if FileAsset.title or FileAsset.displayName is modified
313 bool isNameChanged = false;
314 int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
315 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
316 fileAsset->GetDisplayName().c_str());
317
318 int32_t rowId = UpdateFileInDb(cmd);
319 if (rowId < 0) {
320 MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
321 return rowId;
322 }
323
324 string extraUri = MediaFileUtils::GetExtraUri(fileAsset->GetDisplayName(), fileAsset->GetPath());
325 errCode = SendTrashNotify(cmd, fileAsset->GetId(), extraUri);
326 if (errCode == E_OK) {
327 return rowId;
328 }
329
330 // Audio has no favorite album, do not send favorite notify
331 auto watch = MediaLibraryNotify::GetInstance();
332 CHECK_AND_RETURN_RET_LOG(watch != nullptr, E_ERR, "Can not get MediaLibraryNotify Instance");
333 watch->Notify(MediaFileUtils::GetUriByExtrConditions(AudioColumn::AUDIO_URI_PREFIX, to_string(fileAsset->GetId()),
334 extraUri), NotifyType::NOTIFY_UPDATE);
335 return rowId;
336 }
337
UpdateV9(MediaLibraryCommand & cmd)338 int32_t MediaLibraryAudioOperations::UpdateV9(MediaLibraryCommand &cmd)
339 {
340 vector<string> columns = {
341 AudioColumn::MEDIA_ID,
342 AudioColumn::MEDIA_FILE_PATH,
343 AudioColumn::MEDIA_TYPE,
344 AudioColumn::MEDIA_NAME,
345 AudioColumn::MEDIA_RELATIVE_PATH
346 };
347 shared_ptr<FileAsset> fileAsset = GetFileAssetFromDb(*(cmd.GetAbsRdbPredicates()),
348 OperationObject::FILESYSTEM_AUDIO, columns);
349 if (fileAsset == nullptr) {
350 return E_INVALID_VALUES;
351 }
352
353 // Update if FileAsset.title or FileAsset.displayName is modified
354 bool isNameChanged = false;
355 int32_t errCode = UpdateFileName(cmd, fileAsset, isNameChanged);
356 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio Name failed, fileName=%{private}s",
357 fileAsset->GetDisplayName().c_str());
358 errCode = UpdateRelativePath(cmd, fileAsset, isNameChanged);
359 CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode, "Update Audio RelativePath failed, relativePath=%{private}s",
360 fileAsset->GetRelativePath().c_str());
361 if (isNameChanged) {
362 UpdateVirtualPath(cmd, fileAsset);
363 }
364
365 int32_t rowId = UpdateFileInDb(cmd);
366 if (rowId < 0) {
367 MEDIA_ERR_LOG("Update Audio In database failed, rowId=%{public}d", rowId);
368 return rowId;
369 }
370
371 errCode = SendTrashNotify(cmd, fileAsset->GetId());
372 if (errCode == E_OK) {
373 return rowId;
374 }
375
376 // Audio has no favorite album, do not send favorite notify
377 auto watch = MediaLibraryNotify::GetInstance();
378 CHECK_AND_RETURN_RET_LOG(watch != nullptr, E_ERR, "Can not get MediaLibraryNotify Instance");
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