1 /*
2  * Copyright (C) 2021 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 #define MLOG_TAG "ScannerNapi"
16 
17 #include "media_scanner_napi.h"
18 #include "media_library_napi.h"
19 #include "medialibrary_db_const.h"
20 #include "medialibrary_errno.h"
21 #include "medialibrary_napi_log.h"
22 #include "userfile_client.h"
23 #include "medialibrary_client_errno.h"
24 
25 using OHOS::HiviewDFX::HiLog;
26 using OHOS::HiviewDFX::HiLogLabel;
27 using namespace std;
28 
29 namespace OHOS {
30 namespace Media {
31 thread_local napi_ref MediaScannerNapi::sConstructor_ = nullptr;
32 
MediaScannerNapi()33 MediaScannerNapi::MediaScannerNapi()
34     : env_(nullptr) {}
35 
~MediaScannerNapi()36 MediaScannerNapi::~MediaScannerNapi()
37 {}
38 
Init(napi_env env,napi_value exports)39 napi_value MediaScannerNapi::Init(napi_env env, napi_value exports)
40 {
41     napi_status status;
42     napi_value ctorObj;
43     int32_t refCount = 1;
44 
45     napi_property_descriptor scanner_props[] = {
46         DECLARE_NAPI_FUNCTION("scanDir", ScanDir),
47         DECLARE_NAPI_FUNCTION("scanFile", ScanFile)
48     };
49 
50     napi_property_descriptor static_prop[] = {
51         DECLARE_NAPI_STATIC_FUNCTION("getScannerInstance", GetMediaScannerInstance)
52     };
53 
54     status = napi_define_class(env, SCANNER_HELPER_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH,
55         MediaScannerNapiConstructor, nullptr, sizeof(scanner_props) / sizeof(scanner_props[PARAM0]),
56         scanner_props, &ctorObj);
57     if (status == napi_ok) {
58         if (napi_create_reference(env, ctorObj, refCount, &sConstructor_) == napi_ok) {
59             status = napi_set_named_property(env, exports, SCANNER_HELPER_NAPI_CLASS_NAME.c_str(), ctorObj);
60             if (status == napi_ok && napi_define_properties(env, exports,
61                 sizeof(static_prop) / sizeof(static_prop[PARAM0]), static_prop) == napi_ok) {
62                 return exports;
63             }
64         }
65     }
66     return nullptr;
67 }
68 
MediaScannerNapiConstructor(napi_env env,napi_callback_info info)69 napi_value MediaScannerNapi::MediaScannerNapiConstructor(napi_env env, napi_callback_info info)
70 {
71     napi_status status;
72     napi_value result = nullptr;
73     napi_value thisVar = nullptr;
74 
75     napi_get_undefined(env, &result);
76     GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
77 
78     if (status == napi_ok && thisVar != nullptr) {
79         unique_ptr<MediaScannerNapi> obj = make_unique<MediaScannerNapi>();
80         if (obj != nullptr) {
81             obj->env_ = env;
82 
83             obj->mediaScannerNapiCallbackObj_ = std::make_shared<MediaScannerNapiCallback>(env);
84             if (obj->mediaScannerNapiCallbackObj_ == nullptr) {
85                 NAPI_ERR_LOG("[MediaScannerNapiConstructor] callback instance creation failed!");
86                 return result;
87             }
88 
89             status = napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()),
90                                MediaScannerNapi::MediaScannerNapiDestructor, nullptr, nullptr);
91             if (status == napi_ok) {
92                 obj.release();
93                 return thisVar;
94             } else {
95                 NAPI_ERR_LOG("Failed to wrap the native media scanner client, status: %{private}d", status);
96             }
97         }
98     }
99 
100     NAPI_INFO_LOG("[MediaScannerNapiConstructor] failed");
101     return result;
102 }
103 
MediaScannerNapiDestructor(napi_env env,void * nativeObject,void * finalize_hint)104 void MediaScannerNapi::MediaScannerNapiDestructor(napi_env env, void *nativeObject, void *finalize_hint)
105 {
106     MediaScannerNapi *scannerHelper = reinterpret_cast<MediaScannerNapi*>(nativeObject);
107     if (scannerHelper != nullptr) {
108         delete scannerHelper;
109     }
110 }
111 
GetMediaScannerInstance(napi_env env,napi_callback_info info)112 napi_value MediaScannerNapi::GetMediaScannerInstance(napi_env env, napi_callback_info info)
113 {
114     napi_status status;
115     napi_value result = nullptr;
116     napi_value ctor;
117     size_t argc = ARGS_ONE;
118     napi_value argv[ARGS_ONE] = {0};
119     napi_value thisVar = nullptr;
120 
121     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
122     status = napi_get_reference_value(env, sConstructor_, &ctor);
123     if (status == napi_ok) {
124         status = napi_new_instance(env, ctor, argc, argv, &result);
125         if (status == napi_ok) {
126             NAPI_INFO_LOG("[GetMediaScannerInstance] success");
127             return result;
128         } else {
129             NAPI_ERR_LOG("[GetMediaScannerInstance] New instance could not be obtained, status: %{public}d", status);
130         }
131     }
132 
133     NAPI_ERR_LOG("[GetMediaScannerInstance] failed , status = %{public}d", status);
134     napi_get_undefined(env, &result);
135     return result;
136 }
137 
InvokeJSCallback(napi_env env,const int32_t errCode,const std::string & uri,napi_ref callbackRef)138 void InvokeJSCallback(napi_env env, const int32_t errCode, const std::string &uri, napi_ref callbackRef)
139 {
140     napi_value retVal = nullptr;
141     napi_value results[ARGS_TWO] = {nullptr};
142     napi_get_undefined(env, &results[PARAM0]);
143     napi_create_object(env, &results[PARAM1]);
144 
145     napi_value jsStatus = 0;
146     napi_create_int32(env, errCode, &jsStatus);
147     napi_set_named_property(env, results[PARAM1], "status", jsStatus);
148 
149     napi_value jsUri = 0;
150     napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &jsUri);
151     napi_set_named_property(env, results[PARAM1], "fileUri", jsUri);
152 
153     napi_value callback = nullptr;
154     napi_get_reference_value(env, callbackRef, &callback);
155     napi_call_function(env, nullptr, callback, ARGS_TWO, results, &retVal);
156 }
157 
NapiScanUtils(napi_env env,napi_callback_info info,const string & scanType)158 napi_value MediaScannerNapi::NapiScanUtils(napi_env env, napi_callback_info info, const string &scanType)
159 {
160     char buffer[PATH_MAX];
161     napi_status status;
162     napi_value result = nullptr;
163     size_t argc = ARGS_TWO;
164     napi_value argv[ARGS_TWO] = {0};
165     napi_value thisVar = nullptr;
166     MediaScannerNapi *obj = nullptr;
167     string event = "";
168     napi_ref callbackRef = nullptr;
169     const int32_t refCount = 1;
170     size_t res = 0;
171     int32_t errCode = 0;
172 
173     GET_JS_ARGS(env, info, argc, argv, thisVar);
174     NAPI_ASSERT(env, argc == ARGS_TWO, "requires 2 parameters");
175 
176     NAPI_INFO_LOG("[MediaScannerNapi::NapiScanUtils] start");
177     napi_get_undefined(env, &result);
178     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
179     if (status == napi_ok && obj != nullptr) {
180         if (argc == ARGS_TWO) {
181             napi_valuetype valueType = napi_undefined;
182             napi_typeof(env, argv[PARAM0], &valueType);
183             if (valueType == napi_string) {
184                 napi_get_value_string_utf8(env, argv[PARAM0], buffer, PATH_MAX, &res);
185                 event = string(buffer);
186             } else {
187                 NAPI_ERR_LOG("Invalid arg, valueType: %{public}d", valueType);
188                 return result;
189             }
190             napi_typeof(env, argv[PARAM1], &valueType);
191             if (valueType == napi_function) {
192                 napi_create_reference(env, argv[PARAM1], refCount, &callbackRef);
193             } else {
194                 NAPI_ERR_LOG("Invalid arg, valueType: %{public}d", valueType);
195                 return result;
196             }
197         }
198         errCode = 0;
199         DataShareScanBoardcast(event);
200 
201         if (errCode == 0) {
202             obj->mediaScannerNapiCallbackObj_->SetToMap(event, callbackRef);
203         } else {
204             // Invoke JS callback functions based on results
205             InvokeJSCallback(env, errCode, "", callbackRef);
206         }
207     }
208     NAPI_INFO_LOG("[MediaScannerNapi::NapiScanUtils] end");
209     return result;
210 }
211 
ScanFile(napi_env env,napi_callback_info info)212 napi_value MediaScannerNapi::ScanFile(napi_env env, napi_callback_info info)
213 {
214     return NapiScanUtils(env, info, "FILE");
215 }
216 
ScanDir(napi_env env,napi_callback_info info)217 napi_value MediaScannerNapi::ScanDir(napi_env env, napi_callback_info info)
218 {
219     return NapiScanUtils(env, info, "DIR");
220 }
221 
DataShareScanBoardcast(const std::string & event)222 void MediaScannerNapi::DataShareScanBoardcast(const std::string &event)
223 {
224     NAPI_INFO_LOG("MediaScannerNapi::DataShareScanBoardcast start, event: %{public}s", event.c_str());
225     Uri insertUri(URI_SCANNER);
226     OHOS::DataShare::DataShareValuesBucket valuesBucket;
227     valuesBucket.Put(MEDIA_DATA_DB_RELATIVE_PATH, event);
228     int index = UserFileClient::Insert(insertUri, valuesBucket);
229     if (index < 0) {
230         NAPI_ERR_LOG("[MediaScannerNapi::DataShareScanBoardcast return status %{public}d", index);
231     } else {
232         NAPI_INFO_LOG("[MediaScannerNapi::DataShareScanBoardcast success");
233     }
234 }
235 
OnScanFinished(const int32_t status,const std::string & uri,const std::string & path)236 int32_t MediaScannerNapiCallback::OnScanFinished(const int32_t status, const std::string &uri, const std::string &path)
237 {
238     auto itr = scannerMap_.find(path);
239     if (itr != scannerMap_.end()) {
240         // Invoke JS callback functions based on results
241         InvokeJSCallback(env_, status, uri, itr->second);
242         scannerMap_.erase(path);
243         NAPI_DEBUG_LOG("OnScanFinished exit");
244     }
245 
246     return 0;
247 }
248 
SetToMap(const std::string & path,const napi_ref & cbRef)249 void MediaScannerNapiCallback::SetToMap(const std::string &path, const napi_ref &cbRef)
250 {
251     scannerMap_.insert(std::make_pair(path, cbRef));
252 }
253 } // namespace Media
254 } // namespace OHOS
255