• 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         ZLOGI("output delete reference success");
163     };
164     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
165 }
166 
167 /*
168  * [JS API Prototype]
169  * [AsyncCB]  closeKVStore(appId: string, storeId: string, kvStore: KVStore, callback: AsyncCallback<void>):void
170  * [Promise]  closeKVStore(appId: string, storeId: string, kvStore: KVStore):Promise<void>
171  */
CloseKVStore(napi_env env,napi_callback_info info)172 napi_value JsKVManager::CloseKVStore(napi_env env, napi_callback_info info)
173 {
174     struct ContextInfo : public ContextBase {
175         std::string appId;
176         std::string storeId;
177         napi_value kvStore;
178     };
179     auto ctxt = std::make_shared<ContextInfo>();
180     auto input = [env, ctxt](size_t argc, napi_value* argv) {
181         // required 3 arguments :: <appId> <storeId> <kvStore>
182         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
183         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
184         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
185             "The type of appId must be string.");
186         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->storeId);
187         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->storeId.empty(), Status::INVALID_ARGUMENT,
188             "The type of storeId must be string.");
189     };
190     ctxt->GetCbInfo(env, info, input);
191     ASSERT_NULL(!ctxt->isThrowError, "CloseKVStore exits");
192 
193     auto execute = [ctxt]() {
194         AppId appId { ctxt->appId };
195         StoreId storeId { ctxt->storeId };
196         Status status = reinterpret_cast<JsKVManager*>(ctxt->native)->kvDataManager_.CloseKvStore(appId, storeId);
197         status = GenerateNapiError(status, ctxt->jsCode, ctxt->error);
198         ZLOGD("CloseKVStore return status:%{public}d", status);
199         ctxt->status
200             = (status == Status::SUCCESS) || (status == Status::STORE_NOT_FOUND) || (status == Status::STORE_NOT_OPEN)
201             ? napi_ok
202             : napi_generic_failure;
203     };
204     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
205 }
206 
207 /*
208  * [JS API Prototype]
209  * [AsyncCB]  deleteKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void
210  * [Promise]  deleteKVStore(appId: string, storeId: string):Promise<void>
211  */
DeleteKVStore(napi_env env,napi_callback_info info)212 napi_value JsKVManager::DeleteKVStore(napi_env env, napi_callback_info info)
213 {
214     struct ContextInfo : public ContextBase {
215         std::string appId;
216         std::string storeId;
217     };
218     auto ctxt = std::make_shared<ContextInfo>();
219     auto input = [env, ctxt](size_t argc, napi_value* argv) {
220         // required 2 arguments :: <appId> <storeId>
221         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
222         size_t index = 0;
223         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->appId);
224         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
225             "The parameters of appId is incorrect.");
226         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->storeId);
227         ASSERT_BUSINESS_ERR(ctxt, !ctxt->storeId.empty(), Status::INVALID_ARGUMENT,
228             "The parameters of storeId is incorrect.");
229     };
230     ctxt->GetCbInfo(env, info, input);
231     ASSERT_NULL(!ctxt->isThrowError, "DeleteKVStore exits");
232 
233     auto execute = [ctxt]() {
234         AppId appId { ctxt->appId };
235         StoreId storeId { ctxt->storeId };
236         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
237         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
238         std::string databaseDir = kvm->param_->baseDir;
239         ZLOGD("DeleteKVStore databaseDir is: %{public}s", databaseDir.c_str());
240         Status status = kvm->kvDataManager_.DeleteKvStore(appId, storeId, databaseDir);
241         ZLOGD("DeleteKvStore status:%{public}d", status);
242         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
243             napi_ok : napi_generic_failure;
244     };
245     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
246 }
247 
248 /*
249  * [JS API Prototype]
250  * [AsyncCB]  getAllKVStoreId(appId: string, callback: AsyncCallback<string[]>):void
251  * [Promise]  getAllKVStoreId(appId: string):Promise<string[]>
252  */
GetAllKVStoreId(napi_env env,napi_callback_info info)253 napi_value JsKVManager::GetAllKVStoreId(napi_env env, napi_callback_info info)
254 {
255     struct ContextInfo : public ContextBase {
256         std::string appId;
257         std::vector<StoreId> storeIdList;
258     };
259 
260     auto ctxt = std::make_shared<ContextInfo>();
261     auto input = [env, ctxt](size_t argc, napi_value* argv) {
262         // required 1 arguments :: <appId>
263         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
264         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
265         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
266             "The parameters of appId is incorrect.");
267     };
268     ctxt->GetCbInfo(env, info, input);
269     ASSERT_NULL(!ctxt->isThrowError, "GetAllKVStoreId exits");
270 
271     auto execute = [ctxt]() {
272         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
273         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
274         AppId appId { ctxt->appId };
275         Status status = kvm->kvDataManager_.GetAllKvStoreId(appId, ctxt->storeIdList);
276         ZLOGD("execute status:%{public}d", status);
277         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
278             napi_ok : napi_generic_failure;
279     };
280     auto output = [env, ctxt](napi_value& result) {
281         ctxt->status = JSUtil::SetValue(env, ctxt->storeIdList, result);
282         ZLOGD("output status:%{public}d", ctxt->status);
283     };
284     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
285 }
286 
On(napi_env env,napi_callback_info info)287 napi_value JsKVManager::On(napi_env env, napi_callback_info info)
288 {
289     auto ctxt = std::make_shared<ContextBase>();
290     auto input = [env, ctxt](size_t argc, napi_value* argv) {
291         // required 2 arguments :: <event> <callback>
292         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
293         std::string event;
294         ctxt->status = JSUtil::GetValue(env, argv[0], event);
295         ZLOGI("subscribe to event:%{public}s", event.c_str());
296         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
297             "The parameters of event is incorrect.");
298 
299         napi_valuetype valueType = napi_undefined;
300         ctxt->status = napi_typeof(env, argv[1], &valueType);
301         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function), Status::INVALID_ARGUMENT,
302             "The type of parameters deathCallback must be a function.");
303 
304         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
305         ASSERT_BUSINESS_ERR(ctxt, proxy != nullptr, Status::INVALID_ARGUMENT, "there is no native kv manager.");
306 
307         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
308         for (auto& it : proxy->deathRecipient_) {
309             if (JSUtil::Equals(env, argv[1], it->GetCallback())) {
310                 ZLOGD("KVManager::On callback already register!");
311                 return;
312             }
313         }
314         auto deathRecipient = std::make_shared<DeathRecipient>(proxy->uvQueue_, argv[1]);
315         proxy->kvDataManager_.RegisterKvStoreServiceDeathRecipient(deathRecipient);
316         proxy->deathRecipient_.push_back(deathRecipient);
317         ZLOGD("on mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
318     };
319     ctxt->GetCbInfoSync(env, info, input);
320     if (ctxt->status != napi_ok) {
321         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
322     }
323     return nullptr;
324 }
325 
Off(napi_env env,napi_callback_info info)326 napi_value JsKVManager::Off(napi_env env, napi_callback_info info)
327 {
328     auto ctxt = std::make_shared<ContextBase>();
329     auto input = [env, ctxt](size_t argc, napi_value* argv) {
330         // required 1 or 2 arguments :: <event> [callback]
331         ASSERT_BUSINESS_ERR(ctxt, argc > 0, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
332         std::string event;
333         ctxt->status = JSUtil::GetValue(env, argv[0], event);
334         // required 1 arguments :: <event>
335         ZLOGI("unsubscribe to event:%{public}s %{public}s specified", event.c_str(), (argc == 1) ? "without" : "with");
336         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
337             "The parameters of event is incorrect.");
338         // have 2 arguments :: have the [callback]
339         if (argc == 2) {
340             napi_valuetype valueType = napi_undefined;
341             ctxt->status = napi_typeof(env, argv[1], &valueType);
342             ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function),
343                 Status::INVALID_ARGUMENT, "The type of parameters deathCallback must be a function.");
344         }
345         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
346         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
347         auto it = proxy->deathRecipient_.begin();
348         while (it != proxy->deathRecipient_.end()) {
349             // have 2 arguments :: have the [callback]
350             if ((argc == 1) || JSUtil::Equals(env, argv[1], (*it)->GetCallback())) {
351                 proxy->kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(*it);
352                 (*it)->Clear();
353                 it = proxy->deathRecipient_.erase(it);
354             } else {
355                 ++it;
356             }
357         }
358         ZLOGD("off mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
359     };
360     ctxt->GetCbInfoSync(env, info, input);
361     if (ctxt->status != napi_ok) {
362         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
363     }
364     ZLOGD("KVManager::Off callback is not register or already unregister!");
365     return nullptr;
366 }
367 
Constructor(napi_env env)368 napi_value JsKVManager::Constructor(napi_env env)
369 {
370     auto lambda = []() -> std::vector<napi_property_descriptor> {
371         std::vector<napi_property_descriptor> properties = {
372             DECLARE_NAPI_FUNCTION("getKVStore", JsKVManager::GetKVStore),
373             DECLARE_NAPI_FUNCTION("closeKVStore", JsKVManager::CloseKVStore),
374             DECLARE_NAPI_FUNCTION("deleteKVStore", JsKVManager::DeleteKVStore),
375             DECLARE_NAPI_FUNCTION("getAllKVStoreId", JsKVManager::GetAllKVStoreId),
376             DECLARE_NAPI_FUNCTION("on", JsKVManager::On),
377             DECLARE_NAPI_FUNCTION("off", JsKVManager::Off),
378         };
379         return properties;
380     };
381     return JSUtil::DefineClass(env, "ohos.data.distributedKVStore", "JsKVManager", lambda, JsKVManager::New);
382 }
383 
New(napi_env env,napi_callback_info info)384 napi_value JsKVManager::New(napi_env env, napi_callback_info info)
385 {
386     std::string bundleName;
387     ContextParam param;
388     auto ctxt = std::make_shared<ContextBase>();
389     auto input = [env, ctxt, &bundleName, &param](size_t argc, napi_value* argv) {
390         // required 1 arguments :: <bundleName>
391         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT, "The number of parameters is incorrect.");
392         ctxt->status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
393         ASSERT_BUSINESS_ERR(ctxt, ctxt->status != napi_generic_failure, Status::INVALID_ARGUMENT,
394             "Missing bundleName parameter.");
395         ASSERT_BUSINESS_ERR(ctxt, !bundleName.empty(), Status::INVALID_ARGUMENT,
396             "The type of bundleName must be string.");
397 
398         napi_value jsContext = nullptr;
399         JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
400         ctxt->status = JSUtil::GetValue(env, jsContext, param);
401         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT, "get context parameter failed.");
402     };
403     ctxt->GetCbInfoSync(env, info, input);
404     ASSERT_NULL(!ctxt->isThrowError, "JsKVManager New exit");
405 
406     JsKVManager* kvManager = new (std::nothrow) JsKVManager(bundleName, env, param);
407     ASSERT_ERR(env, kvManager != nullptr, Status::INVALID_ARGUMENT, "no memory for kvManager.");
408 
409     auto finalize = [](napi_env env, void* data, void* hint) {
410         ZLOGD("kvManager finalize.");
411         auto* kvManager = reinterpret_cast<JsKVManager*>(data);
412         ASSERT_VOID(kvManager != nullptr, "kvManager is null!");
413         delete kvManager;
414     };
415     ASSERT_CALL(env, napi_wrap(env, ctxt->self, kvManager, finalize, nullptr, nullptr), kvManager);
416     return ctxt->self;
417 }
418 
OnRemoteDied()419 void JsKVManager::DeathRecipient::OnRemoteDied()
420 {
421     AsyncCall();
422 }
423 } // namespace OHOS::DistributedKVStore
424