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 "thumbnail_manager.h"
17 
18 #include <memory>
19 #include <mutex>
20 #include <sys/mman.h>
21 #include <sys/stat.h>
22 #include <uuid/uuid.h>
23 
24 #include "ashmem.h"
25 #include "image_source.h"
26 #include "image_type.h"
27 #include "js_native_api.h"
28 #include "media_file_uri.h"
29 #include "medialibrary_errno.h"
30 #include "medialibrary_napi_log.h"
31 #include "medialibrary_napi_utils.h"
32 #include "medialibrary_tracer.h"
33 #include "pixel_map.h"
34 #include "pixel_map_napi.h"
35 #include "post_proc.h"
36 #include "securec.h"
37 #include "string_ex.h"
38 #include "thumbnail_const.h"
39 #include "unique_fd.h"
40 #include "userfile_manager_types.h"
41 #include "uv.h"
42 #include "userfile_client.h"
43 
44 #ifdef IMAGE_PURGEABLE_PIXELMAP
45 #include "purgeable_pixelmap_builder.h"
46 #endif
47 
48 using namespace std;
49 const int UUID_STR_LENGTH = 37;
50 
51 namespace OHOS {
52 namespace Media {
53 shared_ptr<ThumbnailManager> ThumbnailManager::instance_ = nullptr;
54 mutex ThumbnailManager::mutex_;
55 bool ThumbnailManager::init_ = false;
56 static constexpr int32_t DEFAULT_FD = -1;
57 
ThumbnailRequest(const RequestPhotoParams & params,napi_env env,napi_ref callback)58 ThumbnailRequest::ThumbnailRequest(const RequestPhotoParams &params, napi_env env,
59     napi_ref callback) : callback_(env, callback), requestPhotoType(params.type), uri_(params.uri),
60     path_(params.path), requestSize_(params.size)
61 {
62 }
63 
~ThumbnailRequest()64 ThumbnailRequest::~ThumbnailRequest()
65 {
66 }
67 
ReleaseCallbackRef()68 void ThumbnailRequest::ReleaseCallbackRef()
69 {
70     std::lock_guard<std::mutex> lock(mutex_);
71     if (callback_.callBackRef_) {
72         napi_delete_reference(callback_.env_, callback_.callBackRef_);
73         callback_.callBackRef_ = nullptr;
74     }
75 }
76 
UpdateStatus(ThumbnailStatus status)77 bool ThumbnailRequest::UpdateStatus(ThumbnailStatus status)
78 {
79     std::lock_guard<std::mutex> lock(mutex_);
80     if (status <= status_) {
81         return false;
82     }
83     status_ = status;
84     return true;
85 }
86 
GetStatus()87 ThumbnailStatus ThumbnailRequest::GetStatus()
88 {
89     std::lock_guard<std::mutex> lock(mutex_);
90     return status_;
91 }
92 
NeedContinue()93 bool ThumbnailRequest::NeedContinue()
94 {
95     return GetStatus() < ThumbnailStatus::THUMB_REMOVE;
96 }
97 
IsPhotoSizeThumb(const Size & size)98 static bool IsPhotoSizeThumb(const Size &size)
99 {
100     return ((size.width >= DEFAULT_THUMB_SIZE || size.height >= DEFAULT_THUMB_SIZE) ||
101         (size.width == DEFAULT_MTH_SIZE || size.height == DEFAULT_MTH_SIZE));
102 }
103 
NeedFastThumb(const Size & size,RequestPhotoType type)104 static bool NeedFastThumb(const Size &size, RequestPhotoType type)
105 {
106     return IsPhotoSizeThumb(size) && (type != RequestPhotoType::REQUEST_QUALITY_THUMBNAIL);
107 }
108 
NeedQualityThumb(const Size & size,RequestPhotoType type)109 static bool NeedQualityThumb(const Size &size, RequestPhotoType type)
110 {
111     return IsPhotoSizeThumb(size) && (type != RequestPhotoType::REQUEST_FAST_THUMBNAIL);
112 }
113 
MMapFdPtr(int32_t fd,bool isNeedRelease)114 MMapFdPtr::MMapFdPtr(int32_t fd, bool isNeedRelease)
115 {
116     if (fd < 0) {
117         NAPI_ERR_LOG("Fd is invalid: %{public}d", fd);
118         return;
119     }
120 
121     struct stat st;
122     if (fstat(fd, &st) == -1) {
123         NAPI_ERR_LOG("fstat error, errno:%{public}d", errno);
124         return;
125     }
126     size_ = st.st_size;
127 
128     // mmap ptr from fd
129     fdPtr_ = mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd, 0);
130     if (fdPtr_ == MAP_FAILED || fdPtr_ == nullptr) {
131         NAPI_ERR_LOG("mmap uniqueFd failed, errno = %{public}d", errno);
132         return;
133     }
134 
135     isValid_ = true;
136     isNeedRelease_ = isNeedRelease;
137 }
138 
~MMapFdPtr()139 MMapFdPtr::~MMapFdPtr()
140 {
141     // munmap ptr from fd
142     if (isNeedRelease_) {
143         munmap(fdPtr_, size_);
144     }
145 }
146 
GetFdPtr()147 void* MMapFdPtr::GetFdPtr()
148 {
149     return fdPtr_;
150 }
151 
GetFdSize()152 off_t MMapFdPtr::GetFdSize()
153 {
154     return size_;
155 }
156 
IsValid()157 bool MMapFdPtr::IsValid()
158 {
159     return isValid_;
160 }
161 
GenerateRequestId()162 static string GenerateRequestId()
163 {
164     uuid_t uuid;
165     uuid_generate(uuid);
166     char str[UUID_STR_LENGTH] = {};
167     uuid_unparse(uuid, str);
168     return str;
169 }
170 
GetInstance()171 shared_ptr<ThumbnailManager> ThumbnailManager::GetInstance()
172 {
173     if (instance_ == nullptr) {
174         lock_guard<mutex> lock(mutex_);
175         if (instance_ == nullptr) {
176             instance_ = shared_ptr<ThumbnailManager>(new ThumbnailManager());
177         }
178     }
179 
180     return instance_;
181 }
182 
Init()183 void ThumbnailManager::Init()
184 {
185     std::lock_guard<std::mutex> lock(mutex_);
186     if (init_) {
187         return;
188     }
189     init_ = true;
190     isThreadRunning_ = true;
191     for (auto i = 0; i < THREAD_NUM; i++) {
192         threads_.emplace_back(
193             std::thread([this, num = i]() { this->ImageWorker(num); })
194         );
195         threads_[i].detach();
196     }
197     return;
198 }
199 
AddPhotoRequest(const RequestPhotoParams & params,napi_env env,napi_ref callback)200 string ThumbnailManager::AddPhotoRequest(const RequestPhotoParams &params, napi_env env, napi_ref callback)
201 {
202     shared_ptr<ThumbnailRequest> request = make_shared<ThumbnailRequest>(params, env, callback);
203     string requestId = GenerateRequestId();
204     request->SetUUID(requestId);
205     if (!thumbRequest_.Insert(requestId, request)) {
206         return "";
207     }
208     // judge from request option
209     if (NeedFastThumb(params.size, params.type)) {
210         AddFastPhotoRequest(request);
211     } else {
212         AddQualityPhotoRequest(request);
213     }
214     return requestId;
215 }
216 
RemovePhotoRequest(const string & requestId)217 void ThumbnailManager::RemovePhotoRequest(const string &requestId)
218 {
219     RequestSharedPtr ptr;
220     if (thumbRequest_.Find(requestId, ptr)) {
221         if (ptr == nullptr) {
222             return;
223         }
224         // do not need delete from queue, just update remove status.
225         ptr->UpdateStatus(ThumbnailStatus::THUMB_REMOVE);
226         ptr->ReleaseCallbackRef();
227     }
228     thumbRequest_.Erase(requestId);
229 }
230 
~ThumbnailManager()231 ThumbnailManager::~ThumbnailManager()
232 {
233     isThreadRunning_ = false;
234     queueCv_.notify_all();
235     for (auto &thread : threads_) {
236         if (thread.joinable()) {
237             thread.join();
238         }
239     }
240 }
241 
SetThreadName(const string & threadName,int num)242 void SetThreadName(const string &threadName, int num)
243 {
244     string name = threadName;
245     name.append(to_string(num));
246     pthread_setname_np(pthread_self(), name.c_str());
247 }
248 
AddFastPhotoRequest(const RequestSharedPtr & request)249 void ThumbnailManager::AddFastPhotoRequest(const RequestSharedPtr &request)
250 {
251     request->UpdateStatus(ThumbnailStatus::THUMB_FAST);
252     fastQueue_.Push(request);
253     queueCv_.notify_one();
254 }
255 
AddQualityPhotoRequest(const RequestSharedPtr & request)256 void ThumbnailManager::AddQualityPhotoRequest(const RequestSharedPtr &request)
257 {
258     request->UpdateStatus(ThumbnailStatus::THUMB_QUALITY);
259     qualityQueue_.Push(request);
260     queueCv_.notify_one();
261 }
262 
GetFastThumbNewSize(const Size & size,Size & newSize)263 static bool GetFastThumbNewSize(const Size &size, Size &newSize)
264 {
265     // if thumbnail size is YEAR SIZE, do not need to request fast thumb
266     // if thumbnail size is MTH SIZE, return YEAR SIZE
267     // if thumbnail size is THUMB SIZE, return MTH SIZE
268     // else return THUMB SIZE
269     if (size.width == DEFAULT_YEAR_SIZE && size.height == DEFAULT_YEAR_SIZE) {
270         newSize.height = DEFAULT_YEAR_SIZE;
271         newSize.width = DEFAULT_YEAR_SIZE;
272         return false;
273     } else if (size.width == DEFAULT_MTH_SIZE && size.height == DEFAULT_MTH_SIZE) {
274         newSize.height = DEFAULT_YEAR_SIZE;
275         newSize.width = DEFAULT_YEAR_SIZE;
276         return true;
277     } else if (size.width <= DEFAULT_THUMB_SIZE && size.height <= DEFAULT_THUMB_SIZE) {
278         newSize.height = DEFAULT_MTH_SIZE;
279         newSize.width = DEFAULT_MTH_SIZE;
280         return true;
281     } else {
282         newSize.height = DEFAULT_THUMB_SIZE;
283         newSize.width = DEFAULT_THUMB_SIZE;
284         return true;
285     }
286 }
287 
288 
OpenThumbnail(const string & path,ThumbnailType type)289 static int OpenThumbnail(const string &path, ThumbnailType type)
290 {
291     if (!path.empty()) {
292         string sandboxPath = GetSandboxPath(path, type);
293         int fd = -1;
294         if (!sandboxPath.empty()) {
295             fd = open(sandboxPath.c_str(), O_RDONLY);
296         }
297         if (fd > 0) {
298             return fd;
299         }
300     }
301     return E_ERR;
302 }
303 
IfSizeEqualsRatio(const Size & imageSize,const Size & targetSize)304 static bool IfSizeEqualsRatio(const Size &imageSize, const Size &targetSize)
305 {
306     if (imageSize.height <= 0 || targetSize.height <= 0) {
307         return false;
308     }
309 
310     float imageSizeScale = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
311     float targetSizeScale = static_cast<float>(targetSize.width) / static_cast<float>(targetSize.height);
312     if (imageSizeScale - targetSizeScale > FLOAT_EPSILON || targetSizeScale - imageSizeScale > FLOAT_EPSILON) {
313         return false;
314     } else {
315         return true;
316     }
317 }
318 
CreateThumbnailByAshmem(UniqueFd & uniqueFd,const Size & size)319 static PixelMapPtr CreateThumbnailByAshmem(UniqueFd &uniqueFd, const Size &size)
320 {
321     MediaLibraryTracer tracer;
322     tracer.Start("CreateThumbnailByAshmem");
323 
324     Media::InitializationOptions option = {
325         .size = size,
326     };
327     PixelMapPtr pixel = Media::PixelMap::Create(option);
328     if (pixel == nullptr) {
329         NAPI_ERR_LOG("Can not create pixel");
330         return nullptr;
331     }
332 
333     UniqueFd dupFd = UniqueFd(dup(uniqueFd.Get()));
334     MMapFdPtr mmapFd(dupFd.Get(), false);
335     if (!mmapFd.IsValid()) {
336         NAPI_ERR_LOG("Can not mmap by fd");
337         return nullptr;
338     }
339     auto memSize = static_cast<int32_t>(mmapFd.GetFdSize());
340 
341     void* fdPtr = new int32_t();
342     *static_cast<int32_t*>(fdPtr) = dupFd.Release();
343     pixel->SetPixelsAddr(mmapFd.GetFdPtr(), fdPtr, memSize, Media::AllocatorType::SHARE_MEM_ALLOC, nullptr);
344     return pixel;
345 }
346 
DecodeThumbnail(const UniqueFd & uniqueFd,const Size & size)347 static PixelMapPtr DecodeThumbnail(const UniqueFd &uniqueFd, const Size &size)
348 {
349     MediaLibraryTracer tracer;
350     tracer.Start("ImageSource::CreateImageSource");
351     SourceOptions opts;
352     uint32_t err = 0;
353     unique_ptr<ImageSource> imageSource = ImageSource::CreateImageSource(uniqueFd.Get(), opts, err);
354     if (imageSource == nullptr) {
355         NAPI_ERR_LOG("CreateImageSource err %{public}d", err);
356         return nullptr;
357     }
358 
359     ImageInfo imageInfo;
360     err = imageSource->GetImageInfo(0, imageInfo);
361     if (err != E_OK) {
362         NAPI_ERR_LOG("GetImageInfo err %{public}d", err);
363         return nullptr;
364     }
365 
366     bool isEqualsRatio = IfSizeEqualsRatio(imageInfo.size, size);
367     DecodeOptions decodeOpts;
368     decodeOpts.desiredSize = isEqualsRatio ? size : imageInfo.size;
369     unique_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, err);
370     if (pixelMap == nullptr) {
371         NAPI_ERR_LOG("CreatePixelMap err %{public}d", err);
372         return nullptr;
373     }
374 
375     PostProc postProc;
376     if (size.width != DEFAULT_ORIGINAL && !isEqualsRatio && !postProc.CenterScale(size, *pixelMap)) {
377         NAPI_ERR_LOG("CenterScale failed, size: %{public}d * %{public}d, imageInfo size: %{public}d * %{public}d",
378             size.width, size.height, imageInfo.size.width, imageInfo.size.height);
379         return nullptr;
380     }
381 
382     // Make the ashmem of pixelmap to be purgeable after the operation on ashmem.
383     // And then make the pixelmap subject to PurgeableManager's control.
384 #ifdef IMAGE_PURGEABLE_PIXELMAP
385     PurgeableBuilder::MakePixelMapToBePurgeable(pixelMap, imageSource, decodeOpts, size);
386 #endif
387     return pixelMap;
388 }
389 
GetPixelMapFromServer(const string & uriStr,const Size & size,const string & path)390 static int32_t GetPixelMapFromServer(const string &uriStr, const Size &size, const string &path)
391 {
392     string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_THUMBNAIL + "&" +
393         MEDIA_DATA_DB_WIDTH + "=" + to_string(size.width) + "&" + MEDIA_DATA_DB_HEIGHT + "=" +
394         to_string(size.height);
395     if (IsAsciiString(path)) {
396         openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
397     }
398     Uri openUri(openUriStr);
399     return UserFileClient::OpenFile(openUri, "R");
400 }
401 
QueryThumbnail(const string & uriStr,const Size & size,const string & path)402 unique_ptr<PixelMap> ThumbnailManager::QueryThumbnail(const string &uriStr, const Size &size, const string &path)
403 {
404     MediaLibraryTracer tracer;
405     tracer.Start("QueryThumbnail uri:" + uriStr);
406     tracer.Start("DataShare::OpenFile");
407     ThumbnailType thumbType = GetThumbType(size.width, size.height);
408     if (MediaFileUri::GetMediaTypeFromUri(uriStr) == MediaType::MEDIA_TYPE_AUDIO &&
409         (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
410         thumbType = ThumbnailType::THUMB;
411     }
412     UniqueFd uniqueFd(OpenThumbnail(path, thumbType));
413     if (uniqueFd.Get() == E_ERR) {
414         uniqueFd = UniqueFd(GetPixelMapFromServer(uriStr, size, path));
415         if (uniqueFd.Get() < 0) {
416             NAPI_ERR_LOG("queryThumb is null, errCode is %{public}d", uniqueFd.Get());
417             return nullptr;
418         }
419         return DecodeThumbnail(uniqueFd, size);
420     }
421     tracer.Finish();
422     if (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR) {
423         return CreateThumbnailByAshmem(uniqueFd, size);
424     } else {
425         return DecodeThumbnail(uniqueFd, size);
426     }
427 }
428 
DeleteRequestIdFromMap(const string & requestId)429 void ThumbnailManager::DeleteRequestIdFromMap(const string &requestId)
430 {
431     thumbRequest_.Erase(requestId);
432 }
433 
RequestFastImage(const RequestSharedPtr & request)434 bool ThumbnailManager::RequestFastImage(const RequestSharedPtr &request)
435 {
436     MediaLibraryTracer tracer;
437     tracer.Start("ThumbnailManager::RequestFastImage");
438     request->SetFd(DEFAULT_FD);
439     Size fastSize;
440     if (!GetFastThumbNewSize(request->GetRequestSize(), fastSize)) {
441         return false;
442     }
443     UniqueFd uniqueFd(OpenThumbnail(request->GetPath(), GetThumbType(fastSize.width, fastSize.height)));
444     if (uniqueFd.Get() < 0) {
445         // Can not get fast image in sandbox
446         int32_t outFd = GetPixelMapFromServer(request->GetUri(), request->GetRequestSize(), request->GetPath());
447         if (outFd <= 0) {
448             NAPI_ERR_LOG("Can not get thumbnail from server, uri=%{private}s", request->GetUri().c_str());
449             request->error = E_FAIL;
450             return false;
451         }
452         request->SetFd(outFd);
453     }
454 
455     ThumbnailType thumbType = GetThumbType(fastSize.width, fastSize.height);
456     PixelMapPtr pixelMap = nullptr;
457     if (request->GetFd().Get() == DEFAULT_FD &&
458         (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
459         pixelMap = CreateThumbnailByAshmem(uniqueFd, fastSize);
460     } else {
461         pixelMap = DecodeThumbnail(request->GetFd(), fastSize);
462     }
463     if (pixelMap == nullptr) {
464         request->error = E_FAIL;
465         return false;
466     }
467     request->SetFastPixelMap(move(pixelMap));
468     return true;
469 }
470 
DealWithFastRequest(const RequestSharedPtr & request)471 void ThumbnailManager::DealWithFastRequest(const RequestSharedPtr &request)
472 {
473     MediaLibraryTracer tracer;
474     tracer.Start("ThumbnailManager::DealWithFastRequest");
475 
476     if (request == nullptr) {
477         return;
478     }
479 
480     if (!RequestFastImage(request) && request->error != E_FAIL) {
481         // when local pixelmap not exit, must add QualityThread
482         AddQualityPhotoRequest(request);
483         return;
484     }
485 
486     // callback
487     NotifyImage(request);
488 }
489 
DealWithQualityRequest(const RequestSharedPtr & request)490 void ThumbnailManager::DealWithQualityRequest(const RequestSharedPtr &request)
491 {
492     MediaLibraryTracer tracer;
493     tracer.Start("ThumbnailManager::DealWithQualityRequest");
494 
495     unique_ptr<PixelMap> pixelMapPtr = nullptr;
496     if (request->GetFd().Get() > 0) {
497         pixelMapPtr = DecodeThumbnail(request->GetFd(), request->GetRequestSize());
498     } else {
499         pixelMapPtr = QueryThumbnail(request->GetUri(), request->GetRequestSize(), request->GetPath());
500     }
501 
502     if (pixelMapPtr == nullptr) {
503         NAPI_ERR_LOG("Can not get pixelMap");
504         request->error = E_FAIL;
505     }
506     request->SetPixelMap(move(pixelMapPtr));
507 
508     // callback
509     NotifyImage(request);
510 }
511 
ImageWorker(int num)512 void ThumbnailManager::ImageWorker(int num)
513 {
514     SetThreadName("ImageWorker", num);
515     while (true) {
516         if (!isThreadRunning_) {
517             return;
518         }
519         if (!fastQueue_.Empty()) {
520             RequestSharedPtr request;
521             if (fastQueue_.Pop(request) && request->NeedContinue()) {
522                 DealWithFastRequest(request);
523             }
524         } else if (!qualityQueue_.Empty()) {
525             RequestSharedPtr request;
526             if (qualityQueue_.Pop(request) && request->NeedContinue()) {
527                 DealWithQualityRequest(request);
528             }
529         } else {
530             std::unique_lock<std::mutex> lock(queueLock_);
531             queueCv_.wait(lock, [this]() {
532                 return !isThreadRunning_ || !(qualityQueue_.Empty() && fastQueue_.Empty());
533             });
534         }
535     }
536 }
537 
HandlePixelCallback(const RequestSharedPtr & request)538 static void HandlePixelCallback(const RequestSharedPtr &request)
539 {
540     napi_env env = request->callback_.env_;
541     napi_value jsCallback = nullptr;
542     napi_status status = napi_get_reference_value(env, request->callback_.callBackRef_, &jsCallback);
543     if (status != napi_ok) {
544         NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
545         return;
546     }
547 
548     napi_value retVal = nullptr;
549     napi_value result[ARGS_TWO];
550     if (request->GetStatus() == ThumbnailStatus::THUMB_REMOVE) {
551         return;
552     }
553 
554     if (request->error == E_FAIL) {
555         int32_t errorNum = MediaLibraryNapiUtils::TransErrorCode("requestPhoto", request->error);
556         MediaLibraryNapiUtils::CreateNapiErrorObject(env, result[PARAM0], errorNum,
557             "Failed to request Photo");
558     } else {
559         result[PARAM0] = nullptr;
560     }
561     if (request->GetStatus() == ThumbnailStatus::THUMB_FAST) {
562         result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
563             shared_ptr<PixelMap>(request->GetFastPixelMap()));
564     } else {
565         result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
566             shared_ptr<PixelMap>(request->GetPixelMap()));
567     }
568 
569     status = napi_call_function(env, nullptr, jsCallback, ARGS_TWO, result, &retVal);
570     if (status != napi_ok) {
571         NAPI_ERR_LOG("CallJs napi_call_function fail, status: %{public}d", status);
572         return;
573     }
574 }
575 
UvJsExecute(uv_work_t * work)576 static void UvJsExecute(uv_work_t *work)
577 {
578     // js thread
579     if (work == nullptr) {
580         return;
581     }
582 
583     ThumnailUv *uvMsg = reinterpret_cast<ThumnailUv *>(work->data);
584     if (uvMsg == nullptr) {
585         delete work;
586         return;
587     }
588     if (uvMsg->request_ == nullptr) {
589         delete uvMsg;
590         delete work;
591         return;
592     }
593     do {
594         napi_env env = uvMsg->request_->callback_.env_;
595         if (!uvMsg->request_->NeedContinue()) {
596             break;
597         }
598         NapiScopeHandler scopeHandler(env);
599         if (!scopeHandler.IsValid()) {
600             break;
601         }
602         HandlePixelCallback(uvMsg->request_);
603     } while (0);
604     if (uvMsg->manager_ == nullptr) {
605         delete uvMsg;
606         delete work;
607         return;
608     }
609     if (uvMsg->request_->GetStatus() == ThumbnailStatus::THUMB_FAST &&
610         NeedQualityThumb(uvMsg->request_->GetRequestSize(), uvMsg->request_->requestPhotoType)) {
611         uvMsg->manager_->AddQualityPhotoRequest(uvMsg->request_);
612     } else {
613         uvMsg->manager_->DeleteRequestIdFromMap(uvMsg->request_->GetUUID());
614         uvMsg->request_->ReleaseCallbackRef();
615     }
616 
617     delete uvMsg;
618     delete work;
619 }
620 
NotifyImage(const RequestSharedPtr & request)621 void ThumbnailManager::NotifyImage(const RequestSharedPtr &request)
622 {
623     MediaLibraryTracer tracer;
624     tracer.Start("ThumbnailManager::NotifyImage");
625 
626     if (!request->NeedContinue()) {
627         DeleteRequestIdFromMap(request->GetUUID());
628         return;
629     }
630 
631     uv_loop_s *loop = nullptr;
632     napi_get_uv_event_loop(request->callback_.env_, &loop);
633     if (loop == nullptr) {
634         DeleteRequestIdFromMap(request->GetUUID());
635         return;
636     }
637 
638     uv_work_t *work = new (nothrow) uv_work_t;
639     if (work == nullptr) {
640         DeleteRequestIdFromMap(request->GetUUID());
641         return;
642     }
643 
644     ThumnailUv *msg = new (nothrow) ThumnailUv(request, this);
645     if (msg == nullptr) {
646         delete work;
647         DeleteRequestIdFromMap(request->GetUUID());
648         return;
649     }
650 
651     work->data = reinterpret_cast<void *>(msg);
652     int ret = uv_queue_work(loop, work, [](uv_work_t *w) {}, [](uv_work_t *w, int s) {
653         UvJsExecute(w);
654     });
655     if (ret != 0) {
656         NAPI_ERR_LOG("Failed to execute libuv work queue, ret: %{public}d", ret);
657         delete msg;
658         delete work;
659         return;
660     }
661     return;
662 }
663 }
664 }
665