resource-container: Install unit tests
[iotivity.git] / resource / csdk / stack / src / oickeepalive.c
1 /* ****************************************************************
2  *
3  * Copyright 2015 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  ******************************************************************/
20
21 #include "oickeepalive.h"
22
23 #include <stdio.h>
24 #include <string.h>
25 #include "oic_malloc.h"
26 #include "oic_string.h"
27 #include "oic_time.h"
28 #include "experimental/ocrandom.h"
29 #include "uarraylist.h"
30 #include "ocstackinternal.h"
31 #include "ocpayloadcbor.h"
32 #include "ocpayload.h"
33 #include "ocresourcehandler.h"
34 #include "experimental/logger.h"
35
36 /**
37  * Logging tag for module name.
38  */
39 #define TAG "OIC_RI_KEEPALIVE"
40
41 static const uint64_t USECS_PER_SEC = 1000000;
42
43 //-----------------------------------------------------------------------------
44 // Macros
45 //-----------------------------------------------------------------------------
46 #define VERIFY_SUCCESS(op, successCode) { if ((op) != (successCode)) \
47             {OIC_LOG_V(FATAL, TAG, "%s failed!!", #op); goto exit;} }
48
49 #define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OIC_LOG((logLevel), \
50              TAG, #arg " is NULL"); return (retVal); } }
51
52 #define VERIFY_NON_NULL_NR(arg, logLevel) { if (!(arg)) { OIC_LOG((logLevel), \
53              TAG, #arg " is NULL"); return; } }
54
55 #define VERIFY_NON_NULL_V(arg) { if (!arg) {OIC_LOG_V(FATAL, TAG, "%s is NULL", #arg);\
56     goto exit;} }
57
58 /**
59  * The KeepAlive table entries are removed
60  * if it can't receive response message within 60 seconds.
61  */
62 #define KEEPALIVE_RESPONSE_TIMEOUT_SEC 60
63
64 /**
65  * The Min time interval value. (2 minutes)
66  * start from 2 minutes and increases in multiples of 2 up to a maximum of 64minutes.
67  */
68 #define KEEPALIVE_MIN_INTERVAL 2
69
70 /**
71  * The Max time interval value. (64 minutes)
72  */
73 #define KEEPALIVE_MAX_INTERVAL 64
74
75 /**
76  * Default counts of interval value.
77  */
78 #define DEFAULT_INTERVAL_COUNT  6
79
80 /**
81  * KeepAlive key to parser Payload Table.
82  */
83 static const char INTERVAL[] = "in";
84
85 /**
86  * KeepAlive key to get interval values from Payload Table.
87  */
88 static const char INTERVAL_ARRAY[] = "inarray";
89
90 /**
91  * To check if KeepAlive is initialized.
92  */
93 static bool g_isKeepAliveInitialized = false;
94
95 /**
96  * Pointer to handle of the newly created KeepAlive resource.
97  */
98 static OCResourceHandle g_keepAliveHandle = NULL;
99
100 /**
101  * KeepAlive table which holds connection interval.
102  */
103 static u_arraylist_t *g_keepAliveConnectionTable = NULL;
104
105 /**
106  * KeepAlive table entries.
107  */
108 typedef struct
109 {
110     OCMode mode;                    /**< host Mode of Operation. */
111     CAEndpoint_t remoteAddr;        /**< destination Address. */
112     int64_t interval;              /**< time interval for KeepAlive. in seconds.*/
113     int32_t currIndex;              /**< current interval value index. */
114     size_t intervalSize;            /**< total interval counts. */
115     int64_t *intervalInfo;          /**< interval values for KeepAlive. */
116     bool sentPingMsg;               /**< if oic client already sent ping message. */
117     uint64_t timeStamp;             /**< last sent or received ping message. in microseconds. */
118 } KeepAliveEntry_t;
119
120 /**
121  * Send disconnect message to remove connection.
122  */
123 static OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry);
124
125 /**
126  * Send ping message to remote endpoint.
127  */
128 static OCStackResult SendPingMessage(KeepAliveEntry_t *entry);
129
130 /**
131  * Increase interval value to send next ping message.
132  */
133 static void IncreaseInterval(KeepAliveEntry_t *entry);
134
135 /**
136  * Ping Message callback registered with RI for KeepAlive Request.
137  */
138 static OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
139                                              OCClientResponse * clientResponse);
140
141 /**
142  * This function creates KeepAlive resource.
143  * @return  ::OC_STACK_OK or Appropriate error code.
144  */
145 static OCStackResult CreateKeepAliveResource(void);
146
147 /**
148  * This function deletes KeepAlive resource.
149  * @return  ::OC_STACK_OK or Appropriate error code.
150  */
151 static OCStackResult DeleteKeepAliveResource(void);
152
153 /**
154  * API to handle the GET request received for a KeepAlive resource.
155  * @param[in]   request     Request Received.
156  * @param[in]   resource    Resource handle used for sending the response.
157  * @return  ::OC_STACK_OK or Appropriate error code.
158  */
159 static OCEntityHandlerResult HandleKeepAliveGETRequest(OCServerRequest *request,
160                                                        const OCResource *resource);
161
162 /**
163  * API to handle the POST request received for a KeepAlive resource.
164  * @param[in]   request     Request Received.
165  * @param[in]   resource    Resource handle used for sending the response.
166  * @return  ::OC_STACK_OK or Appropriate error code.
167  */
168 static OCEntityHandlerResult HandleKeepAlivePOSTRequest(OCServerRequest *request,
169                                                         const OCResource *resource);
170
171 /**
172  * API to handle the Response payload.
173  * @param[in]   endpoint        RemoteEndpoint which sent the packet.
174  * @param[in]   responseCode    Received reseponse code.
175  * @param[in]   respPayload     Response payload.
176  * @return  ::OC_STACK_OK or Appropriate error code.
177  */
178 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
179                                       OCStackResult responseCode,
180                                       const OCRepPayload *respPayload);
181 /**
182  * Gets keepalive entry.
183  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
184  *                          port, reference uri and transport type) to
185  *                          which the ping message has to be sent.
186  * @param[out]  index       index of array list.
187  * @return  KeepAlive entry to send ping message.
188  */
189 static KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, size_t *index);
190
191 /**
192  * Add keepalive entry.
193  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
194  *                          port, reference uri and transport type).
195  * @param[in]   mode        Whether it is OIC Server or OIC Client.
196  * @param[in]   intervalArray   Received interval values from cloud server.
197  * @return  The KeepAlive entry added in KeepAlive Table.
198  */
199 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode,
200                                     int64_t *intervalArray);
201
202 /**
203  * Remove keepalive entry.
204  * @param[in]   endpoint    Remote Endpoint information (like ipaddress,
205  *                          port, reference uri and transport type).
206  * @return  The KeepAlive entry removed in KeepAlive Table.
207  */
208 static OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint);
209
210 /**
211  * Create KeepAlive paylaod to send message.
212  * @param[in]   interval   The interval value to be sent.
213  * @return  Created representation payload.
214  */
215 static OCRepPayload *CreateKeepAlivePayload(int64_t interval);
216
217 /**
218  * Send response to remote device.
219  * @param[in]   request     Request Received.
220  * @param[in]   result      Result to be sent.
221  * @return  ::OC_STACK_OK or Appropriate error code.
222  */
223 static OCStackResult SendKeepAliveResponse(OCServerRequest *request,
224                                            OCEntityHandlerResult result);
225
226 /**
227  * Add resource type name to payload for GET request.
228  * @param[in]   payload     Pointer to the payload to which byte string needs to be added.
229  * @return  ::OC_STACK_OK or Appropriate error code.
230  */
231 static OCStackResult AddResourceTypeNameToPayload(OCRepPayload *payload);
232
233 /**
234  * Add resource interface name to payload for GET request.
235  * @param[in]   payload     Pointer to the payload to which byte string needs to be added.
236  * @return  ::OC_STACK_OK or Appropriate error code.
237  */
238 static OCStackResult AddResourceInterfaceNameToPayload(OCRepPayload *payload);
239
240 OCStackResult InitializeKeepAlive(OCMode mode)
241 {
242     OIC_LOG(DEBUG, TAG, "InitializeKeepAlive IN");
243     if (g_isKeepAliveInitialized)
244     {
245         OIC_LOG(DEBUG, TAG, "KeepAlive already initialized");
246         return OC_STACK_OK;
247     }
248
249     if (OC_CLIENT != mode)
250     {
251         // Create the KeepAlive Resource[/oic/ping].
252         OCStackResult result = CreateKeepAliveResource();
253         if (OC_STACK_OK != result)
254         {
255             OIC_LOG_V(ERROR, TAG, "CreateKeepAliveResource failed[%d]", result);
256             return result;
257         }
258     }
259
260     if (!g_keepAliveConnectionTable)
261     {
262         g_keepAliveConnectionTable = u_arraylist_create();
263         if (NULL == g_keepAliveConnectionTable)
264         {
265             OIC_LOG(ERROR, TAG, "Creating KeepAlive Table failed");
266             TerminateKeepAlive(mode);
267             return OC_STACK_ERROR;
268         }
269     }
270
271     g_isKeepAliveInitialized = true;
272
273     OIC_LOG(DEBUG, TAG, "InitializeKeepAlive OUT");
274     return OC_STACK_OK;
275 }
276
277 OCStackResult TerminateKeepAlive(OCMode mode)
278 {
279     OIC_LOG(DEBUG, TAG, "TerminateKeepAlive IN");
280     if (!g_isKeepAliveInitialized)
281     {
282         OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
283         return OC_STACK_ERROR;
284     }
285
286     if (OC_CLIENT != mode)
287     {
288         // Delete the KeepAlive Resource[/oic/ping].
289         OCStackResult result = DeleteKeepAliveResource();
290         if (OC_STACK_OK != result)
291         {
292             OIC_LOG_V(ERROR, TAG, "DeleteKeepAliveResource failed[%d]", result);
293             return result;
294         }
295     }
296
297     if (NULL != g_keepAliveConnectionTable)
298     {
299         u_arraylist_destroy(g_keepAliveConnectionTable);
300         g_keepAliveConnectionTable = NULL;
301     }
302
303     g_isKeepAliveInitialized = false;
304
305     OIC_LOG(DEBUG, TAG, "TerminateKeepAlive OUT");
306     return OC_STACK_OK;
307 }
308
309 OCStackResult CreateKeepAliveResource(void)
310 {
311     OIC_LOG(DEBUG, TAG, "InitKeepAliveResource IN");
312
313     // Create a KeepAlive resource
314     OCStackResult result = OCCreateResource(&g_keepAliveHandle,
315                                             KEEPALIVE_RESOURCE_TYPE_NAME,
316                                             OC_RSRVD_INTERFACE_DEFAULT,
317                                             KEEPALIVE_RESOURCE_URI,
318                                             NULL,
319                                             NULL,
320                                             OC_RES_PROP_NONE);
321     if (OC_STACK_OK == result)
322     {
323         result = BindResourceInterfaceToResource((OCResource *) g_keepAliveHandle,
324                                                  KEEPALIVE_RESOURCE_INTF_NAME);
325     }
326
327     if (OC_STACK_OK != result)
328     {
329         OIC_LOG_V(ERROR, TAG, "Create resource for KeepAlive failed[%d]", result);
330     }
331
332     OIC_LOG(DEBUG, TAG, "InitKeepAliveResource OUT");
333     return result;
334 }
335
336 OCStackResult DeleteKeepAliveResource(void)
337 {
338     OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource IN");
339
340     // Create a KeepAlive resource
341     OCStackResult result = OCDeleteResource(g_keepAliveHandle);
342
343     if (OC_STACK_OK != result)
344     {
345         OIC_LOG_V(ERROR, TAG, "Delete resource for KeepAlive failed[%d]", result);
346     }
347
348     OIC_LOG(DEBUG, TAG, "DeleteKeepAliveResource OUT");
349     return result;
350 }
351
352 OCStackResult HandleKeepAliveRequest(OCServerRequest *request,
353                                      const OCResource *resource)
354 {
355     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
356     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
357
358     OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest IN");
359
360     OCEntityHandlerResult result = OC_EH_ERROR;
361     if (OC_REST_GET == request->method)
362     {
363         switch ((OCObserveAction)request->observationOption)
364         {
365             case OC_OBSERVE_NO_OPTION:
366             case OC_OBSERVE_REGISTER:
367             case OC_OBSERVE_DEREGISTER:
368                 OIC_LOG(DEBUG, TAG, "Received GET request");
369                 result = HandleKeepAliveGETRequest(request, resource);
370                 break;
371             default:
372                 OIC_LOG(DEBUG, TAG, "Not Supported by KeepAlive");
373                 result = OC_EH_UNAUTHORIZED_REQ;
374         }
375     }
376     else if (OC_REST_PUT == request->method || OC_REST_POST == request->method)
377     {
378         OIC_LOG(DEBUG, TAG, "Received PUT/POST request");
379         result = HandleKeepAlivePOSTRequest(request, resource);
380     }
381     else
382     {
383         OIC_LOG(DEBUG, TAG, "Not Supported by KeepAlive");
384         result = OC_EH_UNAUTHORIZED_REQ;
385     }
386
387     OCStackResult ret = SendKeepAliveResponse(request, result);
388     if (OC_STACK_OK != ret)
389     {
390         OIC_LOG_V(ERROR, TAG, "SendKeepAliveResponse failed with result %u", ret);
391     }
392
393     OIC_LOG(DEBUG, TAG, "HandleKeepAliveRequest OUT");
394     return ret;
395 }
396
397 OCStackResult SendKeepAliveResponse(OCServerRequest *request,
398                                     OCEntityHandlerResult result)
399 {
400     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
401
402     OIC_LOG_V(DEBUG, TAG, "Send KeepAlive response with entity result[%d]", result);
403
404     // Convert OCDevAddr to CAEndpoint_t.
405     CAEndpoint_t endpoint = {.adapter = CA_DEFAULT_ADAPTER};
406     CopyDevAddrToEndpoint(&request->devAddr, &endpoint);
407
408     size_t index = 0;
409     KeepAliveEntry_t *entry = GetEntryFromEndpoint(&endpoint, &index);
410     int64_t interval = (entry) ? entry->interval : 0;
411
412     // Create KeepAlive payload to send response message.
413     OCRepPayload *payload = CreateKeepAlivePayload(interval);
414
415     // Add resource type/interface name to payload for GET request.
416     if (OC_REST_GET == request->method && OC_EH_OK == result)
417     {
418         AddResourceTypeNameToPayload(payload);
419         AddResourceInterfaceNameToPayload(payload);
420     }
421
422     OCEntityHandlerResponse ehResponse = { .ehResult = result, 
423                                            .payload = (OCPayload*) payload, 
424                                            .requestHandle = request };
425     OICStrcpy(ehResponse.resourceUri, sizeof(ehResponse.resourceUri), KEEPALIVE_RESOURCE_URI);
426
427     // Send response message.
428     return OCDoResponse(&ehResponse);
429 }
430
431 OCEntityHandlerResult HandleKeepAliveGETRequest(OCServerRequest *request,
432                                                 const OCResource *resource)
433 {
434     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
435     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
436
437     OIC_LOG_V(DEBUG, TAG, "Find Ping resource [%s]", request->resourceUrl);
438
439     OCResource *resourcePtr = FindResourceByUri(request->resourceUrl);
440     if (!resourcePtr)
441     {
442         // Resource URL not specified
443         OIC_LOG_V(DEBUG, TAG, "There is no Ping resource [%s]", request->resourceUrl);
444         return OC_EH_RESOURCE_NOT_FOUND;
445     }
446
447     return OC_EH_OK;
448 }
449
450 OCEntityHandlerResult HandleKeepAlivePOSTRequest(OCServerRequest *request,
451                                                  const OCResource *resource)
452 {
453     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
454     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
455
456     // Get entry from KeepAlive table.
457     CAEndpoint_t endpoint = { .adapter = CA_DEFAULT_ADAPTER };
458     CopyDevAddrToEndpoint(&request->devAddr, &endpoint);
459
460     size_t index = 0;
461     KeepAliveEntry_t *entry = GetEntryFromEndpoint(&endpoint, &index);
462     if (!entry)
463     {
464         OIC_LOG(ERROR, TAG, "Received the first keepalive message from client");
465         entry = AddKeepAliveEntry(&endpoint, OC_SERVER, NULL);
466         if (!entry)
467         {
468             OIC_LOG(ERROR, TAG, "Failed to add new keepalive entry");
469             return OC_EH_INTERNAL_SERVER_ERROR;
470         }
471     }
472
473     OCPayload *ocPayload = NULL;
474     OCParsePayload(&ocPayload, request->payloadFormat, PAYLOAD_TYPE_REPRESENTATION,
475                    request->payload, request->payloadSize);
476     OCRepPayload *repPayload = (OCRepPayload *)ocPayload;
477
478     int64_t interval = 0;
479     OCRepPayloadGetPropInt(repPayload, INTERVAL, &interval);
480     entry->interval = interval;
481     OIC_LOG_V(DEBUG, TAG, "Received interval is [%" PRId64 "]", entry->interval);
482     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
483
484     OCPayloadDestroy(ocPayload);
485
486     return OC_EH_OK;
487 }
488
489 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
490                                       OCStackResult responseCode,
491                                       const OCRepPayload *respPayload)
492 {
493     VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
494
495     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse IN");
496
497     // Get entry from KeepAlive table.
498     size_t index = 0;
499     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index);
500     if (!entry)
501     {
502         // Receive response message about find /oic/ping request.
503         OIC_LOG(ERROR, TAG, "There is no connection info in KeepAlive table");
504
505         if (OC_STACK_NO_RESOURCE == responseCode)
506         {
507             OIC_LOG(ERROR, TAG, "Server doesn't have a ping resource");
508             return OC_STACK_ERROR;
509         }
510         else if (OC_STACK_OK == responseCode)
511         {
512             int64_t *recvInterval = NULL;
513             size_t dimensions[MAX_REP_ARRAY_DEPTH] = { 0 };
514             OCRepPayloadGetIntArray(respPayload, INTERVAL_ARRAY, &recvInterval, dimensions);
515             size_t serverIntervalSize = calcDimTotal(dimensions);
516
517             entry = AddKeepAliveEntry(endPoint, OC_CLIENT, recvInterval);
518             if (!entry)
519             {
520                 OIC_LOG(ERROR, TAG, "Failed to add new KeepAlive entry");
521                 return OC_STACK_ERROR;
522             }
523
524             if (serverIntervalSize)
525             {
526                 // update interval size with received size of server.
527                 entry->intervalSize = serverIntervalSize;
528             }
529
530             // Send first ping message
531             return SendPingMessage(entry);
532         }
533     }
534     else
535     {
536         // Set sentPingMsg values with false.
537         entry->sentPingMsg = false;
538
539         // Check the received interval value.
540         int64_t interval = 0;
541         OCRepPayloadGetPropInt(respPayload, INTERVAL, &interval);
542         OIC_LOG_V(DEBUG, TAG, "Received interval is [%" PRId64 "]", entry->interval);
543     }
544
545     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse OUT");
546     return OC_STACK_OK;
547 }
548
549 void ProcessKeepAlive()
550 {
551     if (!g_isKeepAliveInitialized)
552     {
553         OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
554         return;
555     }
556
557     size_t len = u_arraylist_length(g_keepAliveConnectionTable);
558
559     for (size_t i = 0; i < len; i++)
560     {
561         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
562                                                                       i);
563         if (NULL == entry)
564         {
565             continue;
566         }
567
568         uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
569         if (OC_CLIENT == entry->mode)
570         {
571             if (entry->sentPingMsg)
572             {
573                 /*
574                  * If an OIC Client does not receive the response within 1 minutes,
575                  * terminate the connection.
576                  * In this case the timeStamp means last time sent ping message.
577                  */
578                 if ((KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) <= currentTime - entry->timeStamp)
579                 {
580                     OIC_LOG(DEBUG, TAG, "Client does not receive the response within 1 minutes.");
581
582                     // Send message to disconnect session.
583                     SendDisconnectMessage(entry);
584                 }
585             }
586             else
587             {
588                 if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
589                         <= currentTime - entry->timeStamp)
590                 {
591                     // Increase interval value.
592                     IncreaseInterval(entry);
593
594                     OCStackResult result = SendPingMessage(entry);
595                     if (OC_STACK_OK != result)
596                     {
597                         OIC_LOG(ERROR, TAG, "Failed to send ping request");
598                         continue;
599                     }
600                 }
601             }
602         }
603         else if (OC_SERVER == entry->mode)
604         {
605             /*
606              * If an OIC Server does not receive a PUT request to ping resource
607              * within the specified interval time, terminate the connection.
608              * In this case the timeStamp means last time received ping message.
609              */
610             if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
611                     <= currentTime - entry->timeStamp)
612             {
613                 OIC_LOG(DEBUG, TAG, "Server does not receive a PUT request.");
614                 SendDisconnectMessage(entry);
615             }
616         }
617     }
618 }
619
620 void IncreaseInterval(KeepAliveEntry_t *entry)
621 {
622     VERIFY_NON_NULL_NR(entry, FATAL);
623
624     OIC_LOG_V(DEBUG, TAG, "Total interval counts: %" PRIuPTR, entry->intervalSize);
625     if (entry->intervalSize > (size_t)entry->currIndex + 1)
626     {
627         entry->currIndex++;
628         entry->interval = entry->intervalInfo[entry->currIndex];
629         OIC_LOG_V(DEBUG, TAG, "increase interval value [%" PRId64 "]", entry->interval);
630     }
631 }
632
633 OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry)
634 {
635     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
636
637     /*
638      * Send empty message to disconnect a connection.
639      * If CA get the empty message from RI, CA will disconnect a connection.
640      */
641
642     OCStackResult result = RemoveKeepAliveEntry(&entry->remoteAddr);
643     if (result != OC_STACK_OK)
644     {
645         return result;
646     }
647
648     CARequestInfo_t requestInfo = { .method = CA_POST };
649     result = CASendRequest(&entry->remoteAddr, &requestInfo);
650     return CAResultToOCResult(result);
651 }
652
653 OCStackResult SendPingMessage(KeepAliveEntry_t *entry)
654 {
655     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
656
657     // Send ping message.
658     OCCallbackData pingData = { .context = NULL, .cb = PingRequestCallback };
659     OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
660     CopyEndpointToDevAddr(&(entry->remoteAddr), &devAddr);
661
662     OCRepPayload *payload = CreateKeepAlivePayload(entry->interval);
663     OCStackResult result = OCDoResource(NULL, OC_REST_POST, KEEPALIVE_RESOURCE_URI, &devAddr,
664                                         (OCPayload *) payload, CT_ADAPTER_TCP, OC_LOW_QOS,
665                                         &pingData, NULL, 0);
666     if (OC_STACK_OK != result)
667     {
668         OIC_LOG(ERROR, TAG, "OCDoResource has failed");
669         return result;
670     }
671
672     // Update timeStamp with time sent ping message for next ping message.
673     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
674     entry->sentPingMsg = true;
675
676     OIC_LOG_V(DEBUG, TAG, "Client sent ping message, interval [%" PRId64 "]", entry->interval);
677
678     return OC_STACK_OK;
679 }
680
681 OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
682                                              OCClientResponse *clientResponse)
683 {
684     (void) ctx;
685     (void) handle;
686     if (NULL == clientResponse)
687     {
688         OIC_LOG(ERROR, TAG, "clientResponse is NULL");
689         return OC_STACK_DELETE_TRANSACTION;
690     }
691
692     CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP };
693     CopyDevAddrToEndpoint(&(clientResponse->devAddr), &endpoint);
694
695     HandleKeepAliveResponse(&endpoint, clientResponse->result,
696                             (OCRepPayload *)clientResponse->payload);
697
698     return OC_STACK_DELETE_TRANSACTION;
699 }
700
701 KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, size_t *index)
702 {
703     if (!g_keepAliveConnectionTable)
704     {
705         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
706         return NULL;
707     }
708
709     size_t len = u_arraylist_length(g_keepAliveConnectionTable);
710
711     for (size_t i = 0; i < len; i++)
712     {
713         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
714                                                                       i);
715         if (NULL == entry)
716         {
717             continue;
718         }
719
720         if (!strncmp(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr))
721                 && (entry->remoteAddr.port == endpoint->port))
722         {
723             OIC_LOG(DEBUG, TAG, "Connection Info found in KeepAlive table");
724             *index = i;
725             return entry;
726         }
727     }
728
729     return NULL;
730 }
731
732 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode,
733                                     int64_t *intervalInfo)
734 {
735     if (!endpoint)
736     {
737         OIC_LOG(ERROR, TAG, "endpoint is NULL");
738         return NULL;
739     }
740
741     if (!g_keepAliveConnectionTable)
742     {
743         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
744         return NULL;
745     }
746
747     KeepAliveEntry_t *entry = (KeepAliveEntry_t *) OICCalloc(1, sizeof(KeepAliveEntry_t));
748     if (NULL == entry)
749     {
750         OIC_LOG(ERROR, TAG, "Failed to Calloc KeepAlive Entry");
751         return NULL;
752     }
753
754     entry->mode = mode;
755     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
756     entry->remoteAddr = *endpoint;
757     entry->intervalSize = DEFAULT_INTERVAL_COUNT;
758     entry->intervalInfo = intervalInfo;
759     if (!entry->intervalInfo)
760     {
761         entry->intervalInfo = (int64_t*) OICMalloc(entry->intervalSize * sizeof(int64_t));
762         for (size_t i = 0; i < entry->intervalSize; i++)
763         {
764             entry->intervalInfo[i] = KEEPALIVE_MIN_INTERVAL << i;
765         }
766     }
767     entry->interval = entry->intervalInfo[0];
768
769     bool result = u_arraylist_add(g_keepAliveConnectionTable, (void *)entry);
770     if (!result)
771     {
772         OIC_LOG(ERROR, TAG, "Adding node to head failed");
773         OICFree(entry->intervalInfo);
774         OICFree(entry);
775         return NULL;
776     }
777
778     return entry;
779 }
780
781 OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint)
782 {
783     VERIFY_NON_NULL(endpoint, FATAL, OC_STACK_INVALID_PARAM);
784
785     size_t index = 0;
786     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endpoint, &index);
787     if (!entry)
788     {
789         OIC_LOG(ERROR, TAG, "There is no entry in keepalive table.");
790         return OC_STACK_ERROR;
791     }
792
793     KeepAliveEntry_t *removedEntry = (KeepAliveEntry_t *)
794                                         u_arraylist_remove(g_keepAliveConnectionTable, index);
795     if (NULL == removedEntry)
796     {
797         OIC_LOG(ERROR, TAG, "Removed Entry is NULL");
798         return OC_STACK_ERROR;
799     }
800
801     OIC_LOG_V(DEBUG, TAG, "Remove Connection Info from KeepAlive table, "
802              "remote addr=%s port:%d", removedEntry->remoteAddr.addr,
803              removedEntry->remoteAddr.port);
804
805     OICFree(entry->intervalInfo);
806     OICFree(removedEntry);
807
808     return OC_STACK_OK;
809 }
810
811 void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint, bool isConnected, bool isClient)
812 {
813     VERIFY_NON_NULL_NR(endpoint, FATAL);
814
815     if (isConnected)
816     {
817         OIC_LOG(DEBUG, TAG, "Received the connected device information from CA");
818
819         if (isClient)
820         {
821             // Send discover message to find ping resource
822             OCCallbackData pingData = {.context = NULL, .cb = PingRequestCallback };
823             OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
824             CopyEndpointToDevAddr(endpoint, &devAddr);
825
826             OCStackResult result = OCDoResource(NULL, OC_REST_DISCOVER, KEEPALIVE_RESOURCE_URI,
827                                                 &devAddr, NULL, CT_ADAPTER_TCP, OC_HIGH_QOS,
828                                                 &pingData, NULL, 0);
829             if (OC_STACK_OK != result)
830             {
831                 OIC_LOG(ERROR, TAG, "OCDoResource has failed");
832                 return;
833             }
834         }
835     }
836     else
837     {
838         OIC_LOG(DEBUG, TAG, "Received the disconnected device information from CA");
839
840         OCStackResult result = RemoveKeepAliveEntry(endpoint);
841         if(result != OC_STACK_OK)
842         {
843             return;
844         }
845     }
846 }
847
848 OCRepPayload *CreateKeepAlivePayload(int64_t interval)
849 {
850     OIC_LOG_V(DEBUG, TAG, "Create KeepAlive Payload, interval is [%" PRId64 "]", interval);
851
852     OCRepPayload *payload = OCRepPayloadCreate();
853     if (!payload)
854     {
855         OIC_LOG(ERROR, TAG, "Failed to allocate Payload");
856         return NULL;
857     }
858     payload->base.type = PAYLOAD_TYPE_REPRESENTATION;
859     OCRepPayloadSetPropInt(payload, INTERVAL, interval);
860
861     return payload;
862 }
863
864 OCStackResult AddResourceTypeNameToPayload(OCRepPayload *payload)
865 {
866     uint8_t numElement = 0;
867     OCStackResult res = OCGetNumberOfResourceTypes(g_keepAliveHandle, &numElement);
868     if (OC_STACK_OK == res)
869     {
870         size_t rtDim[MAX_REP_ARRAY_DEPTH] = {numElement, 0, 0};
871         char **rt = (char **)OICMalloc(sizeof(char *) * numElement);
872         if (!rt)
873         {
874             OIC_LOG(ERROR, TAG, "Could not allocate memory for rf");
875             return OC_STACK_NO_MEMORY;
876         }
877         for (uint8_t i = 0; i < numElement; ++i)
878         {
879             const char *value = OCGetResourceTypeName(g_keepAliveHandle, i);
880             OIC_LOG_V(DEBUG, TAG, "value: %s", value);
881             rt[i] = OICStrdup(value);
882             if (NULL == rt[i])
883             {
884                 OIC_LOG_V(ERROR, TAG, "Creating duplicate string for rt failed!");
885                 for (uint8_t j = 0; j < i; ++j)
886                 {
887                     OICFree(rt[j]);
888                 }
889                 OICFree(rt);
890                 return OC_STACK_NO_MEMORY;
891             }
892         }
893         OCRepPayloadSetStringArray(payload, OC_RSRVD_RESOURCE_TYPE, (const char **) rt, rtDim);
894         for (uint8_t i = 0; i < numElement; ++i)
895         {
896             OICFree(rt[i]);
897         }
898         OICFree(rt);
899     }
900
901     return res;
902 }
903
904 OCStackResult AddResourceInterfaceNameToPayload(OCRepPayload *payload)
905 {
906     uint8_t numElement = 0;
907     OCStackResult res = OCGetNumberOfResourceInterfaces(g_keepAliveHandle, &numElement);
908     if (OC_STACK_OK == res)
909     {
910         size_t ifDim[MAX_REP_ARRAY_DEPTH] = {numElement, 0, 0};
911         char **itf = (char **)OICMalloc(sizeof(char *) * numElement);
912         if (!itf)
913         {
914             OIC_LOG(ERROR, TAG, "Could not allocate memory for itf");
915             return OC_STACK_NO_MEMORY;
916         }
917
918         for (uint8_t i = 0; i < numElement; ++i)
919         {
920             const char *value = OCGetResourceInterfaceName(g_keepAliveHandle, i);
921             OIC_LOG_V(DEBUG, TAG, "value: %s", value);
922             itf[i] = OICStrdup(value);
923             if (NULL == itf[i])
924             {
925                 OIC_LOG_V(ERROR, TAG, "Creating duplicate string for itf failed!");
926                 for (uint8_t j = 0; j < i; ++j)
927                 {
928                     OICFree(itf[j]);
929                 }
930                 OICFree(itf);
931                 return OC_STACK_NO_MEMORY;
932             }
933         }
934         OCRepPayloadSetStringArray(payload, OC_RSRVD_INTERFACE, (const char **) itf, ifDim);
935         for (uint8_t i = 0; i < numElement; ++i)
936         {
937             OICFree(itf[i]);
938         }
939         OICFree(itf);
940     }
941
942     return res;
943 }