• 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, "Parameter error:Mandatory parameters are left unspecified");
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, "Parameter error:Missing bundleName parameter.");
67         return result;
68     }
69     if (bundleName.empty()) {
70         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error: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, "Parameter error: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,
101                 "Parameter error:Mandatory parameters are left unspecified");
102             status = JSUtil::GetValue(env, argv[0], storeId);
103             ASSERT_BUSINESS_ERR(this, ((status == napi_ok) && JSUtil::IsValid(storeId)), Status::INVALID_ARGUMENT,
104                 "Parameter error:storeId must be string,consist of letters,digits,underscores(_),limit 128 chars");
105             status = JSUtil::GetValue(env, argv[1], options);
106             ASSERT_BUSINESS_ERR(this, status == napi_ok, Status::INVALID_ARGUMENT,
107                 "Parameter error:The params type not matching option");
108             ASSERT_BUSINESS_ERR(this, options.securityLevel != INVALID_LABEL, Status::INVALID_ARGUMENT,
109                 "Parameter error:unusable securityLevel");
110             ASSERT_BUSINESS_ERR(this, IsStoreTypeSupported(options), Status::INVALID_ARGUMENT,
111                 "Parameter error:only support DEVICE_COLLABORATION or SINGLE_VERSION");
112             ZLOGD("GetKVStore kvStoreType=%{public}d", options.kvStoreType);
113             if (options.kvStoreType == KvStoreType::DEVICE_COLLABORATION) {
114                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
115                                          JsDeviceKVStore::Constructor(env));
116             } else if (options.kvStoreType == KvStoreType::SINGLE_VERSION) {
117                 ref = JSUtil::NewWithRef(env, argc, argv, reinterpret_cast<void**>(&kvStore),
118                                          JsSingleKVStore::Constructor(env));
119             }
120         };
121         ContextBase::GetCbInfo(env, info, input);
122     }
123 };
124 
125 /*
126  * [JS API Prototype]
127  * [AsyncCallback]
128  *      getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void;
129  * [Promise]
130  *      getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T>;
131  */
GetKVStore(napi_env env,napi_callback_info info)132 napi_value JsKVManager::GetKVStore(napi_env env, napi_callback_info info)
133 {
134     auto ctxt = std::make_shared<GetKVStoreContext>();
135     ctxt->GetCbInfo(env, info);
136     ASSERT_NULL(!ctxt->isThrowError, "GetKVStore exit");
137 
138     auto execute = [ctxt]() {
139         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
140         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
141         AppId appId = { kvm->bundleName_ };
142         StoreId storeId = { ctxt->storeId };
143         ctxt->options.baseDir = kvm->param_->baseDir;
144         ctxt->options.area = kvm->param_->area + 1;
145         ctxt->options.hapName = kvm->param_->hapName;
146         ctxt->options.apiVersion = kvm->param_->apiVersion;
147         ZLOGD("Options area:%{public}d dir:%{public}s", ctxt->options.area, ctxt->options.baseDir.c_str());
148         std::shared_ptr<DistributedKv::SingleKvStore> kvStore;
149         Status status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
150         if (status == DATA_CORRUPTED) {
151             ctxt->options.rebuild = true;
152             status = kvm->kvDataManager_.GetSingleKvStore(ctxt->options, appId, storeId, kvStore);
153             ZLOGE("Data has corrupted, rebuild db");
154         }
155         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
156             napi_ok : napi_generic_failure;
157         ctxt->kvStore->SetKvStorePtr(kvStore);
158         ctxt->kvStore->SetSchemaInfo(!ctxt->options.schema.empty());
159         ctxt->kvStore->SetContextParam(kvm->param_);
160         ctxt->kvStore->SetUvQueue(kvm->uvQueue_);
161     };
162     auto output = [env, ctxt](napi_value& result) {
163         ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
164         napi_delete_reference(env, ctxt->ref);
165         ASSERT_STATUS(ctxt, "output KVManager failed");
166         ZLOGI("output delete reference success");
167     };
168     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
169 }
170 
171 /*
172  * [JS API Prototype]
173  * [AsyncCB]  closeKVStore(appId: string, storeId: string, kvStore: KVStore, callback: AsyncCallback<void>):void
174  * [Promise]  closeKVStore(appId: string, storeId: string, kvStore: KVStore):Promise<void>
175  */
CloseKVStore(napi_env env,napi_callback_info info)176 napi_value JsKVManager::CloseKVStore(napi_env env, napi_callback_info info)
177 {
178     struct ContextInfo : public ContextBase {
179         std::string appId;
180         std::string storeId;
181         napi_value kvStore;
182     };
183     auto ctxt = std::make_shared<ContextInfo>();
184     auto input = [env, ctxt](size_t argc, napi_value* argv) {
185         // required 3 arguments :: <appId> <storeId> <kvStore>
186         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
187             "Parameter error:Mandatory parameters are left unspecified");
188         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
189         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
190             "Parameter error:appId empty");
191         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->storeId);
192         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && JSUtil::IsValid(ctxt->storeId), Status::INVALID_ARGUMENT,
193             "Parameter error:storeId must be string,consist of letters, digits, underscores(_), limit 128 characters");
194     };
195     ctxt->GetCbInfo(env, info, input);
196     ASSERT_NULL(!ctxt->isThrowError, "CloseKVStore exits");
197 
198     auto execute = [ctxt]() {
199         AppId appId { ctxt->appId };
200         StoreId storeId { ctxt->storeId };
201         Status status = reinterpret_cast<JsKVManager*>(ctxt->native)->kvDataManager_.CloseKvStore(appId, storeId);
202         status = GenerateNapiError(status, ctxt->jsCode, ctxt->error);
203         ZLOGD("CloseKVStore return status:%{public}d", status);
204         ctxt->status
205             = (status == Status::SUCCESS) || (status == Status::STORE_NOT_FOUND) || (status == Status::STORE_NOT_OPEN)
206             ? napi_ok
207             : napi_generic_failure;
208     };
209     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
210 }
211 
212 /*
213  * [JS API Prototype]
214  * [AsyncCB]  deleteKVStore(appId: string, storeId: string, callback: AsyncCallback<void>): void
215  * [Promise]  deleteKVStore(appId: string, storeId: string):Promise<void>
216  */
DeleteKVStore(napi_env env,napi_callback_info info)217 napi_value JsKVManager::DeleteKVStore(napi_env env, napi_callback_info info)
218 {
219     struct ContextInfo : public ContextBase {
220         std::string appId;
221         std::string storeId;
222     };
223     auto ctxt = std::make_shared<ContextInfo>();
224     auto input = [env, ctxt](size_t argc, napi_value* argv) {
225         // required 2 arguments :: <appId> <storeId>
226         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
227             "Parameter error:Mandatory parameters are left unspecified");
228         size_t index = 0;
229         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->appId);
230         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
231             "Parameter error:appId empty");
232         ctxt->status = JSUtil::GetValue(env, argv[index++], ctxt->storeId);
233         ASSERT_BUSINESS_ERR(ctxt, JSUtil::IsValid(ctxt->storeId), Status::INVALID_ARGUMENT,
234             "error:storeId must be string; consist of only letters, digits underscores (_),limit 128 characters");
235     };
236     ctxt->GetCbInfo(env, info, input);
237     ASSERT_NULL(!ctxt->isThrowError, "DeleteKVStore exits");
238 
239     auto execute = [ctxt]() {
240         AppId appId { ctxt->appId };
241         StoreId storeId { ctxt->storeId };
242         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
243         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
244         std::string databaseDir = kvm->param_->baseDir;
245         ZLOGD("DeleteKVStore databaseDir is: %{public}s", databaseDir.c_str());
246         Status status = kvm->kvDataManager_.DeleteKvStore(appId, storeId, databaseDir);
247         ZLOGD("DeleteKvStore status:%{public}d", status);
248         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
249             napi_ok : napi_generic_failure;
250     };
251     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
252 }
253 
254 /*
255  * [JS API Prototype]
256  * [AsyncCB]  getAllKVStoreId(appId: string, callback: AsyncCallback<string[]>):void
257  * [Promise]  getAllKVStoreId(appId: string):Promise<string[]>
258  */
GetAllKVStoreId(napi_env env,napi_callback_info info)259 napi_value JsKVManager::GetAllKVStoreId(napi_env env, napi_callback_info info)
260 {
261     struct ContextInfo : public ContextBase {
262         std::string appId;
263         std::vector<StoreId> storeIdList;
264     };
265 
266     auto ctxt = std::make_shared<ContextInfo>();
267     auto input = [env, ctxt](size_t argc, napi_value* argv) {
268         // required 1 arguments :: <appId>
269         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
270             "Parameter error:Mandatory parameters are left unspecified");
271         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->appId);
272         ASSERT_BUSINESS_ERR(ctxt, !ctxt->appId.empty(), Status::INVALID_ARGUMENT,
273             "Parameter error:appId empty");
274         ASSERT_BUSINESS_ERR(ctxt, (ctxt->appId.size() < MAX_APP_ID_LEN), Status::INVALID_ARGUMENT,
275             "Parameter error:appId exceed 256 characters");
276     };
277     ctxt->GetCbInfo(env, info, input);
278     ASSERT_NULL(!ctxt->isThrowError, "GetAllKVStoreId exits");
279 
280     auto execute = [ctxt]() {
281         auto kvm = reinterpret_cast<JsKVManager*>(ctxt->native);
282         ASSERT_ARGS(ctxt, kvm != nullptr, "KVManager is null, failed!");
283         AppId appId { ctxt->appId };
284         Status status = kvm->kvDataManager_.GetAllKvStoreId(appId, ctxt->storeIdList);
285         ZLOGD("execute status:%{public}d", status);
286         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
287             napi_ok : napi_generic_failure;
288     };
289     auto output = [env, ctxt](napi_value& result) {
290         ctxt->status = JSUtil::SetValue(env, ctxt->storeIdList, result);
291         ZLOGD("output status:%{public}d", ctxt->status);
292     };
293     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
294 }
295 
On(napi_env env,napi_callback_info info)296 napi_value JsKVManager::On(napi_env env, napi_callback_info info)
297 {
298     auto ctxt = std::make_shared<ContextBase>();
299     auto input = [env, ctxt](size_t argc, napi_value* argv) {
300         // required 2 arguments :: <event> <callback>
301         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
302             "Parameter error:Mandatory parameters are left unspecified");
303         std::string event;
304         ctxt->status = JSUtil::GetValue(env, argv[0], event);
305         ZLOGI("subscribe to event:%{public}s", event.c_str());
306         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
307             "Parameter error:parameter event type not equal distributedDataServiceDie");
308 
309         napi_valuetype valueType = napi_undefined;
310         ctxt->status = napi_typeof(env, argv[1], &valueType);
311         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function), Status::INVALID_ARGUMENT,
312             "Parameter error:parameter callback must be function type");
313 
314         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
315         ASSERT_BUSINESS_ERR(ctxt, proxy != nullptr, Status::INVALID_ARGUMENT,
316             "Parameter error:JsKVManager nullptr");
317 
318         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
319         for (auto& it : proxy->deathRecipient_) {
320             if (JSUtil::Equals(env, argv[1], it->GetCallback())) {
321                 ZLOGD("KVManager::On callback already register!");
322                 return;
323             }
324         }
325         auto deathRecipient = std::make_shared<DeathRecipient>(proxy->uvQueue_, argv[1]);
326         proxy->kvDataManager_.RegisterKvStoreServiceDeathRecipient(deathRecipient);
327         proxy->deathRecipient_.push_back(deathRecipient);
328         ZLOGD("on mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
329     };
330     ctxt->GetCbInfoSync(env, info, input);
331     if (ctxt->status != napi_ok) {
332         ThrowNapiError(env, Status::INVALID_ARGUMENT, "");
333     }
334     return nullptr;
335 }
336 
Off(napi_env env,napi_callback_info info)337 napi_value JsKVManager::Off(napi_env env, napi_callback_info info)
338 {
339     auto ctxt = std::make_shared<ContextBase>();
340     auto input = [env, ctxt](size_t argc, napi_value* argv) {
341         // required 1 or 2 arguments :: <event> [callback]
342         ASSERT_BUSINESS_ERR(ctxt, argc > 0, Status::INVALID_ARGUMENT,
343             "Parameter error:Mandatory parameters are left unspecified");
344         std::string event;
345         ctxt->status = JSUtil::GetValue(env, argv[0], event);
346         // required 1 arguments :: <event>
347         ZLOGI("unsubscribe to event:%{public}s %{public}s specified", event.c_str(), (argc == 1) ? "without" : "with");
348         ASSERT_BUSINESS_ERR(ctxt, event == "distributedDataServiceDie", Status::INVALID_ARGUMENT,
349             "Parameter error:parameter event not equal distributedDataServiceDie");
350         // have 2 arguments :: have the [callback]
351         if (argc == 2) {
352             napi_valuetype valueType = napi_undefined;
353             ctxt->status = napi_typeof(env, argv[1], &valueType);
354             ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && (valueType == napi_function),
355                 Status::INVALID_ARGUMENT, "Parameter error:parameter callback type must be function");
356         }
357         JsKVManager* proxy = reinterpret_cast<JsKVManager*>(ctxt->native);
358         std::lock_guard<std::mutex> lck(proxy->deathMutex_);
359         auto it = proxy->deathRecipient_.begin();
360         while (it != proxy->deathRecipient_.end()) {
361             // have 2 arguments :: have the [callback]
362             if ((argc == 1) || JSUtil::Equals(env, argv[1], (*it)->GetCallback())) {
363                 proxy->kvDataManager_.UnRegisterKvStoreServiceDeathRecipient(*it);
364                 (*it)->Clear();
365                 it = proxy->deathRecipient_.erase(it);
366             } else {
367                 ++it;
368             }
369         }
370         ZLOGD("off mapsize: %{public}d", static_cast<int>(proxy->deathRecipient_.size()));
371     };
372     ctxt->GetCbInfoSync(env, info, input);
373     if (ctxt->status != napi_ok) {
374         ThrowNapiError(env, Status::INVALID_ARGUMENT, "Parameter error:params must be sting or function");
375     }
376     ZLOGD("KVManager::Off callback is not register or already unregister!");
377     return nullptr;
378 }
379 
Constructor(napi_env env)380 napi_value JsKVManager::Constructor(napi_env env)
381 {
382     auto lambda = []() -> std::vector<napi_property_descriptor> {
383         std::vector<napi_property_descriptor> properties = {
384             DECLARE_NAPI_FUNCTION("getKVStore", JsKVManager::GetKVStore),
385             DECLARE_NAPI_FUNCTION("closeKVStore", JsKVManager::CloseKVStore),
386             DECLARE_NAPI_FUNCTION("deleteKVStore", JsKVManager::DeleteKVStore),
387             DECLARE_NAPI_FUNCTION("getAllKVStoreId", JsKVManager::GetAllKVStoreId),
388             DECLARE_NAPI_FUNCTION("on", JsKVManager::On),
389             DECLARE_NAPI_FUNCTION("off", JsKVManager::Off),
390         };
391         return properties;
392     };
393     return JSUtil::DefineClass(env, "ohos.data.distributedKVStore", "JsKVManager", lambda, JsKVManager::New);
394 }
395 
New(napi_env env,napi_callback_info info)396 napi_value JsKVManager::New(napi_env env, napi_callback_info info)
397 {
398     std::string bundleName;
399     ContextParam param;
400     auto ctxt = std::make_shared<ContextBase>();
401     auto input = [env, ctxt, &bundleName, &param](size_t argc, napi_value* argv) {
402         // required 1 arguments :: <bundleName>
403         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
404             "Parameter error:Mandatory parameters are left unspecified");
405         ctxt->status = JSUtil::GetNamedProperty(env, argv[0], "bundleName", bundleName);
406         ASSERT_BUSINESS_ERR(ctxt, ctxt->status != napi_generic_failure, Status::INVALID_ARGUMENT,
407             "Parameter error:The bundleName parameter is missing.");
408         ASSERT_BUSINESS_ERR(ctxt, !bundleName.empty(), Status::INVALID_ARGUMENT,
409             "Parameter error:The bundleName field cannot be empty.");
410 
411         napi_value jsContext = nullptr;
412         JSUtil::GetNamedProperty(env, argv[0], "context", jsContext);
413         ctxt->status = JSUtil::GetValue(env, jsContext, param);
414         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT,
415             "Parameter error:get context failed");
416     };
417     ctxt->GetCbInfoSync(env, info, input);
418     ASSERT_NULL(!ctxt->isThrowError, "JsKVManager New exit");
419 
420     JsKVManager* kvManager = new (std::nothrow) JsKVManager(bundleName, env, param);
421     ASSERT_ERR(env, kvManager != nullptr, Status::INVALID_ARGUMENT, "Parameter error:kvManager is nullptr");
422 
423     auto finalize = [](napi_env env, void* data, void* hint) {
424         ZLOGD("kvManager finalize.");
425         auto* kvManager = reinterpret_cast<JsKVManager*>(data);
426         ASSERT_VOID(kvManager != nullptr, "kvManager is null!");
427         delete kvManager;
428     };
429     ASSERT_CALL(env, napi_wrap(env, ctxt->self, kvManager, finalize, nullptr, nullptr), kvManager);
430     return ctxt->self;
431 }
432 
OnRemoteDied()433 void JsKVManager::DeathRecipient::OnRemoteDied()
434 {
435     AsyncCall();
436 }
437 } // namespace OHOS::DistributedKVStore
438