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 
OpenThumbnail(const string &path, ThumbnailType type)288 static int OpenThumbnail(const string &path, ThumbnailType type)
289 {
290     if (!path.empty()) {
291         string sandboxPath = GetSandboxPath(path, type);
292         int fd = -1;
293         if (!sandboxPath.empty()) {
294             fd = open(sandboxPath.c_str(), O_RDONLY);
295         }
296         if (fd > 0) {
297             return fd;
298         }
299     }
300     return E_ERR;
301 }
302 
OpenKeyFrameThumbnail(const string &path, const int32_t &beginStamp, const int32_t &type)303 static int OpenKeyFrameThumbnail(const string &path, const int32_t &beginStamp, const int32_t &type)
304 {
305     if (!path.empty()) {
306         string sandboxPath = GetKeyFrameSandboxPath(path, beginStamp, type);
307         int fd = -1;
308         if (!sandboxPath.empty()) {
309             fd = open(sandboxPath.c_str(), O_RDONLY);
310         }
311         if (fd > 0) {
312             return fd;
313         }
314         NAPI_ERR_LOG("OpenKeyFrameThumbnail failed, fd: %{public}d", fd);
315     }
316     return E_ERR;
317 }
318 
IfSizeEqualsRatio(const Size &imageSize, const Size &targetSize)319 static bool IfSizeEqualsRatio(const Size &imageSize, const Size &targetSize)
320 {
321     if (imageSize.height <= 0 || targetSize.height <= 0) {
322         return false;
323     }
324 
325     float imageSizeScale = static_cast<float>(imageSize.width) / static_cast<float>(imageSize.height);
326     float targetSizeScale = static_cast<float>(targetSize.width) / static_cast<float>(targetSize.height);
327     if (imageSizeScale - targetSizeScale > FLOAT_EPSILON || targetSizeScale - imageSizeScale > FLOAT_EPSILON) {
328         return false;
329     } else {
330         return true;
331     }
332 }
333 
CreateThumbnailByAshmem(UniqueFd &uniqueFd, const Size &size)334 static PixelMapPtr CreateThumbnailByAshmem(UniqueFd &uniqueFd, const Size &size)
335 {
336     MediaLibraryTracer tracer;
337     tracer.Start("CreateThumbnailByAshmem");
338 
339     Media::InitializationOptions option = {
340         .size = size,
341     };
342     PixelMapPtr pixel = Media::PixelMap::Create(option);
343     if (pixel == nullptr) {
344         NAPI_ERR_LOG("Can not create pixel");
345         return nullptr;
346     }
347 
348     UniqueFd dupFd = UniqueFd(dup(uniqueFd.Get()));
349     MMapFdPtr mmapFd(dupFd.Get(), false);
350     if (!mmapFd.IsValid()) {
351         NAPI_ERR_LOG("Can not mmap by fd");
352         return nullptr;
353     }
354     auto memSize = static_cast<int32_t>(mmapFd.GetFdSize());
355 
356     void* fdPtr = new int32_t();
357     *static_cast<int32_t*>(fdPtr) = dupFd.Release();
358     pixel->SetPixelsAddr(mmapFd.GetFdPtr(), fdPtr, memSize, Media::AllocatorType::SHARE_MEM_ALLOC, nullptr);
359     return pixel;
360 }
361 
DecodeThumbnail(const UniqueFd &uniqueFd, const Size &size)362 static PixelMapPtr DecodeThumbnail(const UniqueFd &uniqueFd, const Size &size)
363 {
364     MediaLibraryTracer tracer;
365     tracer.Start("ImageSource::CreateImageSource");
366     SourceOptions opts;
367     uint32_t err = 0;
368     unique_ptr<ImageSource> imageSource = ImageSource::CreateImageSource(uniqueFd.Get(), opts, err);
369     if (imageSource == nullptr) {
370         NAPI_ERR_LOG("CreateImageSource err %{public}d", err);
371         return nullptr;
372     }
373 
374     ImageInfo imageInfo;
375     err = imageSource->GetImageInfo(0, imageInfo);
376     if (err != E_OK) {
377         NAPI_ERR_LOG("GetImageInfo err %{public}d", err);
378         return nullptr;
379     }
380 
381     bool isEqualsRatio = IfSizeEqualsRatio(imageInfo.size, size);
382     DecodeOptions decodeOpts;
383     decodeOpts.desiredSize = isEqualsRatio ? size : imageInfo.size;
384     unique_ptr<PixelMap> pixelMap = imageSource->CreatePixelMap(decodeOpts, err);
385     if (pixelMap == nullptr) {
386         NAPI_ERR_LOG("CreatePixelMap err %{public}d", err);
387         return nullptr;
388     }
389 
390     PostProc postProc;
391     if (size.width != DEFAULT_ORIGINAL && !isEqualsRatio && !postProc.CenterScale(size, *pixelMap)) {
392         NAPI_ERR_LOG("CenterScale failed, size: %{public}d * %{public}d, imageInfo size: %{public}d * %{public}d",
393             size.width, size.height, imageInfo.size.width, imageInfo.size.height);
394         return nullptr;
395     }
396 
397     // Make the ashmem of pixelmap to be purgeable after the operation on ashmem.
398     // And then make the pixelmap subject to PurgeableManager's control.
399 #ifdef IMAGE_PURGEABLE_PIXELMAP
400     PurgeableBuilder::MakePixelMapToBePurgeable(pixelMap, imageSource, decodeOpts, size);
401 #endif
402     return pixelMap;
403 }
404 
GetPixelMapFromServer(const string &uriStr, const Size &size, const string &path)405 static int32_t GetPixelMapFromServer(const string &uriStr, const Size &size, const string &path)
406 {
407     string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_THUMBNAIL + "&" +
408         MEDIA_DATA_DB_WIDTH + "=" + to_string(size.width) + "&" + MEDIA_DATA_DB_HEIGHT + "=" +
409         to_string(size.height);
410     if (IsAsciiString(path)) {
411         openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
412     }
413     Uri openUri(openUriStr);
414     return UserFileClient::OpenFile(openUri, "R");
415 }
416 
GetKeyFramePixelMapFromServer(const string &uriStr, const string &path, const int32_t &beginStamp, const int32_t &type)417 static int32_t GetKeyFramePixelMapFromServer(const string &uriStr, const string &path,
418     const int32_t &beginStamp, const int32_t &type)
419 {
420     string openUriStr = uriStr + "?" + MEDIA_OPERN_KEYWORD + "=" + MEDIA_DATA_DB_KEY_FRAME + "&" +
421         MEDIA_DATA_DB_BEGIN_STAMP + "=" + to_string(beginStamp) + "&" + MEDIA_DATA_DB_TYPE + "=" + to_string(type);
422     if (IsAsciiString(path)) {
423         openUriStr += "&" + THUMBNAIL_PATH + "=" + path;
424     }
425     Uri openUri(openUriStr);
426     return UserFileClient::OpenFile(openUri, "R");
427 }
428 
QueryThumbnail(const string &uriStr, const Size &size, const string &path)429 unique_ptr<PixelMap> ThumbnailManager::QueryThumbnail(const string &uriStr, const Size &size, const string &path)
430 {
431     MediaLibraryTracer tracer;
432     tracer.Start("QueryThumbnail uri:" + uriStr);
433     tracer.Start("DataShare::OpenFile");
434     ThumbnailType thumbType = GetThumbType(size.width, size.height);
435     if (MediaFileUri::GetMediaTypeFromUri(uriStr) == MediaType::MEDIA_TYPE_AUDIO &&
436         (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
437         thumbType = ThumbnailType::THUMB;
438     }
439     UniqueFd uniqueFd(OpenThumbnail(path, thumbType));
440     if (uniqueFd.Get() == E_ERR) {
441         uniqueFd = UniqueFd(GetPixelMapFromServer(uriStr, size, path));
442         if (uniqueFd.Get() < 0) {
443             NAPI_ERR_LOG("queryThumb is null, errCode is %{public}d", uniqueFd.Get());
444             return nullptr;
445         }
446         return DecodeThumbnail(uniqueFd, size);
447     }
448     tracer.Finish();
449     if (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR) {
450         return CreateThumbnailByAshmem(uniqueFd, size);
451     } else {
452         return DecodeThumbnail(uniqueFd, size);
453     }
454 }
455 
QueryKeyFrameThumbnail(const string &uriStr, const int &beginStamp, const int &type, const string &path)456 unique_ptr<PixelMap> ThumbnailManager::QueryKeyFrameThumbnail(const string &uriStr, const int &beginStamp,
457     const int &type, const string &path)
458 {
459     MediaLibraryTracer tracer;
460     tracer.Start("QueryKeyFrameThumbnail uri:" + uriStr);
461 
462     UniqueFd uniqueFd(OpenKeyFrameThumbnail(path, beginStamp, type));
463     Size size;
464     size.width = DEFAULT_THUMB_SIZE;
465     size.height = DEFAULT_THUMB_SIZE;
466     if (uniqueFd.Get() == E_ERR) {
467         uniqueFd = UniqueFd(GetKeyFramePixelMapFromServer(uriStr, path, beginStamp, type));
468         if (uniqueFd.Get() < 0) {
469             NAPI_ERR_LOG("queryKeyFrameThumb is null, errCode is %{public}d", uniqueFd.Get());
470             return nullptr;
471         }
472     }
473     tracer.Finish();
474     return DecodeThumbnail(uniqueFd, size);
475 }
476 
DeleteRequestIdFromMap(const string &requestId)477 void ThumbnailManager::DeleteRequestIdFromMap(const string &requestId)
478 {
479     thumbRequest_.Erase(requestId);
480 }
481 
RequestFastImage(const RequestSharedPtr &request)482 bool ThumbnailManager::RequestFastImage(const RequestSharedPtr &request)
483 {
484     MediaLibraryTracer tracer;
485     tracer.Start("ThumbnailManager::RequestFastImage");
486     request->SetFd(DEFAULT_FD);
487     Size fastSize;
488     if (!GetFastThumbNewSize(request->GetRequestSize(), fastSize)) {
489         return false;
490     }
491     UniqueFd uniqueFd(OpenThumbnail(request->GetPath(), GetThumbType(fastSize.width, fastSize.height)));
492     if (uniqueFd.Get() < 0) {
493         // Can not get fast image in sandbox
494         int32_t outFd = GetPixelMapFromServer(request->GetUri(), request->GetRequestSize(), request->GetPath());
495         if (outFd <= 0) {
496             NAPI_ERR_LOG("Can not get thumbnail from server, uri=%{private}s", request->GetUri().c_str());
497             request->error = E_FAIL;
498             return false;
499         }
500         request->SetFd(outFd);
501     }
502 
503     ThumbnailType thumbType = GetThumbType(fastSize.width, fastSize.height);
504     PixelMapPtr pixelMap = nullptr;
505     if (request->GetFd().Get() == DEFAULT_FD &&
506         (thumbType == ThumbnailType::MTH || thumbType == ThumbnailType::YEAR)) {
507         pixelMap = CreateThumbnailByAshmem(uniqueFd, fastSize);
508     } else {
509         pixelMap = DecodeThumbnail(request->GetFd(), fastSize);
510     }
511     if (pixelMap == nullptr) {
512         request->error = E_FAIL;
513         return false;
514     }
515     request->SetFastPixelMap(move(pixelMap));
516     return true;
517 }
518 
DealWithFastRequest(const RequestSharedPtr &request)519 void ThumbnailManager::DealWithFastRequest(const RequestSharedPtr &request)
520 {
521     MediaLibraryTracer tracer;
522     tracer.Start("ThumbnailManager::DealWithFastRequest");
523 
524     if (request == nullptr) {
525         return;
526     }
527 
528     if (!RequestFastImage(request) && request->error != E_FAIL) {
529         // when local pixelmap not exit, must add QualityThread
530         AddQualityPhotoRequest(request);
531         return;
532     }
533 
534     // callback
535     NotifyImage(request);
536 }
537 
DealWithQualityRequest(const RequestSharedPtr &request)538 void ThumbnailManager::DealWithQualityRequest(const RequestSharedPtr &request)
539 {
540     MediaLibraryTracer tracer;
541     tracer.Start("ThumbnailManager::DealWithQualityRequest");
542 
543     unique_ptr<PixelMap> pixelMapPtr = nullptr;
544     if (request->GetFd().Get() > 0) {
545         pixelMapPtr = DecodeThumbnail(request->GetFd(), request->GetRequestSize());
546     } else {
547         pixelMapPtr = QueryThumbnail(request->GetUri(), request->GetRequestSize(), request->GetPath());
548     }
549 
550     if (pixelMapPtr == nullptr) {
551         NAPI_ERR_LOG("Can not get pixelMap");
552         request->error = E_FAIL;
553     }
554     request->SetPixelMap(move(pixelMapPtr));
555 
556     // callback
557     NotifyImage(request);
558 }
559 
ImageWorker(int num)560 void ThumbnailManager::ImageWorker(int num)
561 {
562     SetThreadName("ImageWorker", num);
563     while (true) {
564         if (!isThreadRunning_) {
565             return;
566         }
567         if (!fastQueue_.Empty()) {
568             RequestSharedPtr request;
569             if (fastQueue_.Pop(request) && request->NeedContinue()) {
570                 DealWithFastRequest(request);
571             }
572         } else if (!qualityQueue_.Empty()) {
573             RequestSharedPtr request;
574             if (qualityQueue_.Pop(request) && request->NeedContinue()) {
575                 DealWithQualityRequest(request);
576             }
577         } else {
578             std::unique_lock<std::mutex> lock(queueLock_);
579             queueCv_.wait(lock, [this]() {
580                 return !isThreadRunning_ || !(qualityQueue_.Empty() && fastQueue_.Empty());
581             });
582         }
583     }
584 }
585 
HandlePixelCallback(const RequestSharedPtr &request)586 static void HandlePixelCallback(const RequestSharedPtr &request)
587 {
588     napi_env env = request->callback_.env_;
589     napi_value jsCallback = nullptr;
590     napi_status status = napi_get_reference_value(env, request->callback_.callBackRef_, &jsCallback);
591     if (status != napi_ok) {
592         NAPI_ERR_LOG("Create reference fail, status: %{public}d", status);
593         return;
594     }
595 
596     napi_value retVal = nullptr;
597     napi_value result[ARGS_TWO];
598     if (request->GetStatus() == ThumbnailStatus::THUMB_REMOVE) {
599         return;
600     }
601 
602     if (request->error == E_FAIL) {
603         int32_t errorNum = MediaLibraryNapiUtils::TransErrorCode("requestPhoto", request->error);
604         MediaLibraryNapiUtils::CreateNapiErrorObject(env, result[PARAM0], errorNum,
605             "Failed to request Photo");
606     } else {
607         result[PARAM0] = nullptr;
608     }
609     if (request->GetStatus() == ThumbnailStatus::THUMB_FAST) {
610         result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
611             shared_ptr<PixelMap>(request->GetFastPixelMap()));
612     } else {
613         result[PARAM1] = Media::PixelMapNapi::CreatePixelMap(env,
614             shared_ptr<PixelMap>(request->GetPixelMap()));
615     }
616 
617     status = napi_call_function(env, nullptr, jsCallback, ARGS_TWO, result, &retVal);
618     if (status != napi_ok) {
619         NAPI_ERR_LOG("CallJs napi_call_function fail, status: %{public}d", status);
620         return;
621     }
622 }
623 
UvJsExecute(uv_work_t *work)624 static void UvJsExecute(uv_work_t *work)
625 {
626     // js thread
627     if (work == nullptr) {
628         return;
629     }
630 
631     ThumnailUv *uvMsg = reinterpret_cast<ThumnailUv *>(work->data);
632     if (uvMsg == nullptr) {
633         delete work;
634         return;
635     }
636     if (uvMsg->request_ == nullptr) {
637         delete uvMsg;
638         delete work;
639         return;
640     }
641     do {
642         napi_env env = uvMsg->request_->callback_.env_;
643         if (!uvMsg->request_->NeedContinue()) {
644             break;
645         }
646         NapiScopeHandler scopeHandler(env);
647         if (!scopeHandler.IsValid()) {
648             break;
649         }
650         HandlePixelCallback(uvMsg->request_);
651     } while (0);
652     if (uvMsg->manager_ == nullptr) {
653         delete uvMsg;
654         delete work;
655         return;
656     }
657     if (uvMsg->request_->GetStatus() == ThumbnailStatus::THUMB_FAST &&
658         NeedQualityThumb(uvMsg->request_->GetRequestSize(), uvMsg->request_->requestPhotoType)) {
659         uvMsg->manager_->AddQualityPhotoRequest(uvMsg->request_);
660     } else {
661         uvMsg->manager_->DeleteRequestIdFromMap(uvMsg->request_->GetUUID());
662         uvMsg->request_->ReleaseCallbackRef();
663     }
664 
665     delete uvMsg;
666     delete work;
667 }
668 
NotifyImage(const RequestSharedPtr &request)669 void ThumbnailManager::NotifyImage(const RequestSharedPtr &request)
670 {
671     MediaLibraryTracer tracer;
672     tracer.Start("ThumbnailManager::NotifyImage");
673 
674     if (!request->NeedContinue()) {
675         DeleteRequestIdFromMap(request->GetUUID());
676         return;
677     }
678 
679     uv_loop_s *loop = nullptr;
680     napi_get_uv_event_loop(request->callback_.env_, &loop);
681     if (loop == nullptr) {
682         DeleteRequestIdFromMap(request->GetUUID());
683         return;
684     }
685 
686     uv_work_t *work = new (nothrow) uv_work_t;
687     if (work == nullptr) {
688         DeleteRequestIdFromMap(request->GetUUID());
689         return;
690     }
691 
692     ThumnailUv *msg = new (nothrow) ThumnailUv(request, this);
693     if (msg == nullptr) {
694         delete work;
695         DeleteRequestIdFromMap(request->GetUUID());
696         return;
697     }
698 
699     work->data = reinterpret_cast<void *>(msg);
700     int ret = uv_queue_work(loop, work, [](uv_work_t *w) {}, [](uv_work_t *w, int s) {
701         UvJsExecute(w);
702     });
703     if (ret != 0) {
704         NAPI_ERR_LOG("Failed to execute libuv work queue, ret: %{public}d", ret);
705         delete msg;
706         delete work;
707         return;
708     }
709     return;
710 }
711 }
712 }
713