• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 LOG_TAG "JsKVManager"
16 #include "js_kv_manager.h"
17 #include "distributed_kv_data_manager.h"
18 #include "js_device_kv_store.h"
19 #include "js_single_kv_store.h"
20 #include "js_util.h"
21 #include "log_print.h"
22 #include "napi_queue.h"
23 #include "js_error_utils.h"
24 
25 using namespace OHOS::DistributedKv;
26 
27 namespace OHOS::DistributedKVStore {
IsStoreTypeSupported(Options options)28 static bool IsStoreTypeSupported(Options options)
29 {
30     return (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION)
31         || (options.kvStoreType == KvStoreType::SINGLE_VERSION);
32 }
33 
JsKVManager(const std::string & bundleName,napi_env env,ContextParam param)34 JsKVManager::JsKVManager(const std::string &bundleName, napi_env env, ContextParam param)
35     : bundleName_(bundleName), uvQueue_(std::make_shared<UvQueue>(env)),
36     param_(std::make_shared<ContextParam>(std::move(param)))
37 {
38 }
39 
~JsKVManager()40 JsKVManager::~JsKVManager()
41 {
42     ZLOGD("no memory leak for JsKVManager");
43     std::lock_guard<std::mutex> lck(deathMutex_);
44     for (auto& it : deathRecipient_) {
45         kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(it);
46         it->Clear();
47     }
48     deathRecipient_.clear();
49 }
50 
CreateKVManager(napi_env env,napi_callback_info info)51 napi_value JsKVManager::CreateKVManager(napi_env env, napi_callback_info info)
52 {
53     size_t argc = 2;
54     napi_value argv[2] = { nullptr };
55     napi_value result = nullptr;
56     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
57 
58     if (argc < 1) {
59         ThrowNapiError(env, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
60         return result;
61     }
62 
63     std::string bundleName;
64     napi_status status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
65     if (status == napi_generic_failure) {
66         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Missing bundleName parameter.");
67         return result;
68     }
69     if (bundleName.empty()) {
70         ThrowNapiError(env, Status::INVALID_ARGUMENT, "The type of bundleName must be string.");
71         return result;
72     }
73 
74     napi_value jsContext = nullptr;
75     status = JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
76     if (status == napi_generic_failure) {
77         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Missing context parameter.");
78         return result;
79     }
80 
81     status = napi_new_instance(env, Constructor(env), argc, argv, &result);
82     if (result == nullptr || status != napi_ok) {
83         ThrowNapiError(env, status, "KVManager::New failed!", false);
84         return result;
85     }
86 
87     return result;
88 }
89 
90 struct GetKVStoreContext : public ContextBase {
91     std::string storeId;
92     Options options;
93     JsSingleKVStore* kvStore = nullptr;
94     napi_ref ref = nullptr;
95 
GetCbInfoOHOS::DistributedKVStore::GetKVStoreContext96     void GetCbInfo(napi_env env, napi_callback_info info)
97     {
98         auto input = [env, this](size_t argc, napi_value* argv) {
99             // required 2 arguments :: <storeId> <options>
100             ASSERT_BUSINESS_ERR(this, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
101             status = JSUtil::GetValue(env, argv[0], storeId);
102             ASSERT_BUSINESS_ERR(this, ((status == napi_ok) && !storeId.empty()), Status::INVALID_ARGUMENT,
103                 "The type of storeId must be string.");
104             status = JSUtil::GetValue(env, argv[1], options);
105             ASSERT_BUSINESS_ERR(this, status == napi_ok, Status::INVALID_ARGUMENT, "The type of options is incorrect.");
106             ASSERT_BUSINESS_ERR(this, IsStoreTypeSupported(options), Status::INVALID_ARGUMENT,
107                 "The type of kvStoreType is incorrect.");
108             ZLOGD("GetKVStore kvStoreType=%{public}d", options.kvStoreType);
109             if (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION) {
110                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
111                                          JsDeviceKVStore::Constructor(env));
112             } else if (options.kvStoreType == KvStoreType::SINGLE_VERSION) {
113                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
114                                          JsSingleKVStore::Constructor(env));
115             }
116         };
117         ContextBase::GetCbInfo(env, info, input);
118     }
119 };
120 
121 /*
122  * [JS API Prototype]
123  * [AsyncCallback]
124  *      getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void;
125  * [Promise]
126  *      getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T>;
127  */
GetKVStore(napi_env env,napi_callback_info info)128 napi_value JsKVManager::GetKVStore(napi_env env, napi_callback_info info)
129 {
130     auto ctxt = std::make_shared<GetKVStoreContext>();
131     ctxt->GetCbInfo(env, info);
132     ASSERT_NULL(!ctxt->isThrowError, "GetKVStore exit");
133 
134     auto execute = [ctxt]() {
135         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
136         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
137         AppId appId = { kvm->bundleName_ };
138         StoreId storeId = { ctxt->storeId };
139         ctxt->options.baseDir = kvm->param_->baseDir;
140         ctxt->options.area = kvm->param_->area + 1;
141         ctxt->options.hapName = kvm->param_->hapName;
142         ZLOGD("Options area:%{public}d dir:%{public}s", ctxt->options.area, ctxt->options.baseDir.c_str());
143         std::shared_ptr<DistributedKv::SingleKvStore> kvStore;
144         Status status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
145         if (status == CRYPT_ERROR) {
146             ctxt->options.rebuild = true;
147             status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
148             ZLOGE("Data has corrupted, rebuild db");
149         }
150 
151         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
152             napi_ok : napi_generic_failure;
153         ctxt->kvStore->SetKvStorePtr(kvStore);
154         ctxt->kvStore->SetSchemaInfo(!ctxt->options.schema.empty());
155         ctxt->kvStore->SetContextParam(kvm->param_);
156         ctxt->kvStore->SetUvQueue(kvm->uvQueue_);
157     };
158     auto output = [env, ctxt](napi_value& result) {
159         ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
160         napi_delete_reference(env, ctxt->ref);
161         ASSERT_STATUS(ctxt, "output KVManager failed");
162     };
163     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
164 }
165 
166 /*
167  * [JS API Prototype]
168  * [AsyncCB]  closeKVStore(appId: string, storeId: string, kvStore: KVStore, callback: AsyncCallback<void>):void
169  * [Promise]  closeKVStore(appId: string, storeId: string, kvStore: KVStore):Promise<void>
170  */
CloseKVStore(napi_env env,napi_callback_info info)171 napi_value JsKVManager::CloseKVStore(napi_env env, napi_callback_info info)
172 {
173     struct ContextInfo : public ContextBase {
174         std::string appId;
175         std::string storeId;
176         napi_value kvStore;
177     };
178     auto ctxt = std::make_shared<ContextInfo>();
179     auto input = [env, ctxt](size_t argc, napi_value* argv) {
180         // required 3 arguments :: <appId> <storeId> <kvStore>
181         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
182         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
183         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
184             "The type of appId must be string.");
185         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->storeId);
186         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->storeId.empty(), Status::INVALID_ARGUMENT,
187             "The type of storeId must be string.");
188     };
189     ctxt->GetCbInfo(env, info, input);
190     ASSERT_NULL(!ctxt->isThrowError, "CloseKVStore exits");
191 
192     auto execute = [ctxt]() {
193         AppId appId { ctxt->appId };
194         StoreId storeId { ctxt->storeId };
195         Status status = reinterpret_cast<JsKVManager*>(ctxt->native)->kvDataManager_.CloseKvStore(appId, storeId);
196         status = GenerateNapiError(status, ctxt->jsCode, ctxt->error);
197         ZLOGD("CloseKVStore return status:%{public}d", status);
198         ctxt->status
199             = (status == Status::SUCCESS) || (status == Status::STORE_NOT_FOUND) || (status == Status::STORE_NOT_OPEN)
200             ? napi_ok
201             : napi_generic_failure;
202     };
203     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
204 }
205 
206 /*
207  * [JS API Prototype]
208  * [AsyncCB]  deleteKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void
209  * [Promise]  deleteKVStore(appId: string, storeId: string):Promise<void>
210  */
DeleteKVStore(napi_env env,napi_callback_info info)211 napi_value JsKVManager::DeleteKVStore(napi_env env, napi_callback_info info)
212 {
213     struct ContextInfo : public ContextBase {
214         std::string appId;
215         std::string storeId;
216     };
217     auto ctxt = std::make_shared<ContextInfo>();
218     auto input = [env, ctxt](size_t argc, napi_value* argv) {
219         // required 2 arguments :: <appId> <storeId>
220         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
221         size_t index = 0;
222         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->appId);
223         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
224             "The parameters of appId is incorrect.");
225         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->storeId);
226         ASSERT_BUSINESS_ERR(ctxt, !ctxt->storeId.empty(), Status::INVALID_ARGUMENT,
227             "The parameters of storeId is incorrect.");
228     };
229     ctxt->GetCbInfo(env, info, input);
230     ASSERT_NULL(!ctxt->isThrowError, "DeleteKVStore exits");
231 
232     auto execute = [ctxt]() {
233         AppId appId { ctxt->appId };
234         StoreId storeId { ctxt->storeId };
235         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
236         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
237         std::string databaseDir = kvm->param_->baseDir;
238         ZLOGD("DeleteKVStore databaseDir is: %{public}s", databaseDir.c_str());
239         Status status = kvm->kvDataManager_.DeleteKvStore(appId, storeId, databaseDir);
240         ZLOGD("DeleteKvStore status:%{public}d", status);
241         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
242             napi_ok : napi_generic_failure;
243     };
244     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
245 }
246 
247 /*
248  * [JS API Prototype]
249  * [AsyncCB]  getAllKVStoreId(appId: string, callback: AsyncCallback<string[]>):void
250  * [Promise]  getAllKVStoreId(appId: string):Promise<string[]>
251  */
GetAllKVStoreId(napi_env env,napi_callback_info info)252 napi_value JsKVManager::GetAllKVStoreId(napi_env env, napi_callback_info info)
253 {
254     struct ContextInfo : public ContextBase {
255         std::string appId;
256         std::vector<StoreId> storeIdList;
257     };
258 
259     auto ctxt = std::make_shared<ContextInfo>();
260     auto input = [env, ctxt](size_t argc, napi_value* argv) {
261         // required 1 arguments :: <appId>
262         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
263         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
264         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
265             "The parameters of appId is incorrect.");
266     };
267     ctxt->GetCbInfo(env, info, input);
268     ASSERT_NULL(!ctxt->isThrowError, "GetAllKVStoreId exits");
269 
270     auto execute = [ctxt]() {
271         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
272         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
273         AppId appId { ctxt->appId };
274         Status status = kvm->kvDataManager_.GetAllKvStoreId(appId, ctxt->storeIdList);
275         ZLOGD("execute status:%{public}d", status);
276         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
277             napi_ok : napi_generic_failure;
278     };
279     auto output = [env, ctxt](napi_value& result) {
280         ctxt->status = JSUtil::SetValue(env, ctxt->storeIdList, result);
281         ZLOGD("output status:%{public}d", ctxt->status);
282     };
283     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
284 }
285 
On(napi_env env,napi_callback_info info)286 napi_value JsKVManager::On(napi_env env, napi_callback_info info)
287 {
288     auto ctxt = std::make_shared<ContextBase>();
289     auto input = [env, ctxt](size_t argc, napi_value* argv) {
290         // required 2 arguments :: <event> <callback>
291         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
292         std::string event;
293         ctxt->status = JSUtil::GetValue(env, argv[0], event);
294         ZLOGI("subscribe to event:%{public}s", event.c_str());
295         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
296             "The parameters of event is incorrect.");
297 
298         napi_valuetype valueType = napi_undefined;
299         ctxt->status = napi_typeof(env, argv[1], &valueType);
300         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function), Status::INVALID_ARGUMENT,
301             "The type of parameters deathCallback must be a function.");
302 
303         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
304         ASSERT_BUSINESS_ERR(ctxt, proxy != nullptr, Status::INVALID_ARGUMENT, "there is no native kv manager.");
305 
306         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
307         for (auto& it : proxy->deathRecipient_) {
308             if (JSUtil::Equals(env, argv[1], it->GetCallback())) {
309                 ZLOGD("KVManager::On callback already register!");
310                 return;
311             }
312         }
313         auto deathRecipient = std::make_shared<DeathRecipient>(proxy->uvQueue_, argv[1]);
314         proxy->kvDataManager_.RegisterKvStoreServiceDeathRecipient(deathRecipient);
315         proxy->deathRecipient_.push_back(deathRecipient);
316         ZLOGD("on mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
317     };
318     ctxt->GetCbInfoSync(env, info, input);
319     if (ctxt->status != napi_ok) {
320         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
321     }
322     return nullptr;
323 }
324 
Off(napi_env env,napi_callback_info info)325 napi_value JsKVManager::Off(napi_env env, napi_callback_info info)
326 {
327     auto ctxt = std::make_shared<ContextBase>();
328     auto input = [env, ctxt](size_t argc, napi_value* argv) {
329         // required 1 or 2 arguments :: <event> [callback]
330         ASSERT_BUSINESS_ERR(ctxt, argc > 0, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
331         std::string event;
332         ctxt->status = JSUtil::GetValue(env, argv[0], event);
333         // required 1 arguments :: <event>
334         ZLOGI("unsubscribe to event:%{public}s %{public}s specified", event.c_str(), (argc == 1) ? "without" : "with");
335         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
336             "The parameters of event is incorrect.");
337         // have 2 arguments :: have the [callback]
338         if (argc == 2) {
339             napi_valuetype valueType = napi_undefined;
340             ctxt->status = napi_typeof(env, argv[1], &valueType);
341             ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function),
342                 Status::INVALID_ARGUMENT, "The type of parameters deathCallback must be a function.");
343         }
344         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
345         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
346         auto it = proxy->deathRecipient_.begin();
347         while (it != proxy->deathRecipient_.end()) {
348             // have 2 arguments :: have the [callback]
349             if ((argc == 1) || JSUtil::Equals(env, argv[1], (*it)->GetCallback())) {
350                 proxy->kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(*it);
351                 (*it)->Clear();
352                 it = proxy->deathRecipient_.erase(it);
353             } else {
354                 ++it;
355             }
356         }
357         ZLOGD("off mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
358     };
359     ctxt->GetCbInfoSync(env, info, input);
360     if (ctxt->status != napi_ok) {
361         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
362     }
363     ZLOGD("KVManager::Off callback is not register or already unregister!");
364     return nullptr;
365 }
366 
Constructor(napi_env env)367 napi_value JsKVManager::Constructor(napi_env env)
368 {
369     const napi_property_descriptor properties[] = {
370         DECLARE_NAPI_FUNCTION("getKVStore", JsKVManager::GetKVStore),
371         DECLARE_NAPI_FUNCTION("closeKVStore", JsKVManager::CloseKVStore),
372         DECLARE_NAPI_FUNCTION("deleteKVStore", JsKVManager::DeleteKVStore),
373         DECLARE_NAPI_FUNCTION("getAllKVStoreId", JsKVManager::GetAllKVStoreId),
374         DECLARE_NAPI_FUNCTION("on", JsKVManager::On),
375         DECLARE_NAPI_FUNCTION("off", JsKVManager::Off)
376     };
377     size_t count = sizeof(properties) / sizeof(properties[0]);
378     return JSUtil::DefineClass(env, "JsKVManager", properties, count, JsKVManager::New);
379 }
380 
New(napi_env env,napi_callback_info info)381 napi_value JsKVManager::New(napi_env env, napi_callback_info info)
382 {
383     std::string bundleName;
384     ContextParam param;
385     auto ctxt = std::make_shared<ContextBase>();
386     auto input = [env, ctxt, &bundleName, &param](size_t argc, napi_value* argv) {
387         // required 1 arguments :: <bundleName>
388         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
389         ctxt->status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
390         ASSERT_BUSINESS_ERR(ctxt, ctxt->status != napi_generic_failure, Status::INVALID_ARGUMENT,
391             "Missing bundleName parameter.");
392         ASSERT_BUSINESS_ERR(ctxt, !bundleName.empty(), Status::INVALID_ARGUMENT,
393             "The type of bundleName must be string.");
394 
395         napi_value jsContext = nullptr;
396         JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
397         ctxt->status = JSUtil::GetValue(env, jsContext, param);
398         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT, "get context parameter failed.");
399     };
400     ctxt->GetCbInfoSync(env, info, input);
401     ASSERT_NULL(!ctxt->isThrowError, "JsKVManager New exit");
402 
403     JsKVManager* kvManager = new (std::nothrow) JsKVManager(bundleName, env, param);
404     ASSERT_ERR(env, kvManager != nullptr, Status::INVALID_ARGUMENT, "no memory for kvManager.");
405 
406     auto finalize = [](napi_env env, void* data, void* hint) {
407         ZLOGD("kvManager finalize.");
408         auto* kvManager = reinterpret_cast<JsKVManager*>(data);
409         ASSERT_VOID(kvManager != nullptr, "finalize null!");
410         delete kvManager;
411     };
412     ASSERT_CALL(env, napi_wrap(env, ctxt->self, kvManager, finalize, nullptr, nullptr), kvManager);
413     return ctxt->self;
414 }
415 
OnRemoteDied()416 void JsKVManager::DeathRecipient::OnRemoteDied()
417 {
418     AsyncCall();
419 }
420 } // namespace OHOS::DistributedKVStore
421