1 /*
2 * Copyright (c) 2023-2024 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 #include "kv_adapter.h"
16
17 #include <cinttypes>
18 #include <mutex>
19
20 #include "datetime_ex.h"
21 #include "string_ex.h"
22
23 #include "distributed_device_profile_errors.h"
24 #include "distributed_device_profile_log.h"
25 #include "distributed_device_profile_constants.h"
26 #include "profile_cache.h"
27 #include "profile_utils.h"
28
29 namespace OHOS {
30 namespace DistributedDeviceProfile {
31 using namespace OHOS::DistributedKv;
32 namespace {
33 constexpr int32_t MAX_INIT_RETRY_TIMES = 30;
34 constexpr int32_t INIT_RETRY_SLEEP_INTERVAL = 200 * 1000; // 500ms
35 const std::string DATABASE_DIR = "/data/service/el1/public/database/distributed_device_profile_service";
36 const std::string TAG = "KVAdapter";
37 constexpr uint32_t MAX_BATCH_SIZE = 128;
38 }
39
KVAdapter(const std::string & appId,const std::string & storeId,const std::shared_ptr<DistributedKv::KvStoreObserver> & dataChangeListener,const std::shared_ptr<DistributedKv::KvStoreSyncCallback> & syncCompletedListener,const std::shared_ptr<DistributedKv::KvStoreDeathRecipient> & deathListener,DistributedKv::DataType dataType)40 KVAdapter::KVAdapter(const std::string &appId, const std::string &storeId,
41 const std::shared_ptr<DistributedKv::KvStoreObserver> &dataChangeListener,
42 const std::shared_ptr<DistributedKv::KvStoreSyncCallback> &syncCompletedListener,
43 const std::shared_ptr<DistributedKv::KvStoreDeathRecipient> &deathListener,
44 DistributedKv::DataType dataType)
45 {
46 this->appId_.appId = appId;
47 this->storeId_.storeId = storeId;
48 this->dataChangeListener_ = dataChangeListener;
49 this->syncCompletedListener_= syncCompletedListener;
50 this->deathRecipient_ = deathListener;
51 this->dataType_ = dataType;
52 HILOGD("KVAdapter Constructor Success, appId: %{public}s, storeId: %{public}s", appId.c_str(), storeId.c_str());
53 }
54
~KVAdapter()55 KVAdapter::~KVAdapter()
56 {
57 HILOGD("KVAdapter Destruction!");
58 }
59
Init()60 int32_t KVAdapter::Init()
61 {
62 HILOGI("Init kvAdapter, storeId: %{public}s", storeId_.storeId.c_str());
63 int32_t tryTimes = MAX_INIT_RETRY_TIMES;
64 int64_t beginTime = GetTickCount();
65 while (tryTimes > 0) {
66 DistributedKv::Status status = GetKvStorePtr(dataType_);
67 if (status == DistributedKv::Status::SUCCESS) {
68 int64_t endTime = GetTickCount();
69 HILOGI("Init KvStorePtr Success, spend %{public}" PRId64 " ms", endTime - beginTime);
70 RegisterSyncCompletedListener();
71 RegisterDataChangeListener();
72 RegisterDeathListener();
73 return DP_SUCCESS;
74 }
75 HILOGI("CheckKvStore, left times: %{public}d, status: %{public}d", tryTimes, status);
76 if (status == DistributedKv::Status::STORE_META_CHANGED) {
77 HILOGW("This db meta changed, remove and rebuild it");
78 DeleteKvStore();
79 }
80 if (status == DistributedKv::Status::SECURITY_LEVEL_ERROR) {
81 DeleteKvStore();
82 }
83 usleep(INIT_RETRY_SLEEP_INTERVAL);
84 tryTimes--;
85 }
86 return DP_KV_DB_INIT_FAIL;
87 }
88
UnInit()89 int32_t KVAdapter::UnInit()
90 {
91 HILOGI("DBAdapter UnInit");
92 UnRegisterSyncCompletedListener();
93 UnRegisterDataChangeListener();
94 UnRegisterDeathListener();
95 DeleteSyncCompletedListener();
96 DeleteDataChangeListener();
97 DeleteDeathListener();
98 DeleteKvStorePtr();
99 return DP_SUCCESS;
100 }
101
Put(const std::string & key,const std::string & value)102 int32_t KVAdapter::Put(const std::string& key, const std::string& value)
103 {
104 if (key.empty() || key.size() > MAX_STRING_LEN || value.empty() || value.size() > MAX_STRING_LEN) {
105 HILOGE("Param is invalid!");
106 return DP_INVALID_PARAMS;
107 }
108 DistributedKv::Status status;
109 {
110 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
111 if (kvStorePtr_ == nullptr) {
112 HILOGE("kvDBPtr is null!");
113 return DP_KV_DB_PTR_NULL;
114 }
115
116 DistributedKv::Key kvKey(key);
117 DistributedKv::Value oldV;
118 if (kvStorePtr_->Get(kvKey, oldV) == DistributedKv::Status::SUCCESS && oldV.ToString() == value) {
119 HILOGI("The key-value pair already exists. key=%{public}s,value=%{public}s",
120 ProfileUtils::GetDbKeyAnonyString(key).c_str(),
121 ProfileUtils::GetAnonyString(value).c_str());
122 return DP_SUCCESS;
123 }
124
125 DistributedKv::Value kvValue(value);
126 status = kvStorePtr_->Put(kvKey, kvValue);
127 }
128 if (status != DistributedKv::Status::SUCCESS) {
129 HILOGE("Put kv to db failed, ret: %{public}d", status);
130 return DP_PUT_KV_DB_FAIL;
131 }
132 return DP_SUCCESS;
133 }
134
PutBatch(const std::map<std::string,std::string> & values)135 int32_t KVAdapter::PutBatch(const std::map<std::string, std::string>& values)
136 {
137 if (values.empty() || values.size() > MAX_PROFILE_SIZE) {
138 HILOGE("Param is invalid!");
139 return DP_INVALID_PARAMS;
140 }
141 DistributedKv::Status status;
142 {
143 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
144 if (kvStorePtr_ == nullptr) {
145 HILOGE("kvDBPtr is null!");
146 return DP_KV_DB_PTR_NULL;
147 }
148 std::vector<DistributedKv::Entry> entries;
149 DistributedKv::Value oldV;
150 DistributedKv::Key kvKey;
151 for (auto item : values) {
152 kvKey = item.first;
153 if (kvStorePtr_->Get(kvKey, oldV) == DistributedKv::Status::SUCCESS && oldV.ToString() == item.second) {
154 HILOGI("The key-value pair already exists. key=%{public}s,value=%{public}s",
155 ProfileUtils::GetDbKeyAnonyString(item.first).c_str(),
156 ProfileUtils::GetAnonyString(item.second).c_str());
157 continue;
158 }
159
160 Entry entry;
161 entry.key = kvKey;
162 entry.value = item.second;
163 entries.emplace_back(entry);
164 }
165 if (entries.empty()) {
166 HILOGD("All key-value pair already exists.");
167 return DP_SUCCESS;
168 }
169 status = kvStorePtr_->PutBatch(entries);
170 }
171 if (status != DistributedKv::Status::SUCCESS) {
172 HILOGE("PutBatch kv to db failed, ret: %{public}d", status);
173 return DP_PUT_KV_DB_FAIL;
174 }
175 return DP_SUCCESS;
176 }
177
Delete(const std::string & key)178 int32_t KVAdapter::Delete(const std::string& key)
179 {
180 HILOGI("key: %{public}s", ProfileUtils::GetDbKeyAnonyString(key).c_str());
181 DistributedKv::Status status;
182 {
183 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
184 if (kvStorePtr_ == nullptr) {
185 HILOGE("kvDBPtr is null!");
186 return DP_KV_DB_PTR_NULL;
187 }
188 DistributedKv::Key kvKey(key);
189 status = kvStorePtr_->Delete(kvKey);
190 }
191 if (status != DistributedKv::Status::SUCCESS) {
192 HILOGE("Delete kv by key failed!");
193 return DP_DEL_KV_DB_FAIL;
194 }
195 HILOGD("Delete kv by key success!");
196 return DP_SUCCESS;
197 }
198
Get(const std::string & key,std::string & value)199 int32_t KVAdapter::Get(const std::string& key, std::string& value)
200 {
201 HILOGI("key: %{public}s", ProfileUtils::GetDbKeyAnonyString(key).c_str());
202 DistributedKv::Key kvKey(key);
203 DistributedKv::Value kvValue;
204 DistributedKv::Status status;
205 {
206 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
207 if (kvStorePtr_ == nullptr) {
208 HILOGE("kvStoragePtr_ is null");
209 return DP_KV_DB_PTR_NULL;
210 }
211 status = kvStorePtr_->Get(kvKey, kvValue);
212 }
213 if (status != DistributedKv::Status::SUCCESS) {
214 HILOGE("Get data from kv failed, key: %{public}s", ProfileUtils::GetDbKeyAnonyString(key).c_str());
215 return DP_GET_KV_DB_FAIL;
216 }
217 value = kvValue.ToString();
218 return DP_SUCCESS;
219 }
220
GetByPrefix(const std::string & keyPrefix,std::map<std::string,std::string> & values)221 int32_t KVAdapter::GetByPrefix(const std::string& keyPrefix, std::map<std::string, std::string>& values)
222 {
223 HILOGI("key prefix: %{public}s", ProfileUtils::GetDbKeyAnonyString(keyPrefix).c_str());
224 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
225 if (kvStorePtr_ == nullptr) {
226 HILOGE("kvStoragePtr_ is null");
227 return DP_KV_DB_PTR_NULL;
228 }
229 // if prefix is empty, get all entries.
230 DistributedKv::Key allEntryKeyPrefix(keyPrefix);
231 std::vector<DistributedKv::Entry> allEntries;
232 DistributedKv::Status status = kvStorePtr_->GetEntries(allEntryKeyPrefix, allEntries);
233 if (status != DistributedKv::Status::SUCCESS) {
234 HILOGE("Query data by keyPrefix failed, prefix: %{public}s",
235 ProfileUtils::GetDbKeyAnonyString(keyPrefix).c_str());
236 return DP_GET_KV_DB_FAIL;
237 }
238 if (allEntries.size() == 0 || allEntries.size() > MAX_DB_SIZE) {
239 HILOGE("AllEntries size is invalid!size: %{public}zu! prefix: %{public}s",
240 allEntries.size(), ProfileUtils::GetDbKeyAnonyString(keyPrefix).c_str());
241 return DP_INVALID_PARAMS;
242 }
243 for (const auto& item : allEntries) {
244 values[item.key.ToString()] = item.value.ToString();
245 }
246 return DP_SUCCESS;
247 }
248
DeleteByPrefix(const std::string & keyPrefix)249 int32_t KVAdapter::DeleteByPrefix(const std::string& keyPrefix)
250 {
251 HILOGI("delete by key prefix: %{public}s", ProfileUtils::GetDbKeyAnonyString(keyPrefix).c_str());
252 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
253 if (kvStorePtr_ == nullptr) {
254 HILOGE("kvStoragePtr_ is null");
255 return DP_KV_DB_PTR_NULL;
256 }
257 // if prefix is empty, get all entries.
258 DistributedKv::Key allEntryKeyPrefix(keyPrefix);
259 std::vector<DistributedKv::Entry> allEntries;
260 DistributedKv::Status status = kvStorePtr_->GetEntries(allEntryKeyPrefix, allEntries);
261 if (status != DistributedKv::Status::SUCCESS) {
262 return DP_DEL_KV_DB_FAIL;
263 }
264 std::vector<DistributedKv::Key> keys;
265 for (auto item : allEntries) {
266 keys.push_back(item.key);
267 }
268 status = kvStorePtr_->DeleteBatch(keys);
269 if (status != DistributedKv::Status::SUCCESS) {
270 return DP_DEL_KV_DB_FAIL;
271 }
272 return DP_SUCCESS;
273 }
274
GetKvStorePtr(DistributedKv::DataType dataType)275 DistributedKv::Status KVAdapter::GetKvStorePtr(DistributedKv::DataType dataType)
276 {
277 HILOGI("called");
278 DistributedKv::Options options = {
279 .createIfMissing = true,
280 .encrypt = false,
281 .isPublic = true,
282 .securityLevel = DistributedKv::SecurityLevel::S1,
283 .area = 1,
284 .kvStoreType = KvStoreType::SINGLE_VERSION,
285 .baseDir = DATABASE_DIR,
286 .dataType = dataType,
287 .cloudConfig = {
288 .enableCloud = true,
289 .autoSync = true,
290 }
291 };
292 DistributedKv::Status status;
293 {
294 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
295 status = kvDataMgr_.GetSingleKvStore(options, appId_, storeId_, kvStorePtr_);
296 if (status == DistributedKv::Status::SUCCESS && kvStorePtr_ == nullptr) {
297 status = DistributedKv::Status::ERROR;
298 }
299 }
300 return status;
301 }
302
DeleteKvStorePtr()303 int32_t KVAdapter::DeleteKvStorePtr()
304 {
305 HILOGI("Delete KvStore Ptr!");
306 {
307 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
308 kvStorePtr_ = nullptr;
309 }
310 return DP_SUCCESS;
311 }
312
Sync(const std::vector<std::string> & deviceList,SyncMode syncMode)313 int32_t KVAdapter::Sync(const std::vector<std::string>& deviceList, SyncMode syncMode)
314 {
315 HILOGD("Sync!");
316 {
317 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
318 if (kvStorePtr_ == nullptr) {
319 HILOGE("kvStorePtr is nullptr!");
320 return DP_KV_DB_PTR_NULL;
321 }
322 if (deviceList.empty() || deviceList.size() > MAX_DEVICE_SIZE) {
323 HILOGE("deviceList is invalid!");
324 return DP_INVALID_PARAMS;
325 }
326 if (syncMode <= SyncMode::MIN || syncMode >= SyncMode::MAX) {
327 HILOGE("syncMode is invalid!");
328 return DP_INVALID_PARAMS;
329 }
330 DistributedKv::Status status = kvStorePtr_->Sync(deviceList, static_cast<DistributedKv::SyncMode>(syncMode));
331 if (status != DistributedKv::Status::SUCCESS) {
332 HILOGE("Sync fail!");
333 return DP_KV_SYNC_FAIL;
334 }
335 }
336 return DP_SUCCESS;
337 }
338
GetDeviceEntries(const std::string & udid,std::map<std::string,std::string> & values)339 int32_t KVAdapter::GetDeviceEntries(const std::string& udid, std::map<std::string, std::string>& values)
340 {
341 if (udid.empty()) {
342 HILOGE("udid is invalid!");
343 return DP_INVALID_PARAMS;
344 }
345 std::vector<DistributedKv::Entry> entries;
346 {
347 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
348 if (kvStorePtr_ == nullptr) {
349 HILOGE("kvStorePtr is nullptr!");
350 return DP_KV_DB_PTR_NULL;
351 }
352 DistributedKv::Status status = kvStorePtr_->GetDeviceEntries(udid, entries);
353 if (status != DistributedKv::Status::SUCCESS) {
354 HILOGE("GetDeviceEntries fail! udid=%{public}s", ProfileUtils::GetAnonyString(udid).c_str());
355 return DP_GET_KV_DB_FAIL;
356 }
357 }
358 for (const auto& item : entries) {
359 auto key = item.key.ToString();
360 if (key.empty()) {
361 continue;
362 }
363 values[key] = item.value.ToString();
364 }
365 return DP_SUCCESS;
366 }
367
DeleteBatch(const std::vector<std::string> & keys)368 int32_t KVAdapter::DeleteBatch(const std::vector<std::string>& keys)
369 {
370 if (keys.empty() || keys.size() > MAX_PROFILE_SIZE) {
371 HILOGE("keys size(%{public}zu) is invalid!", keys.size());
372 return DP_INVALID_PARAMS;
373 }
374
375 uint32_t keysSize = static_cast<uint32_t>(keys.size());
376 std::vector<std::vector<DistributedKv::Key>> delKeyBatches;
377 for (uint32_t i = 0; i < keysSize; i += MAX_BATCH_SIZE) {
378 uint32_t end = (i + MAX_BATCH_SIZE) > keysSize ? keysSize : (i + MAX_BATCH_SIZE);
379 auto batch = std::vector<std::string>(keys.begin() + i, keys.begin() + end);
380 std::vector<DistributedKv::Key> delKeys;
381 for (auto item : batch) {
382 DistributedKv::Key key(item);
383 delKeys.emplace_back(key);
384 }
385 delKeyBatches.emplace_back(delKeys);
386 }
387
388 {
389 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
390 if (kvStorePtr_ == nullptr) {
391 HILOGE("kvStorePtr is nullptr!");
392 return DP_KV_DB_PTR_NULL;
393 }
394 for (auto delKeys : delKeyBatches) {
395 DistributedKv::Status status = kvStorePtr_->DeleteBatch(delKeys);
396 if (status != DistributedKv::Status::SUCCESS) {
397 HILOGE("DeleteBatch failed!");
398 return DP_DEL_KV_DB_FAIL;
399 }
400 }
401 }
402 return DP_SUCCESS;
403 }
404
RegisterDataChangeListener()405 int32_t KVAdapter::RegisterDataChangeListener()
406 {
407 HILOGI("Register db data change listener");
408 {
409 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
410 if (kvStorePtr_ == nullptr) {
411 HILOGE("kvStoragePtr_ is null");
412 return DP_INVALID_PARAMS;
413 }
414 DistributedKv::Status status =
415 kvStorePtr_->SubscribeKvStore(DistributedKv::SubscribeType::SUBSCRIBE_TYPE_ALL, dataChangeListener_);
416 if (status != DistributedKv::Status::SUCCESS) {
417 HILOGE("Register db data change listener failed, ret: %{public}d", status);
418 return DP_REGISTER_KV_DATA_LISTENER_FAILED;
419 }
420 }
421 return DP_SUCCESS;
422 }
423
UnRegisterDataChangeListener()424 int32_t KVAdapter::UnRegisterDataChangeListener()
425 {
426 HILOGI("UnRegister db data change listener");
427 {
428 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
429 if (kvStorePtr_ == nullptr) {
430 HILOGE("kvStoragePtr_ is null");
431 return DP_KV_DB_PTR_NULL;
432 }
433 DistributedKv::Status status =
434 kvStorePtr_->UnSubscribeKvStore(DistributedKv::SubscribeType::SUBSCRIBE_TYPE_ALL, dataChangeListener_);
435 if (status != DistributedKv::Status::SUCCESS) {
436 HILOGE("UnRegister db data change listener failed, ret: %{public}d", status);
437 return DP_UNREGISTER_KV_DATA_LISTENER_FAILED;
438 }
439 }
440 return DP_SUCCESS;
441 }
442
DeleteDataChangeListener()443 int32_t KVAdapter::DeleteDataChangeListener()
444 {
445 HILOGI("Delete DataChangeListener!");
446 {
447 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
448 dataChangeListener_ = nullptr;
449 }
450 return DP_SUCCESS;
451 }
452
RegisterSyncCompletedListener()453 int32_t KVAdapter::RegisterSyncCompletedListener()
454 {
455 HILOGI("Register syncCompleted listener");
456 {
457 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
458 if (kvStorePtr_ == nullptr) {
459 HILOGE("kvStoragePtr_ is null");
460 return DP_KV_DB_PTR_NULL;
461 }
462 DistributedKv::Status status = kvStorePtr_->RegisterSyncCallback(syncCompletedListener_);
463 if (status != DistributedKv::Status::SUCCESS) {
464 HILOGE("Register syncCompleted listener failed, ret: %{public}d", status);
465 return DP_REGISTER_KV_SYNC_LISTENER_FAILED;
466 }
467 }
468 return DP_SUCCESS;
469 }
470
UnRegisterSyncCompletedListener()471 int32_t KVAdapter::UnRegisterSyncCompletedListener()
472 {
473 HILOGI("UnRegister syncCompleted listener");
474 {
475 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
476 if (kvStorePtr_ == nullptr) {
477 HILOGE("kvStoragePtr_ is null");
478 return DP_KV_DB_PTR_NULL;
479 }
480 DistributedKv::Status status = kvStorePtr_->UnRegisterSyncCallback();
481 if (status != DistributedKv::Status::SUCCESS) {
482 HILOGE("UnRegister db data change listener failed, ret: %{public}d", status);
483 return DP_UNREGISTER_KV_SYNC_LISTENER_FAILED;
484 }
485 }
486 return DP_SUCCESS;
487 }
488
DeleteSyncCompletedListener()489 int32_t KVAdapter::DeleteSyncCompletedListener()
490 {
491 HILOGI("Delete SyncCompletedListener!");
492 {
493 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
494 syncCompletedListener_ = nullptr;
495 }
496 return DP_SUCCESS;
497 }
498
RegisterDeathListener()499 int32_t KVAdapter::RegisterDeathListener()
500 {
501 HILOGI("Register syncCompleted listener");
502 {
503 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
504 kvDataMgr_.RegisterKvStoreServiceDeathRecipient(deathRecipient_);
505 }
506 return DP_SUCCESS;
507 }
508
UnRegisterDeathListener()509 int32_t KVAdapter::UnRegisterDeathListener()
510 {
511 HILOGI("UnRegister death listener");
512 {
513 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
514 kvDataMgr_.UnRegisterKvStoreServiceDeathRecipient(deathRecipient_);
515 }
516 return DP_SUCCESS;
517 }
518
DeleteDeathListener()519 int32_t KVAdapter::DeleteDeathListener()
520 {
521 HILOGI("Delete DeathListener!");
522 {
523 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
524 deathRecipient_ = nullptr;
525 }
526 return DP_SUCCESS;
527 }
528
DeleteKvStore()529 int32_t KVAdapter::DeleteKvStore()
530 {
531 HILOGI("Delete KvStore!");
532 {
533 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
534 kvDataMgr_.CloseKvStore(appId_, storeId_);
535 kvDataMgr_.DeleteKvStore(appId_, storeId_, DATABASE_DIR);
536 }
537 return DP_SUCCESS;
538 }
539
RemoveDeviceData(const std::string & uuid)540 int32_t KVAdapter::RemoveDeviceData(const std::string& uuid)
541 {
542 if (uuid.empty()) {
543 HILOGE("uuid is invalid!");
544 return DP_INVALID_PARAMS;
545 }
546 {
547 std::lock_guard<std::mutex> lock(kvAdapterMutex_);
548 if (kvStorePtr_ == nullptr) {
549 HILOGE("kvStorePtr is nullptr!");
550 return DP_KV_DB_PTR_NULL;
551 }
552 DistributedKv::Status status = kvStorePtr_->RemoveDeviceData(uuid);
553 if (status != DistributedKv::Status::SUCCESS) {
554 HILOGE("GetDeviceEntries fail! uuid=%{public}s", ProfileUtils::GetAnonyString(uuid).c_str());
555 return DP_GET_KV_DB_FAIL;
556 }
557 }
558 return DP_SUCCESS;
559 }
560 } // namespace DeviceProfile
561 } // namespace OHOS
562