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
16 #include "request_manager.h"
17
18 #include "privacy_kit.h"
19
20 #include "common_utils.h"
21 #include "constant_definition.h"
22 #include "gnss_ability_proxy.h"
23 #include "fusion_controller.h"
24 #include "location_log.h"
25 #include "locator_ability.h"
26 #include "locator_background_proxy.h"
27 #include "locator_event_manager.h"
28 #include "network_ability_proxy.h"
29 #include "passive_ability_proxy.h"
30 #include "request_config.h"
31
32 namespace OHOS {
33 namespace Location {
34 std::mutex RequestManager::requestMutex_;
RequestManager()35 RequestManager::RequestManager()
36 {
37 auto locatorDftManager = DelayedSingleton<LocatorDftManager>::GetInstance();
38 if (locatorDftManager != nullptr) {
39 locatorDftManager->Init();
40 }
41 }
42
~RequestManager()43 RequestManager::~RequestManager()
44 {
45 }
46
InitSystemListeners()47 bool RequestManager::InitSystemListeners()
48 {
49 LBSLOGI(REQUEST_MANAGER, "Init system listeners.");
50 return true;
51 }
52
UpdateUsingPermission(std::shared_ptr<Request> request)53 void RequestManager::UpdateUsingPermission(std::shared_ptr<Request> request)
54 {
55 if (request == nullptr) {
56 LBSLOGE(REQUEST_MANAGER, "request is null");
57 return;
58 }
59 LBSLOGI(REQUEST_MANAGER, "UpdateUsingPermission : tokenId = %{public}d, firstTokenId = %{public}d",
60 request->GetTokenId(), request->GetFirstTokenId());
61 UpdateUsingLocationPermission(request);
62 UpdateUsingApproximatelyPermission(request);
63 UpdateUsingBackgroundPermission(request);
64 }
65
UpdateUsingLocationPermission(std::shared_ptr<Request> request)66 void RequestManager::UpdateUsingLocationPermission(std::shared_ptr<Request> request)
67 {
68 uint32_t callingTokenId = request->GetTokenId();
69 uint32_t callingFirstTokenid = request->GetFirstTokenId();
70 int32_t uid = request->GetUid();
71 if (IsUidInProcessing(uid) &&
72 CommonUtils::CheckLocationPermission(callingTokenId, callingFirstTokenid)) {
73 if (!request->GetLocationPermState()) {
74 PrivacyKit::StartUsingPermission(callingTokenId, ACCESS_LOCATION);
75 request->SetLocationPermState(true);
76 }
77 } else {
78 if (request->GetLocationPermState()) {
79 PrivacyKit::StopUsingPermission(callingTokenId, ACCESS_LOCATION);
80 request->SetLocationPermState(false);
81 }
82 }
83 }
84
UpdateUsingApproximatelyPermission(std::shared_ptr<Request> request)85 void RequestManager::UpdateUsingApproximatelyPermission(std::shared_ptr<Request> request)
86 {
87 uint32_t callingTokenId = request->GetTokenId();
88 uint32_t callingFirstTokenid = request->GetFirstTokenId();
89 int32_t uid = request->GetUid();
90 if (IsUidInProcessing(uid) &&
91 CommonUtils::CheckApproximatelyPermission(callingTokenId, callingFirstTokenid)) {
92 if (!request->GetApproximatelyPermState()) {
93 PrivacyKit::StartUsingPermission(callingTokenId, ACCESS_APPROXIMATELY_LOCATION);
94 request->SetApproximatelyPermState(true);
95 }
96 } else {
97 if (request->GetApproximatelyPermState()) {
98 PrivacyKit::StopUsingPermission(callingTokenId, ACCESS_APPROXIMATELY_LOCATION);
99 request->SetApproximatelyPermState(false);
100 }
101 }
102 }
103
UpdateUsingBackgroundPermission(std::shared_ptr<Request> request)104 void RequestManager::UpdateUsingBackgroundPermission(std::shared_ptr<Request> request)
105 {
106 uint32_t callingTokenId = request->GetTokenId();
107 uint32_t callingFirstTokenid = request->GetFirstTokenId();
108 int32_t uid = request->GetUid();
109 std::string bundleName;
110 if (!CommonUtils::GetBundleNameByUid(uid, bundleName)) {
111 LBSLOGE(REQUEST_MANAGER, "Fail to Get bundle name: uid = %{public}d.", uid);
112 return;
113 }
114 if (DelayedSingleton<LocatorBackgroundProxy>::GetInstance().get()->IsAppBackground(bundleName) &&
115 IsUidInProcessing(uid) && CommonUtils::CheckBackgroundPermission(callingTokenId, callingFirstTokenid)) {
116 if (!request->GetBackgroundPermState()) {
117 PrivacyKit::StartUsingPermission(callingTokenId, ACCESS_BACKGROUND_LOCATION);
118 request->SetBackgroundPermState(true);
119 }
120 } else {
121 if (request->GetBackgroundPermState()) {
122 PrivacyKit::StopUsingPermission(callingTokenId, ACCESS_BACKGROUND_LOCATION);
123 request->SetBackgroundPermState(false);
124 }
125 }
126 }
127
HandleStartLocating(std::shared_ptr<Request> request)128 void RequestManager::HandleStartLocating(std::shared_ptr<Request> request)
129 {
130 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
131 auto locatorDftManager = DelayedSingleton<LocatorDftManager>::GetInstance();
132 if (locatorAbility == nullptr || locatorDftManager == nullptr) {
133 return;
134 }
135 // restore request to all request list
136 bool isNewRequest = RestorRequest(request);
137 // update request map
138 if (isNewRequest) {
139 locatorAbility->RegisterPermissionCallback(request->GetTokenId(),
140 {ACCESS_APPROXIMATELY_LOCATION, ACCESS_LOCATION, ACCESS_BACKGROUND_LOCATION});
141 UpdateRequestRecord(request, true);
142 UpdateUsingPermission(request);
143 locatorDftManager->LocationSessionStart(request);
144 }
145 // process location request
146 HandleRequest();
147 }
148
RestorRequest(std::shared_ptr<Request> newRequest)149 bool RequestManager::RestorRequest(std::shared_ptr<Request> newRequest)
150 {
151 std::lock_guard lock(requestMutex_);
152
153 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
154 if (locatorAbility == nullptr) {
155 return false;
156 }
157 auto receivers = locatorAbility->GetReceivers();
158 if (receivers == nullptr) {
159 LBSLOGE(REQUEST_MANAGER, "receivers is empty");
160 return false;
161 }
162 if (newRequest == nullptr) {
163 LBSLOGE(REQUEST_MANAGER, "newRequest is empty");
164 return false;
165 }
166 newRequest->SetRequesting(true);
167 sptr<IRemoteObject> newCallback = newRequest->GetLocatorCallBack()->AsObject();
168
169 LBSLOGI(REQUEST_MANAGER, "add request:%{public}s", newRequest->ToString().c_str());
170 // if callback and request config type is same, take new request configuration over the old one in request list
171 // otherwise, add restore the new request in the list.
172 auto iterator = receivers->find(newCallback);
173 if (iterator == receivers->end()) {
174 std::list<std::shared_ptr<Request>> requestList;
175 requestList.push_back(newRequest);
176 receivers->insert(make_pair(newCallback, requestList));
177 LBSLOGD(REQUEST_MANAGER, "add new receiver with new callback");
178 return true;
179 }
180
181 sptr<RequestConfig> newConfig = newRequest->GetRequestConfig();
182 std::list<std::shared_ptr<Request>> requestWithSameCallback = iterator->second;
183 for (auto iter = requestWithSameCallback.begin(); iter != requestWithSameCallback.end(); ++iter) {
184 auto request = *iter;
185 if (request == nullptr) {
186 continue;
187 }
188 auto requestConfig = request->GetRequestConfig();
189 if (requestConfig == nullptr || newConfig == nullptr) {
190 continue;
191 }
192 if (newConfig->IsSame(*requestConfig)) {
193 request->SetRequestConfig(*newConfig);
194 LBSLOGI(REQUEST_MANAGER, "find same type request, update request configuration");
195 return false;
196 }
197 }
198 requestWithSameCallback.push_back(newRequest);
199 LBSLOGD(REQUEST_MANAGER, "add new receiver with old callback");
200 return true;
201 }
202
UpdateRequestRecord(std::shared_ptr<Request> request,bool shouldInsert)203 void RequestManager::UpdateRequestRecord(std::shared_ptr<Request> request, bool shouldInsert)
204 {
205 std::shared_ptr<std::list<std::string>> proxys = std::make_shared<std::list<std::string>>();
206 request->GetProxyName(proxys);
207 if (proxys->empty()) {
208 LBSLOGE(REQUEST_MANAGER, "can not get proxy name according to request configuration");
209 return;
210 }
211
212 for (std::list<std::string>::iterator iter = proxys->begin(); iter != proxys->end(); ++iter) {
213 std::string abilityName = *iter;
214 UpdateRequestRecord(request, abilityName, shouldInsert);
215 }
216 }
217
UpdateRequestRecord(std::shared_ptr<Request> request,std::string abilityName,bool shouldInsert)218 void RequestManager::UpdateRequestRecord(std::shared_ptr<Request> request, std::string abilityName, bool shouldInsert)
219 {
220 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
221 if (locatorAbility == nullptr) {
222 LBSLOGE(REQUEST_MANAGER, "locatorAbility is null");
223 return;
224 }
225 std::lock_guard lock(requestMutex_);
226 auto requests = locatorAbility->GetRequests();
227 if (requests == nullptr) {
228 LBSLOGE(REQUEST_MANAGER, "requests map is empty");
229 return;
230 }
231 auto mapIter = requests->find(abilityName);
232 if (mapIter == requests->end()) {
233 LBSLOGE(REQUEST_MANAGER, "can not find %{public}s ability request list.", abilityName.c_str());
234 return;
235 }
236
237 auto list = &(mapIter->second);
238 LBSLOGD(REQUEST_MANAGER, "%{public}s ability current request size %{public}s",
239 abilityName.c_str(), std::to_string(list->size()).c_str());
240 if (shouldInsert) {
241 list->push_back(request);
242 runningUids_.push_back(request->GetUid());
243 } else {
244 for (auto iter = list->begin(); iter != list->end();) {
245 auto findRequest = *iter;
246 if (request == findRequest) {
247 iter = list->erase(iter);
248 runningUids_.remove(findRequest->GetUid());
249 LBSLOGD(REQUEST_MANAGER, "find request");
250 } else {
251 ++iter;
252 }
253 }
254 }
255 LBSLOGD(REQUEST_MANAGER, "%{public}s ability request size %{public}s",
256 abilityName.c_str(), std::to_string(list->size()).c_str());
257 }
258
HandleStopLocating(sptr<ILocatorCallback> callback)259 void RequestManager::HandleStopLocating(sptr<ILocatorCallback> callback)
260 {
261 if (callback == nullptr) {
262 LBSLOGE(REQUEST_MANAGER, "stop locating but callback is null");
263 return;
264 }
265 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
266 if (locatorAbility == nullptr) {
267 LBSLOGE(REQUEST_MANAGER, "locatorAbility is null");
268 return;
269 }
270 std::unique_lock<std::mutex> lock(requestMutex_, std::defer_lock);
271 lock.lock();
272 auto receivers = locatorAbility->GetReceivers();
273 if (receivers == nullptr) {
274 LBSLOGE(REQUEST_MANAGER, "receivers map is empty");
275 lock.unlock();
276 return;
277 }
278 sptr<IRemoteObject> deadCallback = callback->AsObject();
279 // get dead request list
280 LBSLOGD(REQUEST_MANAGER, "stop callback");
281 auto iterator = receivers->find(deadCallback);
282 if (iterator == receivers->end()) {
283 LBSLOGD(REQUEST_MANAGER, "this callback has no record in receiver map");
284 lock.unlock();
285 return;
286 }
287
288 auto requests = iterator->second;
289 auto deadRequests = std::make_shared<std::list<std::shared_ptr<Request>>>();
290 for (auto iter = requests.begin(); iter != requests.end(); ++iter) {
291 auto request = *iter;
292 locatorAbility->UnregisterPermissionCallback(request->GetTokenId());
293 deadRequests->push_back(request);
294 LBSLOGI(REQUEST_MANAGER, "remove request:%{public}s", request->ToString().c_str());
295 }
296 LBSLOGD(REQUEST_MANAGER, "get %{public}s dead request", std::to_string(deadRequests->size()).c_str());
297 // update request map
298 if (deadRequests->size() == 0) {
299 lock.unlock();
300 return;
301 }
302 iterator->second.clear();
303 receivers->erase(iterator);
304 lock.unlock();
305 DeleteRequestRecord(deadRequests);
306 deadRequests->clear();
307 // process location request
308 HandleRequest();
309 }
310
DeleteRequestRecord(std::shared_ptr<std::list<std::shared_ptr<Request>>> requests)311 void RequestManager::DeleteRequestRecord(std::shared_ptr<std::list<std::shared_ptr<Request>>> requests)
312 {
313 for (auto iter = requests->begin(); iter != requests->end(); ++iter) {
314 auto request = *iter;
315 UpdateRequestRecord(request, false);
316 UpdateUsingPermission(request);
317 auto locatorBackgroundProxy = DelayedSingleton<LocatorBackgroundProxy>::GetInstance();
318 if (locatorBackgroundProxy == nullptr) {
319 LBSLOGE(REQUEST_MANAGER, "DeleteRequestRecord: LocatorBackgroundProxy is nullptr.");
320 break;
321 }
322 locatorBackgroundProxy.get()->OnDeleteRequestRecord(request);
323 }
324 }
325
HandleRequest()326 void RequestManager::HandleRequest()
327 {
328 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
329 if (locatorAbility == nullptr) {
330 LBSLOGE(REQUEST_MANAGER, "locatorAbility is null");
331 return;
332 }
333 auto proxyMap = locatorAbility->GetProxyMap();
334 if (proxyMap->empty()) {
335 LBSLOGE(REQUEST_MANAGER, "proxy map is empty");
336 return;
337 }
338 std::map<std::string, sptr<IRemoteObject>>::iterator iter;
339 for (iter = proxyMap->begin(); iter != proxyMap->end(); ++iter) {
340 std::string abilityName = iter->first;
341 HandleRequest(abilityName);
342 }
343 }
344
HandleRequest(std::string abilityName)345 void RequestManager::HandleRequest(std::string abilityName)
346 {
347 std::unique_lock<std::mutex> lock(requestMutex_, std::defer_lock);
348 lock.lock();
349 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
350 if (locatorAbility == nullptr) {
351 lock.unlock();
352 return;
353 }
354 auto requests = locatorAbility->GetRequests();
355 if (requests == nullptr) {
356 LBSLOGE(REQUEST_MANAGER, "requests map is empty");
357 lock.unlock();
358 return;
359 }
360 auto mapIter = requests->find(abilityName);
361 if (mapIter == requests->end()) {
362 LBSLOGE(REQUEST_MANAGER, "can not find %{public}s ability request list.", abilityName.c_str());
363 lock.unlock();
364 return;
365 }
366 auto list = mapIter->second;
367 // generate work record, and calculate interval
368 std::shared_ptr<WorkRecord> workRecord = std::make_shared<WorkRecord>();
369 for (auto iter = list.begin(); iter != list.end(); iter++) {
370 auto request = *iter;
371 if (!AddRequestToWorkRecord(request, workRecord)) {
372 continue;
373 }
374 if (!ActiveLocatingStrategies(request)) {
375 continue;
376 }
377 LBSLOGD(REQUEST_MANAGER, "add pid:%{public}d uid:%{public}d %{public}s", request->GetPid(), request->GetUid(),
378 request->GetPackageName().c_str());
379 }
380 LBSLOGD(REQUEST_MANAGER, "detect %{public}s ability requests(size:%{public}s) work record:%{public}s",
381
382 abilityName.c_str(), std::to_string(list.size()).c_str(), workRecord->ToString().c_str());
383 lock.unlock();
384
385 ProxySendLocationRequest(abilityName, *workRecord);
386 }
387
ActiveLocatingStrategies(const std::shared_ptr<Request> & request)388 bool RequestManager::ActiveLocatingStrategies(const std::shared_ptr<Request>& request)
389 {
390 if (request == nullptr) {
391 return false;
392 }
393 auto requestConfig = request->GetRequestConfig();
394 if (requestConfig == nullptr) {
395 return false;
396 }
397 int requestType = requestConfig->GetScenario();
398 if (requestType == SCENE_UNSET) {
399 requestType = requestConfig->GetPriority();
400 }
401 auto fusionController = DelayedSingleton<FusionController>::GetInstance();
402 if (fusionController != nullptr) {
403 fusionController->ActiveFusionStrategies(requestType);
404 }
405 return true;
406 }
407
AddRequestToWorkRecord(std::shared_ptr<Request> & request,std::shared_ptr<WorkRecord> & workRecord)408 bool RequestManager::AddRequestToWorkRecord(std::shared_ptr<Request>& request,
409 std::shared_ptr<WorkRecord>& workRecord)
410 {
411 if (request == nullptr) {
412 return false;
413 }
414 UpdateUsingPermission(request);
415 if (!request->GetIsRequesting()) {
416 return false;
417 }
418 uint32_t tokenId = request->GetTokenId();
419 uint32_t firstTokenId = request->GetFirstTokenId();
420 // if location access permission granted, add request info to work record
421 if (!CommonUtils::CheckLocationPermission(tokenId, firstTokenId) &&
422 !CommonUtils::CheckApproximatelyPermission(tokenId, firstTokenId)) {
423 LBSLOGI(LOCATOR, "CheckLocationPermission return false, tokenId=%{public}d", tokenId);
424 return false;
425 }
426 auto requestConfig = request->GetRequestConfig();
427 if (requestConfig == nullptr) {
428 return false;
429 }
430 // add request info to work record
431 if (workRecord != nullptr) {
432 workRecord->Add(request->GetUid(), request->GetPid(), request->GetPackageName(),
433 requestConfig->GetTimeInterval(), request->GetUuid());
434 }
435 return true;
436 }
437
ProxySendLocationRequest(std::string abilityName,WorkRecord & workRecord)438 void RequestManager::ProxySendLocationRequest(std::string abilityName, WorkRecord& workRecord)
439 {
440 sptr<IRemoteObject> remoteObject = GetRemoteObject(abilityName);
441 if (remoteObject == nullptr) {
442 return;
443 }
444 workRecord.SetDeviceId(CommonUtils::InitDeviceId());
445 if (abilityName == GNSS_ABILITY) {
446 std::unique_ptr<GnssAbilityProxy> gnssProxy = std::make_unique<GnssAbilityProxy>(remoteObject);
447 gnssProxy->SendLocationRequest(workRecord);
448 } else if (abilityName == NETWORK_ABILITY) {
449 std::unique_ptr<NetworkAbilityProxy> networkProxy = std::make_unique<NetworkAbilityProxy>(remoteObject);
450 networkProxy->SendLocationRequest(workRecord);
451 } else if (abilityName == PASSIVE_ABILITY) {
452 std::unique_ptr<PassiveAbilityProxy> passiveProxy = std::make_unique<PassiveAbilityProxy>(remoteObject);
453 passiveProxy->SendLocationRequest(workRecord);
454 }
455 auto fusionController = DelayedSingleton<FusionController>::GetInstance();
456 if (fusionController != nullptr) {
457 fusionController->Process(abilityName);
458 }
459 }
460
GetRemoteObject(std::string abilityName)461 sptr<IRemoteObject> RequestManager::GetRemoteObject(std::string abilityName)
462 {
463 sptr<IRemoteObject> remoteObject = nullptr;
464 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
465 if (locatorAbility == nullptr) {
466 LBSLOGE(REQUEST_MANAGER, "locatorAbility is null");
467 return remoteObject;
468 }
469 auto remoteManagerMap = locatorAbility->GetProxyMap();
470 if (remoteManagerMap == nullptr) {
471 LBSLOGE(REQUEST_MANAGER, "proxy map is empty");
472 return remoteObject;
473 }
474 auto remoteObjectIter = remoteManagerMap->find(abilityName);
475 if (remoteObjectIter == remoteManagerMap->end()) {
476 LBSLOGE(REQUEST_MANAGER, "sa init fail!");
477 return remoteObject;
478 }
479 remoteObject = remoteObjectIter->second;
480 return remoteObject;
481 }
482
HandlePowerSuspendChanged(int32_t pid,int32_t uid,int32_t state)483 void RequestManager::HandlePowerSuspendChanged(int32_t pid, int32_t uid, int32_t state)
484 {
485 if (!IsUidInProcessing(uid)) {
486 LBSLOGE(REQUEST_MANAGER, "Current uid : %{public}d is not locating.", uid);
487 return;
488 }
489 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
490 if (locatorAbility == nullptr) {
491 LBSLOGE(REQUEST_MANAGER, "locatorAbility is null");
492 return;
493 }
494 auto requests = locatorAbility->GetRequests();
495 if (requests == nullptr || requests->empty()) {
496 LBSLOGE(REQUEST_MANAGER, "requests map is empty");
497 return;
498 }
499 bool isActive = (state == static_cast<int>(AppExecFwk::ApplicationState::APP_STATE_FOREGROUND));
500 for (auto mapIter = requests->begin(); mapIter != requests->end(); mapIter++) {
501 auto list = mapIter->second;
502 for (auto request : list) {
503 std::string uid1 = std::to_string(request->GetUid());
504 std::string uid2 = std::to_string(uid);
505 std::string pid1 = std::to_string(request->GetPid());
506 std::string pid2 = std::to_string(pid);
507 if ((uid1.compare(uid2) != 0) || (pid1.compare(pid2) != 0)) {
508 continue;
509 }
510 request->SetRequesting(isActive);
511 auto locatorBackgroundProxy = DelayedSingleton<LocatorBackgroundProxy>::GetInstance();
512 if (locatorBackgroundProxy != nullptr) {
513 locatorBackgroundProxy.get()->OnSuspend(request, isActive);
514 }
515 }
516 }
517 if (DelayedSingleton<LocatorAbility>::GetInstance() != nullptr) {
518 DelayedSingleton<LocatorAbility>::GetInstance().get()->ApplyRequests();
519 }
520 }
521
HandlePermissionChanged(uint32_t tokenId)522 void RequestManager::HandlePermissionChanged(uint32_t tokenId)
523 {
524 auto locatorAbility = DelayedSingleton<LocatorAbility>::GetInstance();
525 if (locatorAbility == nullptr) {
526 LBSLOGE(REQUEST_MANAGER, "HandlePermissionChanged locatorAbility is null");
527 return;
528 }
529 auto requests = locatorAbility->GetRequests();
530 if (requests == nullptr || requests->empty()) {
531 LBSLOGE(REQUEST_MANAGER, "HandlePermissionChanged requests map is empty");
532 return;
533 }
534 for (auto mapIter = requests->begin(); mapIter != requests->end(); mapIter++) {
535 auto list = mapIter->second;
536 for (auto request : list) {
537 if (request == nullptr || tokenId != request->GetTokenId()) {
538 continue;
539 }
540 auto backgroundProxy = DelayedSingleton<LocatorBackgroundProxy>::GetInstance();
541 if (backgroundProxy != nullptr) {
542 backgroundProxy.get()->UpdateListOnRequestChange(request);
543 }
544 }
545 }
546 }
547
IsUidInProcessing(int32_t uid)548 bool RequestManager::IsUidInProcessing(int32_t uid)
549 {
550 if (runningUids_.size() == 0) {
551 return false;
552 }
553
554 bool isFound = std::any_of(runningUids_.begin(), runningUids_.end(), [uid](int32_t i) {
555 return i == uid;
556 });
557 return isFound;
558 }
559 } // namespace Location
560 } // namespace OHOS
561