1 /*
2  * Copyright (C) 2022 Huawei Technologies Co., Ltd.
3  * Licensed under the Mulan PSL v2.
4  * You can use this software according to the terms and conditions of the Mulan PSL v2.
5  * You may obtain a copy of Mulan PSL v2 at:
6  *     http://license.coscl.org.cn/MulanPSL2
7  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8  * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9  * PURPOSE.
10  * See the Mulan PSL v2 for more details.
11  */
12 
13 #include "cadaemon_stub.h"
14 #include <malloc.h>
15 #include <memory>
16 #include <securec.h>
17 #include "ipc_skeleton.h"
18 #include "ipc_types.h"
19 #include "string_ex.h"
20 #include "tee_client_api.h"
21 #include "tee_client_inner.h"
22 #include "tee_log.h"
23 
24 using namespace std;
25 
26 namespace OHOS {
27 namespace CaDaemon {
28 const std::u16string INTERFACE_TOKEN = u"ohos.tee_client.accessToken";
CaDaemonStub()29 CaDaemonStub::CaDaemonStub()
30 {
31 }
32 
~CaDaemonStub()33 CaDaemonStub::~CaDaemonStub()
34 {
35     memberFuncMap_.clear();
36 }
37 
OnRemoteRequest(uint32_t code,MessageParcel & data,MessageParcel & reply,MessageOption & option)38 int32_t CaDaemonStub::OnRemoteRequest(uint32_t code,
39     MessageParcel& data, MessageParcel &reply, MessageOption &option)
40 {
41     tlogi("CaDaemonStub::OnReceived, code = %{public}u, flags= %{public}d.", code, option.GetFlags());
42     int32_t result;
43     (void)mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_DISABLE);
44     (void)mallopt(M_DELAYED_FREE, M_DELAYED_FREE_DISABLE);
45     switch (code) {
46         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::INIT_CONTEXT):
47             result = InitContextRecvProc(data, reply);
48             break;
49         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::FINAL_CONTEXT):
50             result = FinalContextRecvProc(data, reply);
51             break;
52         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::OPEN_SESSION):
53             result = OpenSessionRecvProc(data, reply);
54             break;
55         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::CLOSE_SESSION):
56             result = CloseSessionRecvProc(data, reply);
57             break;
58         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::INVOKE_COMMND):
59             result = InvokeCommandRecvProc(data, reply);
60             break;
61         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::REGISTER_MEM):
62             result = RegisterMemRecvProc(data, reply);
63             break;
64         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::ALLOC_MEM):
65             result = AllocMemRecvProc(data, reply);
66             break;
67         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::RELEASE_MEM):
68             result = ReleaseMemRecvProc(data, reply);
69             break;
70         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::SET_CALL_BACK):
71             result = SetCallBackRecvProc(data, reply);
72             break;
73         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::SEND_SECFILE):
74             result = SendSecFileRecvProc(data, reply);
75             break;
76         case static_cast<uint32_t>(CadaemonOperationInterfaceCode::GET_TEE_VERSION):
77             result = GetTeeVersionRecvProc(data, reply);
78             break;
79         default:
80             tlogi("CaDaemonStub: default case, need check");
81             (void)mallopt(M_FLUSH_THREAD_CACHE, 0);
82             return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
83     }
84 
85     (void)mallopt(M_FLUSH_THREAD_CACHE, 0);
86     return result;
87 }
88 
GetChar(MessageParcel & data,char tempChar[],const char ** str)89 static bool GetChar(MessageParcel &data, char tempChar[], const char **str)
90 {
91     uint32_t strLen;
92     string tempStr;
93     int32_t ret;
94 
95     ret = data.ReadUint32(strLen);
96     CHECK_ERR_RETURN(ret, true, ret);
97 
98     if (strLen > 0) {
99         ret = data.ReadString(tempStr);
100         CHECK_ERR_RETURN(ret, true, ret);
101         if (strnlen(tempStr.c_str(), PATH_MAX) == PATH_MAX || strLen != strnlen(tempStr.c_str(), PATH_MAX)) {
102             tloge("recv str length check fail\n");
103             return false;
104         }
105 
106         if (strcpy_s(tempChar, PATH_MAX + 1, tempStr.c_str()) != EOK) {
107             tloge("copy str fail, errno = %{public}d\n", errno);
108             return false;
109         }
110         *str = tempChar;
111     }
112 
113     return true;
114 }
115 
InitContextRecvProc(MessageParcel & data,MessageParcel & reply)116 int32_t CaDaemonStub::InitContextRecvProc(MessageParcel &data, MessageParcel &reply)
117 {
118     tlogi("CaDaemonStub: InitContextRecvProc start");
119     if (!EnforceInterceToken(data)) {
120         tloge("CaDaemonStub: InitContextRecvProc interface token check failed!");
121         return ERR_UNKNOWN_REASON;
122     }
123 
124     const char *name = nullptr;
125     char tempChar[PATH_MAX + 1] = { 0 };
126     if (!GetChar(data, tempChar, &name)) {
127         tloge("InitContextRecvProc: get name failed\n");
128         return ERR_UNKNOWN_OBJECT;
129     }
130 
131     if (InitializeContext(name, reply) != TEEC_SUCCESS) {
132         tloge("initialize context failed\n");
133         return ERR_UNKNOWN_REASON;
134     }
135 
136     return ERR_NONE;
137 }
138 
FinalContextRecvProc(MessageParcel & data,MessageParcel & reply)139 int32_t CaDaemonStub::FinalContextRecvProc(MessageParcel &data, MessageParcel &reply)
140 {
141     (void)reply;
142     tlogi("CaDaemonStub: FinalContextRecvProc start");
143     if (!EnforceInterceToken(data)) {
144         tloge("CaDaemonStub: FinalContextRecvProc interface token check failed!");
145         return -1;
146     }
147 
148     TEEC_Context *context = nullptr;
149     size_t len = sizeof(*context);
150     context = (TEEC_Context *)(data.ReadBuffer(len));
151     if (context == nullptr) {
152         return ERR_UNKNOWN_OBJECT;
153     }
154 
155     if (FinalizeContext(context) != TEEC_SUCCESS) {
156         return ERR_UNKNOWN_REASON;
157     }
158 
159     return ERR_NONE;
160 }
161 
GetContextFromData(MessageParcel & data,TEEC_Context * context)162 static bool GetContextFromData(MessageParcel &data, TEEC_Context *context)
163 {
164     size_t len = sizeof(*context);
165     TEEC_Context *tempContext = (TEEC_Context *)(data.ReadBuffer(len));
166     if (tempContext == nullptr) {
167         return false;
168     }
169 
170     if (memcpy_s(context, len, tempContext, len) != EOK) {
171         tloge("getContext: operation memcpy failed\n");
172         return false;
173     }
174     return true;
175 }
176 
GetSessionFromData(MessageParcel & data,TEEC_Session * session)177 static bool GetSessionFromData(MessageParcel &data, TEEC_Session *session)
178 {
179     TEEC_Session *tempSession = nullptr;
180     size_t len = sizeof(*tempSession);
181     tempSession = (TEEC_Session *)(data.ReadBuffer(len));
182     if (tempSession == nullptr) {
183         return ERR_UNKNOWN_OBJECT;
184     }
185 
186     if (memcpy_s(session, len, tempSession, len) != EOK) {
187         tloge("getSession: operation memcpy failed\n");
188         return false;
189     }
190     return true;
191 }
192 
GetSharedMemFromData(MessageParcel & data,TEEC_SharedMemory * shm)193 static bool GetSharedMemFromData(MessageParcel &data, TEEC_SharedMemory *shm)
194 {
195     tlogi("start to recieve sharedmem\n");
196     TEEC_SharedMemory *tempShm = nullptr;
197     size_t len = sizeof(*tempShm);
198     tempShm = (TEEC_SharedMemory *)(data.ReadBuffer(len));
199     if (tempShm == nullptr) {
200         return ERR_UNKNOWN_OBJECT;
201     }
202 
203     if (memcpy_s(shm, len, tempShm, len) != EOK) {
204         tloge("getShamem: operation memcpy failed\n");
205         return false;
206     }
207     return true;
208 }
209 
GetOperationFromData(MessageParcel & data,TEEC_Operation * operation,bool & opFlag)210 static bool GetOperationFromData(MessageParcel &data, TEEC_Operation *operation, bool &opFlag)
211 {
212     bool retTmp = data.ReadBool(opFlag);
213     uint32_t paramType[TEEC_PARAM_NUM] = { 0 };
214     CHECK_ERR_RETURN(retTmp, true, retTmp);
215 
216     if (!opFlag) {
217         tloge("operation is nullptr\n");
218         return true;
219     }
220 
221     size_t len = sizeof(*operation);
222     TEEC_Operation *tempOp = (TEEC_Operation *)(data.ReadBuffer(len));
223     if (tempOp == nullptr) {
224         return false;
225     }
226 
227     if (memcpy_s(operation, len, tempOp, len) != EOK) {
228         tloge("getOperation: operation memcpy failed\n");
229         return false;
230     }
231 
232     /* clear the pointer from ca to avoid access to invalid address */
233     operation->session = nullptr;
234     for (uint32_t paramCnt = 0; paramCnt < TEEC_PARAM_NUM; paramCnt++) {
235         paramType[paramCnt] = TEEC_PARAM_TYPE_GET(operation->paramTypes, paramCnt);
236         if (IS_TEMP_MEM(paramType[paramCnt])) {
237             operation->params[paramCnt].tmpref.buffer = nullptr;
238         } else if (IS_PARTIAL_MEM(paramType[paramCnt])) {
239             operation->params[paramCnt].memref.parent = nullptr;
240         }
241     }
242     return true;
243 }
244 
245 
ReadFd(MessageParcel & data,int32_t & fd)246 static bool ReadFd(MessageParcel &data, int32_t &fd)
247 {
248     bool fdFlag = false;
249 
250     bool retTmp = data.ReadBool(fdFlag);
251     CHECK_ERR_RETURN(retTmp, true, retTmp);
252 
253     if (fdFlag) {
254         fd = data.ReadFileDescriptor();
255         if (fd < 0) {
256             tloge("read fd failed\n");
257             return false;
258         }
259         tloge("read fd success = %{public}d\n", fd);
260     }
261     return true;
262 }
263 
ClearAsmMem(sptr<Ashmem> & optMem)264 static void ClearAsmMem(sptr<Ashmem> &optMem)
265 {
266     if (optMem != nullptr) {
267         optMem->UnmapAshmem();
268         optMem->CloseAshmem();
269     }
270 }
271 
GetOptMemFromData(MessageParcel & data,sptr<Ashmem> & optMem,uint32_t & optMemSize)272 static bool GetOptMemFromData(MessageParcel &data, sptr<Ashmem> &optMem, uint32_t &optMemSize)
273 {
274     tlogi("get optMem start\n");
275     bool retTmp = data.ReadUint32(optMemSize);
276     CHECK_ERR_RETURN(retTmp, true, retTmp);
277 
278     if (optMemSize > 0) {
279         optMem = data.ReadAshmem();
280         if (optMem == nullptr) {
281             tloge("get optMem failed\n");
282             return false;
283         }
284 
285         bool ret = optMem->MapReadAndWriteAshmem();
286         if (!ret) {
287             tloge("map ashmem failed\n");
288             return false;
289         }
290         tloge("get optMem success\n");
291     }
292 
293     return true;
294 }
295 
ReadOpenData(MessageParcel & data,int32_t & fd,TEEC_UUID ** uuid,uint32_t & connMethod)296 static int32_t ReadOpenData(MessageParcel &data, int32_t &fd, TEEC_UUID **uuid, uint32_t &connMethod)
297 {
298     size_t len = sizeof(**uuid);
299 
300     bool retTmp = ReadFd(data, fd);
301     if (!retTmp) {
302         tloge("read ta fd failed\n");
303         goto ERROR;
304     }
305 
306     *uuid = (TEEC_UUID *)(data.ReadBuffer(len));
307     if (*uuid == nullptr) {
308         tloge("read uuid failed\n");
309         goto ERROR;
310     }
311 
312     retTmp = data.ReadUint32(connMethod);
313     if (!retTmp) {
314         tloge("read connection method failed\n");
315         goto ERROR;
316     }
317 
318     return ERR_NONE;
319 
320 ERROR:
321     if (fd >= 0) {
322         close(fd);
323     }
324 
325     return ERR_UNKNOWN_OBJECT;
326 }
327 
OpenSessionRecvProc(MessageParcel & data,MessageParcel & reply)328 int32_t CaDaemonStub::OpenSessionRecvProc(MessageParcel &data, MessageParcel &reply)
329 {
330     int32_t result = ERR_NONE;
331     tlogi("CaDaemonStub: OpenSessionRecvProc start");
332     if (!EnforceInterceToken(data)) {
333         tloge("CaDaemonStub: OpenSessionRecvProc interface token check failed!");
334         return -1;
335     }
336 
337     TEEC_Context context;
338     bool retTmp = GetContextFromData(data, &context);
339     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
340 
341     const char *taPath = nullptr;
342     char tempChar[PATH_MAX + 1] = { 0 };
343     retTmp = GetChar(data, tempChar, &taPath);
344     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
345     if (taPath != nullptr) {
346         tloge("recieve taPath is not nullptr\n");
347         context.ta_path = reinterpret_cast<uint8_t *>(const_cast<char *>(taPath));
348     }
349 
350     int32_t fd = -1;
351     TEEC_UUID *uuid = nullptr;
352     uint32_t connMethod;
353     result = ReadOpenData(data, fd, &uuid, connMethod);
354     CHECK_ERR_RETURN(result, ERR_NONE, ERR_UNKNOWN_OBJECT);
355 
356     TEEC_Operation operation;
357     bool opFlag = false;
358     retTmp = GetOperationFromData(data, &operation, opFlag);
359     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
360 
361     uint32_t optMemSize;
362     sptr<Ashmem> optMem;
363     retTmp = GetOptMemFromData(data, optMem, optMemSize);
364     if (!retTmp) {
365         result = ERR_UNKNOWN_OBJECT;
366         goto END;
367     }
368 
369     if (opFlag) {
370         result = OpenSession(&context, taPath, fd, uuid, connMethod, &operation, optMemSize, optMem, reply);
371     } else {
372         result = OpenSession(&context, taPath, fd, uuid, connMethod, nullptr, optMemSize, optMem, reply);
373     }
374 
375 END:
376     ClearAsmMem(optMem);
377 
378     if (fd >= 0) {
379         close(fd);
380     }
381 
382     return result;
383 }
384 
CloseSessionRecvProc(MessageParcel & data,MessageParcel & reply)385 int32_t CaDaemonStub::CloseSessionRecvProc(MessageParcel &data, MessageParcel &reply)
386 {
387     (void)reply;
388     tlogi("CaDaemonStub: CloseSessionRecvProc start");
389     if (!EnforceInterceToken(data)) {
390         tloge("CaDaemonStub: CloseSessionRecvProc interface token check failed!");
391         return -1;
392     }
393 
394     TEEC_Context context;
395     bool retTmp = GetContextFromData(data, &context);
396     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
397 
398     TEEC_Session session;
399     retTmp = GetSessionFromData(data, &session);
400     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
401 
402     if (CloseSession(&session, &context) != TEEC_SUCCESS) {
403         return ERR_UNKNOWN_REASON;
404     }
405 
406     return ERR_NONE;
407 }
InvokeCommandRecvProc(MessageParcel & data,MessageParcel & reply)408 int32_t CaDaemonStub::InvokeCommandRecvProc(MessageParcel &data, MessageParcel &reply)
409 {
410     int32_t result = ERR_NONE;
411     tlogi("CaDaemonStub: InvokeCommandRecvProc start");
412     if (!EnforceInterceToken(data)) {
413         tloge("CaDaemonStub: InvokeCommandRecvProc interface token check failed!");
414         return -1;
415     }
416 
417     TEEC_Context context;
418     bool retTmp = GetContextFromData(data, &context);
419     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
420 
421     TEEC_Session session;
422     retTmp = GetSessionFromData(data, &session);
423     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
424 
425     uint32_t commandID;
426     retTmp = data.ReadUint32(commandID);
427     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
428 
429     TEEC_Operation operation;
430     bool opFlag = false;
431     retTmp = GetOperationFromData(data, &operation, opFlag);
432     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
433 
434     uint32_t optMemSize;
435     sptr<Ashmem> optMem;
436     retTmp = GetOptMemFromData(data, optMem, optMemSize);
437     if (!retTmp) {
438         result = ERR_UNKNOWN_OBJECT;
439         goto END;
440     }
441 
442     if (opFlag) {
443         result = InvokeCommand(&context, &session, commandID, &operation, optMemSize, optMem, reply);
444     } else {
445         result = InvokeCommand(&context, &session, commandID, nullptr, optMemSize, optMem, reply);
446     }
447 
448 END:
449     ClearAsmMem(optMem);
450     return result;
451 }
452 
RegisterMemRecvProc(MessageParcel & data,MessageParcel & reply)453 int32_t CaDaemonStub::RegisterMemRecvProc(MessageParcel &data, MessageParcel &reply)
454 {
455     tlogi("CaDaemonStub: RegisterMemRecvProc start");
456     if (!EnforceInterceToken(data)) {
457         tloge("CaDaemonStub: RegisterMemRecvProc interface token check failed!");
458         return -1;
459     }
460 
461     TEEC_Context context;
462     bool retTmp = GetContextFromData(data, &context);
463     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
464 
465     TEEC_SharedMemory sharedMem;
466     retTmp = GetSharedMemFromData(data, &sharedMem);
467     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
468 
469     if (RegisterSharedMemory(&context, &sharedMem, reply) != TEEC_SUCCESS) {
470         return ERR_UNKNOWN_REASON;
471     }
472 
473     return ERR_NONE;
474 }
475 
AllocMemRecvProc(MessageParcel & data,MessageParcel & reply)476 int32_t CaDaemonStub::AllocMemRecvProc(MessageParcel &data, MessageParcel &reply)
477 {
478     tlogi("CaDaemonStub: AllocMemRecvProc start");
479     if (!EnforceInterceToken(data)) {
480         tloge("CaDaemonStub: AllocMemRecvProc interface token check failed!");
481         return -1;
482     }
483 
484     TEEC_Context context;
485     bool retTmp = GetContextFromData(data, &context);
486     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
487 
488     TEEC_SharedMemory sharedMem;
489     retTmp = GetSharedMemFromData(data, &sharedMem);
490     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
491 
492     if (AllocateSharedMemory(&context, &sharedMem, reply) != TEEC_SUCCESS) {
493         return ERR_UNKNOWN_REASON;
494     }
495 
496     return ERR_NONE;
497 }
498 
ReleaseMemRecvProc(MessageParcel & data,MessageParcel & reply)499 int32_t CaDaemonStub::ReleaseMemRecvProc(MessageParcel &data, MessageParcel &reply)
500 {
501     tlogi("CaDaemonStub: ReleaseMemRecvProc start");
502     if (!EnforceInterceToken(data)) {
503         tloge("CaDaemonStub: ReleaseMemRecvProc interface token check failed!");
504         return -1;
505     }
506 
507     TEEC_Context context;
508     bool retTmp = GetContextFromData(data, &context);
509     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
510 
511     TEEC_SharedMemory sharedMem;
512     retTmp = GetSharedMemFromData(data, &sharedMem);
513     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
514 
515     uint32_t shmOffset;
516     retTmp = data.ReadUint32(shmOffset);
517     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
518 
519     ReleaseSharedMemory(&context, &sharedMem, shmOffset, reply);
520 
521     return ERR_NONE;
522 }
523 
SetCallBackRecvProc(MessageParcel & data,MessageParcel & reply)524 int32_t CaDaemonStub::SetCallBackRecvProc(MessageParcel &data, MessageParcel &reply)
525 {
526     tlogi("CaDaemonStub: SetCallBackRecvProc start");
527     if (!EnforceInterceToken(data)) {
528         tloge("CaDaemonStub: SetCallBackRecvProc interface token check failed!");
529         return -1;
530     }
531 
532     sptr<IRemoteObject> notify = nullptr;
533     notify = data.ReadRemoteObject();
534     if (notify == nullptr) {
535         tloge("CaDaemonStub: recieve notify is nullptr\n");
536         return ERR_NONE;
537     }
538 
539     return SetCallBack(notify);
540 }
541 
EnforceInterceToken(MessageParcel & data)542 bool CaDaemonStub::EnforceInterceToken(MessageParcel& data)
543 {
544     u16string interfaceToken = data.ReadInterfaceToken();
545     return interfaceToken == INTERFACE_TOKEN;
546 }
547 
SendSecFileRecvProc(MessageParcel & data,MessageParcel & reply)548 int32_t CaDaemonStub::SendSecFileRecvProc(MessageParcel& data, MessageParcel &reply)
549 {
550     tlogi("CaDaemonStub: SendSecFileRecvProc start");
551     if (!EnforceInterceToken(data)) {
552         tloge("CaDaemonStub: SendSecFileRecvProc interface token check failed!");
553         return -1;
554     }
555     const char *path = nullptr;
556     char tempChar[PATH_MAX + 1] = {0};
557     if (!GetChar(data, tempChar, &path)) {
558         tloge("SendSecFileRecvProc: get path failed\n");
559         return ERR_UNKNOWN_OBJECT;
560     }
561 
562     int fd;
563     FILE *fp = nullptr;
564     bool retTmp = ReadFd(data, fd);
565     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
566     fp = fdopen(fd, "r");
567 
568     TEEC_Context context;
569     retTmp = GetContextFromData(data, &context);
570     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
571 
572     TEEC_Session session;
573     retTmp = GetSessionFromData(data, &session);
574     CHECK_ERR_RETURN(retTmp, true, ERR_UNKNOWN_OBJECT);
575 
576     (void)SendSecfile(path, context.fd, fp, reply);
577 
578     return ERR_NONE;
579 }
580 
GetTeeVersionRecvProc(MessageParcel & data,MessageParcel & reply)581 int32_t CaDaemonStub::GetTeeVersionRecvProc(MessageParcel& data, MessageParcel &reply)
582 {
583     (void)GetTeeVersion(reply);
584     return ERR_NONE;
585 }
586 } // namespace CaDaemon
587 } // namespace OHOS
588