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