1 /*
2  * Copyright (C) 2024 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 #define MLOG_TAG "EnhancementServiceCallback"
17 
18 #include "enhancement_service_callback.h"
19 
20 #include "enhancement_database_operations.h"
21 #include "enhancement_manager.h"
22 #include "enhancement_task_manager.h"
23 #include "enhancement_service_adapter.h"
24 #include "media_log.h"
25 #include "medialibrary_errno.h"
26 #include "result_set_utils.h"
27 #include "file_utils.h"
28 #include "medialibrary_object_utils.h"
29 #include "media_file_utils.h"
30 #include "medialibrary_rdb_transaction.h"
31 #include "medialibrary_unistore_manager.h"
32 #include "medialibrary_asset_operations.h"
33 #include "medialibrary_rdb_utils.h"
34 #include "medialibrary_notify.h"
35 #include "photo_file_utils.h"
36 #include "medialibrary_photo_operations.h"
37 #include "mimetype_utils.h"
38 #include "securec.h"
39 
40 using namespace std;
41 #ifdef ABILITY_CLOUD_ENHANCEMENT_SUPPORT
42 using namespace OHOS::MediaEnhance;
43 #endif
44 namespace OHOS {
45 namespace Media {
46 static vector<string> needUpdateUris;
47 
EnhancementServiceCallback()48 EnhancementServiceCallback::EnhancementServiceCallback()
49 {}
50 
~EnhancementServiceCallback()51 EnhancementServiceCallback::~EnhancementServiceCallback()
52 {}
53 
54 #ifdef ABILITY_CLOUD_ENHANCEMENT_SUPPORT
checkStatusCode(int32_t statusCode)55 bool checkStatusCode(int32_t statusCode)
56 {
57     return (statusCode >= static_cast<int32_t>(MediaEnhance_Status_Code::LIMIT_USAGE)
58         && statusCode <= static_cast<int32_t>(MediaEnhance_Status_Code::TASK_CANNOT_EXECUTE))
59         || statusCode == static_cast<int32_t>(MediaEnhance_Status_Code::NON_RECOVERABLE);
60 }
61 
CheckDisplayNameWithType(const string & displayName,int32_t mediaType)62 static int32_t CheckDisplayNameWithType(const string &displayName, int32_t mediaType)
63 {
64     int32_t ret = MediaFileUtils::CheckDisplayName(displayName);
65     CHECK_AND_RETURN_RET_LOG(ret == E_OK, E_INVALID_DISPLAY_NAME, "Check DisplayName failed, "
66         "displayName=%{private}s", displayName.c_str());
67 
68     string ext = MediaFileUtils::GetExtensionFromPath(displayName);
69     CHECK_AND_RETURN_RET_LOG(!ext.empty(), E_INVALID_DISPLAY_NAME, "invalid extension, displayName=%{private}s",
70         displayName.c_str());
71 
72     auto typeFromExt = MediaFileUtils::GetMediaType(displayName);
73     CHECK_AND_RETURN_RET_LOG(typeFromExt == mediaType, E_CHECK_MEDIATYPE_MATCH_EXTENSION_FAIL,
74         "cannot match, mediaType=%{public}d, ext=%{private}s, type from ext=%{public}d",
75         mediaType, ext.c_str(), typeFromExt);
76     return E_OK;
77 }
78 
SetAssetPathInCreate(FileAsset & fileAsset,std::shared_ptr<TransactionOperations> trans)79 static int32_t SetAssetPathInCreate(FileAsset &fileAsset, std::shared_ptr<TransactionOperations> trans)
80 {
81     if (!fileAsset.GetPath().empty()) {
82         return E_OK;
83     }
84     string extension = MediaFileUtils::GetExtensionFromPath(fileAsset.GetDisplayName());
85     string filePath;
86     int32_t uniqueId = MediaLibraryAssetOperations::CreateAssetUniqueId(fileAsset.GetMediaType(), trans);
87     int32_t errCode = MediaLibraryAssetOperations::CreateAssetPathById(uniqueId, fileAsset.GetMediaType(),
88         extension, filePath);
89     if (errCode != E_OK) {
90         MEDIA_ERR_LOG("Create Asset Path failed, errCode=%{public}d", errCode);
91         return errCode;
92     }
93 
94     // filePath can not be empty
95     fileAsset.SetPath(filePath);
96     return E_OK;
97 }
98 
checkAddrAndBytes(CloudEnhancementThreadTask & task)99 static int32_t checkAddrAndBytes(CloudEnhancementThreadTask& task)
100 {
101     if (task.addr == nullptr || task.bytes == 0) {
102         MEDIA_ERR_LOG("task.addr is nullptr or task.bytes(%{public}u) is 0", task.bytes);
103         delete[] task.addr;
104         task.addr = nullptr;
105         return E_ERR;
106     }
107     return E_OK;
108 }
109 
SaveCloudEnhancementPhoto(shared_ptr<CloudEnhancementFileInfo> info,CloudEnhancementThreadTask & task,shared_ptr<NativeRdb::ResultSet> resultSet)110 int32_t EnhancementServiceCallback::SaveCloudEnhancementPhoto(shared_ptr<CloudEnhancementFileInfo> info,
111     CloudEnhancementThreadTask& task, shared_ptr<NativeRdb::ResultSet> resultSet)
112 {
113     CHECK_AND_RETURN_RET(checkAddrAndBytes(task) == E_OK, E_ERR);
114     CHECK_AND_RETURN_RET_LOG(MediaFileUtils::CheckDisplayName(info->displayName) == E_OK,
115         E_ERR, "display name not valid");
116     auto pos = info->displayName.rfind('.');
117     string prefix = info->displayName.substr(0, pos);
118     string suffix = info->displayName.substr(pos);
119     string newDisplayName = prefix + "_enhanced" + suffix;
120     string newFilePath;
121     int32_t newFileId = -1;
122     shared_ptr<CloudEnhancementFileInfo> newFileInfo = make_shared<CloudEnhancementFileInfo>(0, newFilePath,
123         newDisplayName, info->subtype, info->hidden);
124     newFileId = CreateCloudEnhancementPhoto(info->fileId, newFileInfo, resultSet);
125     CHECK_AND_RETURN_RET_LOG(newFileId > 0, newFileId, "insert file in db failed, error = %{public}d", newFileId);
126     int32_t ret = FileUtils::SaveImage(newFileInfo->filePath, (void*)(task.addr), static_cast<size_t>(task.bytes));
127     delete[] task.addr;
128     task.addr = nullptr;
129     CHECK_AND_RETURN_RET_LOG(ret == E_OK, ret, "save cloud enhancement image failed. ret=%{public}d, errno=%{public}d",
130         ret, errno);
131     if (info->subtype == static_cast<int32_t>(PhotoSubType::MOVING_PHOTO)) {
132         string sourceVideoPath = MediaFileUtils::GetMovingPhotoVideoPath(info->filePath);
133         string newVideoPath = MediaFileUtils::GetMovingPhotoVideoPath(newFileInfo->filePath);
134         bool copyResult = MediaFileUtils::CopyFileUtil(sourceVideoPath, newVideoPath);
135         if (!copyResult) {
136             MEDIA_ERR_LOG(
137                 "save moving photo video failed. file_id: %{public}d, errno=%{public}d", newFileId, errno);
138         }
139     }
140     string editDataCameraSourcePath = PhotoFileUtils::GetEditDataCameraPath(info->filePath);
141     if (MediaFileUtils::IsFileExists(editDataCameraSourcePath)) {
142         string extension = MediaFileUtils::GetExtensionFromPath(info->filePath);
143         string mimeType = MimeTypeUtils::GetMimeTypeFromExtension(extension);
144         MediaLibraryPhotoOperations::AddFiltersForCloudEnhancementPhoto(newFileId,
145             newFileInfo->filePath, editDataCameraSourcePath, mimeType);
146     }
147     MediaLibraryObjectUtils::ScanFileSyncWithoutAlbumUpdate(newFileInfo->filePath,
148         to_string(newFileId), MediaLibraryApi::API_10);
149     string newFileUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX, to_string(newFileId),
150         MediaFileUtils::GetExtraUri(newFileInfo->displayName, newFileInfo->filePath));
151     needUpdateUris.emplace_back(newFileUri);
152     auto watch = MediaLibraryNotify::GetInstance();
153     watch->Notify(newFileUri, NotifyType::NOTIFY_ADD);
154     return newFileId;
155 }
156 
CreateCloudEnhancementPhoto(int32_t sourceFileId,shared_ptr<CloudEnhancementFileInfo> info,shared_ptr<NativeRdb::ResultSet> resultSet)157 int32_t EnhancementServiceCallback::CreateCloudEnhancementPhoto(int32_t sourceFileId,
158     shared_ptr<CloudEnhancementFileInfo> info, shared_ptr<NativeRdb::ResultSet> resultSet)
159 {
160     MediaLibraryCommand cmd(OperationObject::FILESYSTEM_PHOTO, OperationType::CREATE);
161     FileAsset fileAsset;
162     fileAsset.SetDisplayName(info->displayName);
163     fileAsset.SetTimePending(UNCREATE_FILE_TIMEPENDING);
164     fileAsset.SetMediaType(MediaType::MEDIA_TYPE_IMAGE);
165     // Check rootdir
166     int32_t errCode = CheckDisplayNameWithType(info->displayName, static_cast<int32_t>(MediaType::MEDIA_TYPE_IMAGE));
167     CHECK_AND_RETURN_RET(errCode == E_OK, errCode);
168     std::shared_ptr<TransactionOperations> trans = make_shared<TransactionOperations>(__func__);
169     int32_t outRow = -1;
170     std::function<int(void)> func = [&]()->int {
171         errCode = SetAssetPathInCreate(fileAsset, trans);
172         CHECK_AND_RETURN_RET_LOG(errCode == E_OK, errCode,
173             "Failed to Solve FileAsset Path and Name, displayName=%{private}s", info->displayName.c_str());
174         outRow = EnhancementDatabaseOperations::InsertCloudEnhancementImageInDb(cmd, fileAsset,
175             sourceFileId, info, resultSet, trans);
176         CHECK_AND_RETURN_RET_LOG(outRow > 0, E_HAS_DB_ERROR, "insert file in db failed, error = %{public}d", outRow);
177         fileAsset.SetId(outRow);
178         return errCode;
179     };
180     errCode = trans->RetryTrans(func);
181     if (errCode != E_OK) {
182         MEDIA_ERR_LOG("CreateCloudEnhancementPhoto: tans finish fail!, ret:%{public}d", errCode);
183     }
184     info->filePath = fileAsset.GetPath();
185     return outRow;
186 }
187 
OnSuccess(const char * photoId,MediaEnhanceBundleHandle * bundle)188 void EnhancementServiceCallback::OnSuccess(const char* photoId, MediaEnhanceBundleHandle* bundle)
189 {
190     string taskId = string(photoId);
191     MEDIA_INFO_LOG("callback OnSuccess start, photo_id: %{public}s", taskId.c_str());
192     CHECK_AND_RETURN_LOG(!taskId.empty(), "enhancement callback error: taskId is empty");
193     CHECK_AND_RETURN_LOG(bundle != nullptr, "enhancement callback error: bundle is nullptr");
194     EnhancementTaskManager::SetTaskRequestCount(taskId, 1);
195     CloudEnhancementThreadTask task(taskId, 0, nullptr, 0, true);
196     int32_t ret = EnhancementManager::GetInstance().enhancementService_->FillTaskWithResultBuffer(bundle, task);
197     CHECK_AND_RETURN_LOG(ret == E_OK, "enhancement callback error: FillTaskWithResultBuffer failed");
198     EnhancementManager::GetInstance().threadManager_->OnProducerCallback(task);
199     MEDIA_INFO_LOG("callback OnSuccess: add %{public}s to queue", photoId);
200 }
201 
OnFailed(const char * photoId,MediaEnhanceBundleHandle * bundle)202 void EnhancementServiceCallback::OnFailed(const char* photoId, MediaEnhanceBundleHandle* bundle)
203 {
204     string taskId = string(photoId);
205     CHECK_AND_RETURN_LOG(!taskId.empty(), "enhancement callback error: taskId is empty");
206     CHECK_AND_RETURN_LOG(bundle != nullptr, "enhancement callback error: bundle is nullptr");
207     int32_t statusCode = EnhancementManager::GetInstance().enhancementService_->GetInt(bundle,
208         MediaEnhance_Bundle_Key::ERROR_CODE);
209     MEDIA_INFO_LOG("callback start, photo_id: %{public}s enter, status code: %{public}d", taskId.c_str(), statusCode);
210     CHECK_AND_RETURN_LOG(checkStatusCode(statusCode),
211         "status code is invalid, task id:%{public}s, statusCode: %{public}d", taskId.c_str(), statusCode);
212     CloudEnhancementThreadTask task(taskId, statusCode, nullptr, 0, false);
213     EnhancementManager::GetInstance().threadManager_->OnProducerCallback(task);
214     MEDIA_INFO_LOG("callback OnFailed: add %{public}s to queue", photoId);
215 }
216 
OnServiceReconnected()217 void EnhancementServiceCallback::OnServiceReconnected()
218 {
219     MEDIA_INFO_LOG("Cloud enhancement service is reconnected, try to submit processing tasks");
220     EnhancementManager::GetInstance().Init();
221 }
222 
DealWithSuccessedTask(CloudEnhancementThreadTask & task)223 void EnhancementServiceCallback::DealWithSuccessedTask(CloudEnhancementThreadTask& task)
224 {
225     string taskId = task.taskId;
226     MEDIA_INFO_LOG("DealWithSuccessedTask start, photo_id: %{public}s", taskId.c_str());
227     // query 100 per
228     string where = PhotoColumn::PHOTO_ID + " = ? ";
229     vector<string> whereArgs { taskId };
230     NativeRdb::RdbPredicates servicePredicates(PhotoColumn::PHOTOS_TABLE);
231     servicePredicates.SetWhereClause(where);
232     servicePredicates.SetWhereArgs(whereArgs);
233     vector<string> columns;
234     auto resultSet = MediaLibraryRdbStore::QueryWithFilter(servicePredicates, columns);
235     CHECK_AND_RETURN_LOG(resultSet != nullptr && resultSet->GoToFirstRow() == E_OK,
236         "enhancement callback error: query result set is empty");
237     int32_t sourceFileId = GetInt32Val(MediaColumn::MEDIA_ID, resultSet);
238     string sourceFilePath = GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
239     string sourceDisplayName = GetStringVal(MediaColumn::MEDIA_NAME, resultSet);
240     int32_t hidden = GetInt32Val(MediaColumn::MEDIA_HIDDEN, resultSet);
241     int32_t sourceSubtype = GetInt32Val(PhotoColumn::PHOTO_SUBTYPE, resultSet);
242     int32_t sourceCEAvailable = GetInt32Val(PhotoColumn::PHOTO_CE_AVAILABLE, resultSet);
243     CHECK_AND_PRINT_LOG(sourceCEAvailable == static_cast<int32_t>(CloudEnhancementAvailableType::PROCESSING),
244         "enhancement callback error: db CE_AVAILABLE status not processing, file_id: %{public}d", sourceFileId);
245     // save 120 per
246     shared_ptr<CloudEnhancementFileInfo> info = make_shared<CloudEnhancementFileInfo>(sourceFileId,
247         sourceFilePath, sourceDisplayName, sourceSubtype, hidden);
248     int32_t newFileId = SaveCloudEnhancementPhoto(info, task, resultSet);
249     CHECK_AND_RETURN_LOG(newFileId > 0, "invalid file id");
250     resultSet->Close();
251     NativeRdb::ValuesBucket rdbValues;
252     rdbValues.PutInt(PhotoColumn::PHOTO_CE_AVAILABLE, static_cast<int32_t>(CloudEnhancementAvailableType::SUCCESS));
253     rdbValues.PutInt(PhotoColumn::PHOTO_STRONG_ASSOCIATION,
254         static_cast<int32_t>(StrongAssociationType::NORMAL));
255     rdbValues.PutInt(PhotoColumn::PHOTO_ASSOCIATE_FILE_ID, newFileId);
256     int32_t ret = EnhancementDatabaseOperations::Update(rdbValues, servicePredicates);
257     CHECK_AND_PRINT_LOG(ret == E_OK, "update source photo failed. ret: %{public}d, photoId: %{public}s",
258         ret, taskId.c_str());
259     EnhancementTaskManager::RemoveEnhancementTask(taskId);
260     CloudEnhancementGetCount::GetInstance().Report("SuccessType", taskId);
261     string fileUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX, to_string(sourceFileId),
262         MediaFileUtils::GetExtraUri(sourceDisplayName, sourceFilePath));
263     auto watch = MediaLibraryNotify::GetInstance();
264     watch->Notify(fileUri, NotifyType::NOTIFY_UPDATE);
265     MEDIA_INFO_LOG("DealWithSuccessedTask success, photo_id: %{public}s", taskId.c_str());
266 }
267 
DealWithFailedTask(CloudEnhancementThreadTask & task)268 void EnhancementServiceCallback::DealWithFailedTask(CloudEnhancementThreadTask& task)
269 {
270     string taskId = task.taskId;
271     MEDIA_INFO_LOG("DealWithFailedTask start, photo_id: %{public}s", taskId.c_str());
272     int32_t statusCode = task.statusCode;
273     NativeRdb::RdbPredicates servicePredicates(PhotoColumn::PHOTOS_TABLE);
274     servicePredicates.EqualTo(PhotoColumn::PHOTO_ID, taskId);
275     vector<string> columns { MediaColumn::MEDIA_ID, MediaColumn::MEDIA_FILE_PATH,
276         MediaColumn::MEDIA_NAME, PhotoColumn::PHOTO_CE_AVAILABLE};
277     auto resultSet = MediaLibraryRdbStore::QueryWithFilter(servicePredicates, columns);
278     if (resultSet == nullptr || resultSet->GoToFirstRow() != E_OK) {
279         MEDIA_ERR_LOG("enhancement callback error: query result set is empty");
280         return;
281     }
282     int32_t fileId = GetInt32Val(MediaColumn::MEDIA_ID, resultSet);
283     string filePath = GetStringVal(MediaColumn::MEDIA_FILE_PATH, resultSet);
284     string displayName = GetStringVal(MediaColumn::MEDIA_NAME, resultSet);
285     int32_t ceAvailable = GetInt32Val(PhotoColumn::PHOTO_CE_AVAILABLE, resultSet);
286     resultSet->Close();
287     CHECK_AND_PRINT_LOG(ceAvailable == static_cast<int32_t>(CloudEnhancementAvailableType::PROCESSING),
288         "enhancement callback error: db CE_AVAILABLE status not processing, file_id: %{public}d", fileId);
289     NativeRdb::ValuesBucket valueBucket;
290     if (statusCode == static_cast<int32_t>(CEErrorCodeType::EXECUTE_FAILED) ||
291         statusCode == static_cast<int32_t>(CEErrorCodeType::NON_RECOVERABLE)) {
292         valueBucket.Put(PhotoColumn::PHOTO_CE_AVAILABLE,
293             static_cast<int32_t>(CloudEnhancementAvailableType::FAILED));
294     } else {
295         valueBucket.Put(PhotoColumn::PHOTO_CE_AVAILABLE,
296             static_cast<int32_t>(CloudEnhancementAvailableType::FAILED_RETRY));
297     }
298     valueBucket.Put(PhotoColumn::PHOTO_CE_STATUS_CODE, statusCode);
299     servicePredicates.NotEqualTo(PhotoColumn::PHOTO_CE_AVAILABLE,
300         static_cast<int32_t>(CloudEnhancementAvailableType::SUCCESS));
301     int32_t ret = EnhancementDatabaseOperations::Update(valueBucket, servicePredicates);
302     CHECK_AND_RETURN_LOG(ret == E_OK, "enhancement callback error: db CE_AVAILABLE status update failed");
303     EnhancementTaskManager::RemoveEnhancementTask(taskId);
304     CloudEnhancementGetCount::GetInstance().Report("FailedType", taskId);
305     string fileUri = MediaFileUtils::GetUriByExtrConditions(PhotoColumn::PHOTO_URI_PREFIX, to_string(fileId),
306         MediaFileUtils::GetExtraUri(displayName, filePath));
307     auto watch = MediaLibraryNotify::GetInstance();
308     watch->Notify(fileUri, NotifyType::NOTIFY_UPDATE);
309     MEDIA_INFO_LOG("DealWithFailedTask success, photo_id: %{public}s", taskId.c_str());
310 }
311 
UpdateAlbumsForCloudEnhancement()312 void EnhancementServiceCallback::UpdateAlbumsForCloudEnhancement()
313 {
314     MEDIA_INFO_LOG("UpdateAlbumsForCloudEnhancement start");
315     if (!needUpdateUris.empty()) {
316         auto rdbStore = MediaLibraryUnistoreManager::GetInstance().GetRdbStore();
317         MediaLibraryRdbUtils::UpdateAllAlbums(rdbStore, needUpdateUris);
318         needUpdateUris.clear();
319     } else {
320         MEDIA_INFO_LOG("no uris need to update albums");
321     }
322     MEDIA_INFO_LOG("UpdateAlbumsForCloudEnhancement end");
323 }
324 #endif
325 } // namespace Media
326 } // namespace OHOS