1 //******************************************************************
3 // Copyright 2015 Samsung Electronics All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
26 #include "ocpayload.h"
27 #include "provisioninghandler.h"
32 #include "cathreadpool.h"
34 #include "oic_malloc.h"
35 static bool sent_provision_request = false;
37 * @var g_provisioningMutex
38 * @brief Mutex to synchronize access to g_caDtlsContext.
40 static ca_mutex g_provisioningMutex = NULL;
41 static ca_cond g_provisioningCond = NULL;
42 bool g_provisioningCondFlag = false;
44 static EnrolleeNWProvInfo_t* netProvInfo;
48 * @brief Callback for providing provisioning status callback to application
50 static OCProvisioningStatusCB cbData = NULL;
51 static ca_thread_pool_t g_threadPoolHandle = NULL;
53 void ErrorCallback(ProvStatus status)
55 ProvisioningInfo *provInfo = GetCallbackObjectOnError(status);
59 OCStackResult InitProvisioningHandler()
61 OCStackResult ret = OC_STACK_ERROR;
62 /* Initialize OCStack*/
63 if (OCInit(NULL, 0, OC_CLIENT) != OC_STACK_OK)
65 OIC_LOG(ERROR, TAG, "OCStack init error");
69 g_provisioningMutex = ca_mutex_new();
71 OIC_LOG(DEBUG, TAG, "ca_thread_pool_init initializing");
73 if (CA_STATUS_OK != ca_thread_pool_init(2, &g_threadPoolHandle))
75 OIC_LOG(DEBUG, TAG, "thread_pool_init failed");
76 return OC_STACK_ERROR;
79 g_provisioningCond = ca_cond_new();
80 if (NULL == g_provisioningCond)
82 OIC_LOG(DEBUG, TAG, "Failed to create condition");
83 ca_mutex_free(g_provisioningMutex);
84 ca_thread_pool_free(g_threadPoolHandle);
85 return OC_STACK_ERROR;
88 char *string = "listeningFunc invoked in a thread";
89 if (CA_STATUS_OK != ca_thread_pool_add_task(g_threadPoolHandle, listeningFunc, (void *) string))
91 OIC_LOG(DEBUG, TAG, "thread_pool_add_task failed");
92 ca_thread_pool_free(g_threadPoolHandle);
93 ca_mutex_unlock(g_provisioningMutex);
94 ca_mutex_free(g_provisioningMutex);
95 ca_cond_free(g_provisioningCond);
96 return OC_STACK_ERROR;
101 OCStackResult TerminateProvisioningHandler()
103 OCStackResult ret = OC_STACK_ERROR;
104 if (OCStop() != OC_STACK_OK)
106 OIC_LOG(ERROR, TAG, "OCStack stop error");
109 ca_mutex_lock(g_provisioningMutex);
110 g_provisioningCondFlag = true;
111 //ca_cond_signal(g_provisioningCond);
112 ca_mutex_unlock(g_provisioningMutex);
114 ca_mutex_free(g_provisioningMutex);
115 g_provisioningMutex = NULL;
117 ca_thread_pool_free(g_threadPoolHandle);
118 g_threadPoolHandle = NULL;
124 void listeningFunc(void *data)
126 while (!g_provisioningCondFlag)
128 OCStackResult result;
130 ca_mutex_lock(g_provisioningMutex);
131 result = OCProcess();
132 ca_mutex_unlock(g_provisioningMutex);
134 if (result != OC_STACK_OK)
136 OIC_LOG(ERROR, TAG, "OCStack stop error");
139 // To minimize CPU utilization we may wish to do this with sleep
144 OCStackApplicationResult ProvisionEnrolleeResponse(void* ctx, OCDoHandle handle,
145 OCClientResponse * clientResponse)
147 OIC_LOG_V(DEBUG, TAG, "INSIDE ProvisionEnrolleeResponse");
148 ProvisioningInfo *provInfo;
150 if (!ValidateEnrolleResponse(clientResponse))
152 ErrorCallback( DEVICE_NOT_PROVISIONED);
153 return OC_STACK_DELETE_TRANSACTION;
159 OCRepPayload* input = (OCRepPayload*) (clientResponse->payload);
165 if (OCRepPayloadGetPropInt(input, OC_RSRVD_ES_PS, &ps))
175 OIC_LOG_V(DEBUG, TAG, "PS is NOT proper");
181 if (OCRepPayloadGetPropString(input, OC_RSRVD_ES_TNN, &tnn))
183 if (!strcmp(tnn, netProvInfo->netAddressInfo.WIFI.ssid))
185 OIC_LOG_V(DEBUG, TAG, "SSID is proper");
191 OIC_LOG_V(DEBUG, TAG, "SSID is NOT proper");
196 if (OCRepPayloadGetPropString(input, OC_RSRVD_ES_CD, &cd))
198 if (!strcmp(cd, netProvInfo->netAddressInfo.WIFI.pwd))
200 OIC_LOG_V(DEBUG, TAG, "Password is proper");
206 OIC_LOG_V(DEBUG, TAG, "Password is NOT proper");
211 LogProvisioningResponse(input->values);
217 SuccessCallback(clientResponse);
219 return OC_STACK_KEEP_TRANSACTION;
224 ErrorCallback( DEVICE_NOT_PROVISIONED);
226 return OC_STACK_DELETE_TRANSACTION;
231 OCStackResult ProvisionEnrollee(OCQualityOfService qos, const char* query, const char* resUri,
232 OCDevAddr *destination)
234 OIC_LOG_V(INFO, TAG, "Sleeping for 2 seconds");
236 OIC_LOG_V(INFO, TAG, "\n\nExecuting ProvisionEnrollee%s", __func__);
238 OCRepPayload* payload = OCRepPayloadCreate();
240 OCRepPayloadSetUri(payload, resUri);
241 OCRepPayloadSetPropString(payload, OC_RSRVD_ES_TNN, netProvInfo->netAddressInfo.WIFI.ssid);
242 OCRepPayloadSetPropString(payload, OC_RSRVD_ES_CD, netProvInfo->netAddressInfo.WIFI.pwd);
244 OIC_LOG_V(DEBUG, TAG, "OCPayload ready for ProvisionEnrollee");
246 OCStackResult ret = InvokeOCDoResource(query, OC_REST_PUT, destination, OC_HIGH_QOS,
247 ProvisionEnrolleeResponse, payload, NULL, 0);
251 OCStackApplicationResult GetProvisioningStatusResponse(void* ctx, OCDoHandle handle,
252 OCClientResponse * clientResponse)
254 if (sent_provision_request == true)
255 return OC_STACK_DELETE_TRANSACTION;
256 sent_provision_request = true;
257 OIC_LOG_V(DEBUG, TAG, "INside GetProvisioningStatusResponse");
259 ProvisioningInfo *provInfo;
261 if (!ValidateEnrolleResponse(clientResponse))
263 ErrorCallback( DEVICE_NOT_PROVISIONED);
265 return OC_STACK_DELETE_TRANSACTION;
268 OCRepPayload* input = (OCRepPayload*) (clientResponse->payload);
270 char query[OIC_STRING_MAX_VALUE] =
272 char resURI[MAX_URI_LENGTH] =
275 OIC_LOG_V(DEBUG, TAG, "resUri = %s", input->uri);
277 strncpy(resURI, input->uri, sizeof(resURI));
279 snprintf(query, sizeof(query), UNICAST_PROVISIONING_QUERY_BLE, clientResponse->addr->addr);
281 OIC_LOG_V(DEBUG, TAG, "query = %s", query);
283 OCDevAddr *devaddress = &clientResponse->devAddr;
284 devaddress->adapter = OC_ADAPTER_GATT_BTLE;
286 //OCPayloadLogRep(DEBUG,TAG,input);
288 if (ProvisionEnrollee(OC_HIGH_QOS, query, OC_RSRVD_ES_URI_PROV, devaddress) != OC_STACK_OK)
291 "GetProvisioningStatusResponse received NULL clientResponse.Invoking Provisioing Status Callback");
293 ErrorCallback( DEVICE_NOT_PROVISIONED);
295 return OC_STACK_DELETE_TRANSACTION;
298 return OC_STACK_KEEP_TRANSACTION;
302 OCStackResult InvokeOCDoResource(const char* query, OCMethod method, const OCDevAddr *dest,
303 OCQualityOfService qos, OCClientResponseHandler cb, OCRepPayload* payload,
304 OCHeaderOption * options, uint8_t numOptions)
307 OCCallbackData cbData;
310 cbData.context = (void*) DEFAULT_CONTEXT_VALUE;
313 ret = OCDoResource(NULL, method, query, dest, (OCPayload*) payload, OC_CONNTYPE_BLE, qos,
314 &cbData, options, numOptions);
316 if (ret != OC_STACK_OK)
318 OIC_LOG_V(ERROR, TAG, "OCDoResource returns error %d with method %d", ret, method);
324 OCStackResult GetProvisioningStatus(OCQualityOfService qos, const char* query,
325 const OCDevAddr *destination)
327 OCStackResult ret = OC_STACK_ERROR;
328 OIC_LOG_V(INFO, TAG, "\n\nExecuting %s %d", __func__, destination->adapter);
329 ret = InvokeOCDoResource(query, OC_REST_GET, destination, OC_HIGH_QOS,
330 GetProvisioningStatusResponse, NULL, NULL, 0);
334 OCStackResult StartProvisioningProcess(const EnrolleeNWProvInfo_t *netInfo,
335 OCProvisioningStatusCB provisioningStatusCallback)
338 OCStackResult result = OC_STACK_ERROR;
340 if (!ValidateEasySetupParams(netInfo, provisioningStatusCallback))
345 //Only basis test is done for below API
346 if (!SetProgress(provisioningStatusCallback))
348 // Device provisioning session is running already.
349 OIC_LOG(INFO, TAG, PCF("Device provisioning session is running already"));
353 if (!ConfigEnrolleeObject(netInfo))
358 if (CA_STATUS_OK != ca_thread_pool_add_task(g_threadPoolHandle, FindProvisioningResource,
368 ErrorCallback( DEVICE_NOT_PROVISIONED);
370 return OC_STACK_ERROR;
375 void StopProvisioningProcess()
377 //Only basis test is done for below API
381 // This is a function called back when a device is discovered
382 OCStackApplicationResult FindProvisioningResourceResponse(void* ctx, OCDoHandle handle,
383 OCClientResponse * clientResponse)
386 OIC_LOG(INFO, TAG, PCF("Entering FindProvisioningResourceResponse"));
388 if (!ValidateFinddResourceResponse(clientResponse))
390 ErrorCallback( DEVICE_NOT_PROVISIONED);
391 return OC_STACK_DELETE_TRANSACTION;
394 OCStackApplicationResult response = OC_STACK_DELETE_TRANSACTION;
396 ProvisioningInfo *provInfo;
397 char szQueryUri[64] =
400 OCDiscoveryPayload* discoveryPayload = (OCDiscoveryPayload*) (clientResponse->payload);
402 // Need to conform if below check is required or not. As Null check of clientResponse->payload is already performed above
403 if (!discoveryPayload)
405 OIC_LOG_V(DEBUG, TAG, "Failed To parse");
406 ErrorCallback( DEVICE_NOT_PROVISIONED);
407 return OC_STACK_DELETE_TRANSACTION;
410 OIC_LOG_V(DEBUG, TAG, "resUri = %s", discoveryPayload->resources->uri);
412 snprintf(szQueryUri, sizeof(szQueryUri), UNICAST_PROVISIONING_QUERY_BLE,
413 clientResponse->addr->addr);
415 OCDevAddr *devaddress = &clientResponse->devAddr;
416 devaddress->adapter = OC_ADAPTER_GATT_BTLE;
418 if (strcmp(netProvInfo->netAddressInfo.WIFI.ipAddress, clientResponse->devAddr.addr))
419 OIC_LOG_V(INFO, TAG, "unequal %s %s", netProvInfo->netAddressInfo.WIFI.ipAddress,
420 clientResponse->devAddr.addr);
422 OIC_LOG_V(INFO, TAG, "unequal %s %s", netProvInfo->netAddressInfo.WIFI.ipAddress,
423 clientResponse->devAddr.addr);
425 OIC_LOG_V(DEBUG, TAG, "query before GetProvisioningStatus call = %s %d", szQueryUri,
426 devaddress->adapter);
428 if (GetProvisioningStatus(OC_HIGH_QOS, szQueryUri, devaddress) != OC_STACK_OK)
430 ErrorCallback( DEVICE_NOT_PROVISIONED);
431 return OC_STACK_DELETE_TRANSACTION;
434 return OC_STACK_KEEP_TRANSACTION;
438 void FindProvisioningResource(void *data)
440 OCStackResult ret = OC_STACK_ERROR;
442 /* Start a discovery query*/
443 char szQueryUri[64] =
446 snprintf(szQueryUri, sizeof(szQueryUri), MULTICAST_PROVISIONING_QUERY_BLE);
448 OIC_LOG_V(DEBUG, TAG, "szQueryUri = %s", szQueryUri);
450 OCCallbackData ocCBData;
452 ocCBData.cb = FindProvisioningResourceResponse;
453 ocCBData.context = (void*) DEFAULT_CONTEXT_VALUE;
456 ret = OCDoResource(NULL, OC_REST_DISCOVER, szQueryUri, NULL, NULL, OC_CONNTYPE_BLE, OC_LOW_QOS,
459 if (ret != OC_STACK_OK)
461 ErrorCallback( DEVICE_NOT_PROVISIONED);
466 OCStackApplicationResult SubscribeProvPresenceCallback(void* ctx, OCDoHandle handle,
467 OCClientResponse* clientResponse)
469 OIC_LOG(INFO, TAG, PCF("Entering SubscribeProvPresenceCallback"));
471 OCStackApplicationResult response = OC_STACK_DELETE_TRANSACTION;
473 if (clientResponse->result != OC_STACK_OK)
475 OIC_LOG(ERROR, TAG, "OCStack stop error");
481 OIC_LOG(INFO, TAG, PCF("Client Response exists"));
483 if (clientResponse->payload && clientResponse->payload->type != PAYLOAD_TYPE_REPRESENTATION)
485 OIC_LOG_V(DEBUG, TAG, "Incoming payload not a representation");
489 OCRepPayload* discoveryPayload = (OCRepPayload*) (clientResponse->payload);
490 if (!discoveryPayload)
492 OIC_LOG_V(DEBUG, TAG, "invalid payload");
496 char sourceIPAddr[OIC_STRING_MAX_VALUE] =
498 snprintf(sourceIPAddr, sizeof(sourceIPAddr), "%s", clientResponse->addr->addr);
500 OIC_LOG_V(DEBUG, TAG, "Discovered %s @ %s", discoveryPayload->uri, sourceIPAddr);
502 /* Start a discovery query*/
503 char szQueryUri[64] =
506 snprintf(szQueryUri, sizeof(szQueryUri), UNICAST_PROVISIONING_QUERY, sourceIPAddr, IP_PORT);
508 /*if (FindProvisioningResource(qos, szQueryUri) != OC_STACK_OK) {
509 OIC_LOG(ERROR, TAG, "FindProvisioningResource failed");
510 return OC_STACK_KEEP_TRANSACTION;
515 // clientResponse is invalid
516 OIC_LOG(ERROR, TAG, PCF("Client Response is NULL!"));
518 return OC_STACK_KEEP_TRANSACTION;
521 OCStackResult SubscribeProvPresence(OCQualityOfService qos, const char* requestURI)
523 OCStackResult ret = OC_STACK_ERROR;
525 OCCallbackData cbData;
527 cbData.cb = &SubscribeProvPresenceCallback;
528 cbData.context = (void*) DEFAULT_CONTEXT_VALUE;
531 ret = OCDoResource(NULL, OC_REST_PRESENCE, requestURI, 0, 0, OC_CONNTYPE_BLE, OC_LOW_QOS,
534 if (ret != OC_STACK_OK)
536 OIC_LOG(ERROR, TAG, "OCStack resource error");
542 OCStackResult FindNetworkResource()
544 OCStackResult ret = OC_STACK_ERROR;
545 if (OCStop() != OC_STACK_OK)
547 OIC_LOG(ERROR, TAG, "OCStack stop error");
553 ProvisioningInfo* PrepareProvisioingStatusCB(OCClientResponse * clientResponse,
554 ProvStatus provStatus)
557 ProvisioningInfo *provInfo = (ProvisioningInfo *) OICCalloc(1, sizeof(ProvisioningInfo));
559 if (provInfo == NULL)
561 OIC_LOG_V(ERROR, TAG, "Failed to allocate memory");
565 OCDevAddr *devAddr = (OCDevAddr *) OICCalloc(1, sizeof(OCDevAddr));
569 OIC_LOG_V(ERROR, TAG, "Failed to allocate memory");
573 strncpy(devAddr->addr, clientResponse->addr->addr, sizeof(devAddr->addr));
574 devAddr->port = clientResponse->addr->port;
576 provInfo->provDeviceInfo.addr = devAddr;
578 provInfo->provStatus = provStatus;
583 bool ValidateEasySetupParams(const EnrolleeNWProvInfo_t *netInfo,
584 OCProvisioningStatusCB provisioningStatusCallback)
587 if (netInfo == NULL || netInfo->netAddressInfo.WIFI.ipAddress == NULL)
589 OIC_LOG(ERROR, TAG, "Request URI is NULL");
593 if (provisioningStatusCallback == NULL)
595 OIC_LOG(ERROR, TAG, "ProvisioningStatusCallback is NULL");
606 // It means already Easy Setup provisioning session is going on.
609 OIC_LOG(ERROR, TAG, "Easy setup session is already in progress");
616 bool SetProgress(OCProvisioningStatusCB provisioningStatusCallback)
618 ca_mutex_lock(g_provisioningMutex);
623 cbData = provisioningStatusCallback;
625 ca_mutex_unlock(g_provisioningMutex);
632 ca_mutex_lock(g_provisioningMutex);
636 ca_mutex_unlock(g_provisioningMutex);
639 ProvisioningInfo* CreateCallBackObject()
642 ProvisioningInfo *provInfo = (ProvisioningInfo *) OICCalloc(1, sizeof(ProvisioningInfo));
644 if (provInfo == NULL)
646 OIC_LOG_V(ERROR, TAG, "Failed to allocate memory");
650 OCDevAddr *devAddr = (OCDevAddr *) OICCalloc(1, sizeof(OCDevAddr));
654 OIC_LOG_V(ERROR, TAG, "Failed to allocate memory");
658 provInfo->provDeviceInfo.addr = devAddr;
664 ProvisioningInfo* GetCallbackObjectOnError(ProvStatus status)
667 ProvisioningInfo *provInfo = CreateCallBackObject();
668 strncpy(provInfo->provDeviceInfo.addr->addr, netProvInfo->netAddressInfo.WIFI.ipAddress,
669 sizeof(provInfo->provDeviceInfo.addr->addr));
670 provInfo->provDeviceInfo.addr->port = IP_PORT;
671 provInfo->provStatus = status;
675 ProvisioningInfo* GetCallbackObjectOnSuccess(OCClientResponse * clientResponse,
676 ProvStatus provStatus)
678 ProvisioningInfo *provInfo = CreateCallBackObject();
679 strncpy(provInfo->provDeviceInfo.addr->addr, clientResponse->addr->addr,
680 sizeof(provInfo->provDeviceInfo.addr->addr));
681 provInfo->provDeviceInfo.addr->port = clientResponse->addr->port;
682 provInfo->provStatus = provStatus;
686 bool ValidateFinddResourceResponse(OCClientResponse * clientResponse)
689 if (!(clientResponse) || !(clientResponse->payload))
692 OIC_LOG_V(INFO, TAG, "ProvisionEnrolleeResponse received Null clientResponse");
700 bool ValidateEnrolleResponse(OCClientResponse * clientResponse)
703 if (!(clientResponse) || !(clientResponse->payload))
706 OIC_LOG_V(INFO, TAG, "ProvisionEnrolleeResponse received Null clientResponse");
712 if (clientResponse->payload->type != PAYLOAD_TYPE_REPRESENTATION)
715 OIC_LOG_V(DEBUG, TAG, "Incoming payload not a representation");
720 // If flow reachese here means no error condition hit.
725 void SuccessCallback(OCClientResponse * clientResponse)
727 ProvisioningInfo *provInfo = GetCallbackObjectOnSuccess(clientResponse, DEVICE_PROVISIONED);
734 OIC_LOG(DEBUG, TAG, "thread_pool_add_task of FindProvisioningResource failed");
735 ca_thread_pool_free(g_threadPoolHandle);
736 ca_mutex_unlock(g_provisioningMutex);
737 ca_mutex_free(g_provisioningMutex);
738 ca_cond_free(g_provisioningCond);
744 bool ConfigEnrolleeObject(const EnrolleeNWProvInfo_t *netInfo)
747 //Copy Network Provisioning Information
748 netProvInfo = (EnrolleeNWProvInfo_t *) OICCalloc(1, sizeof(EnrolleeNWProvInfo_t));
750 if (netProvInfo == NULL)
752 OIC_LOG(ERROR, TAG, "Invalid input..");
756 memcpy(netProvInfo, netInfo, sizeof(EnrolleeNWProvInfo_t));
758 OIC_LOG_V(DEBUG, TAG, "Network Provisioning Info. SSID = %s",
759 netProvInfo->netAddressInfo.WIFI.ssid);
761 OIC_LOG_V(DEBUG, TAG, "Network Provisioning Info. PWD = %s",
762 netProvInfo->netAddressInfo.WIFI.pwd);
764 OIC_LOG_V(DEBUG, TAG, "Network Provisioning Info. MAC ADDRESS = %s",
765 netInfo->netAddressInfo.LE.leMacAddress);
771 void LogProvisioningResponse(OCRepPayloadValue* val)
776 case OCREP_PROP_NULL:
777 OIC_LOG_V(DEBUG, TAG, "\t\t%s: NULL", val->name);
780 OIC_LOG_V(DEBUG, TAG, "\t\t%s(int):%lld", val->name, val->i);
782 case OCREP_PROP_DOUBLE:
783 OIC_LOG_V(DEBUG, TAG, "\t\t%s(double):%f", val->name, val->d);
785 case OCREP_PROP_BOOL:
786 OIC_LOG_V(DEBUG, TAG, "\t\t%s(bool):%s", val->name, val->b ? "true" : "false");
788 case OCREP_PROP_STRING:
789 OIC_LOG_V(DEBUG, TAG, "\t\t%s(string):%s", val->name, val->str);
791 case OCREP_PROP_OBJECT:
792 // Note: Only prints the URI (if available), to print further, you'll
793 // need to dig into the object better!
794 OIC_LOG_V(DEBUG, TAG, "\t\t%s(OCRep):%s", val->name, val->obj->uri);
796 case OCREP_PROP_ARRAY:
797 switch (val->arr.type)
800 OIC_LOG_V(DEBUG, TAG, "\t\t%s(int array):%lld x %lld x %lld", val->name,
801 val->arr.dimensions[0], val->arr.dimensions[1], val->arr.dimensions[2]);
803 case OCREP_PROP_DOUBLE:
804 OIC_LOG_V(DEBUG, TAG, "\t\t%s(double array):%lld x %lld x %lld", val->name,
805 val->arr.dimensions[0], val->arr.dimensions[1], val->arr.dimensions[2]);
807 case OCREP_PROP_BOOL:
808 OIC_LOG_V(DEBUG, TAG, "\t\t%s(bool array):%lld x %lld x %lld", val->name,
809 val->arr.dimensions[0], val->arr.dimensions[1], val->arr.dimensions[2]);
811 case OCREP_PROP_STRING:
812 OIC_LOG_V(DEBUG, TAG, "\t\t%s(string array):%lld x %lld x %lld", val->name,
813 val->arr.dimensions[0], val->arr.dimensions[1], val->arr.dimensions[2]);
815 case OCREP_PROP_OBJECT:
816 OIC_LOG_V(DEBUG, TAG, "\t\t%s(OCRep array):%lld x %lld x %lld", val->name,
817 val->arr.dimensions[0], val->arr.dimensions[1], val->arr.dimensions[2]);
820 //OIC_LOG_V(ERROR, TAG, "\t\t%s <-- Unknown/unsupported array type!",
826 /*OC_LOG_V(ERROR, TAG
827 , "\t\t%s <-- Unknown type!", val->name);*/