52dae8907823d123c6f15d941afa42e8d6d4e988
[platform/upstream/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 "ocrandom.h"
29 #include "uarraylist.h"
30 #include "ocstackinternal.h"
31 #include "ocpayloadcbor.h"
32 #include "ocpayload.h"
33 #include "ocresourcehandler.h"
34 #include "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();
146
147 /**
148  * This function deletes KeepAlive resource.
149  * @return  ::OC_STACK_OK or Appropriate error code.
150  */
151 static OCStackResult DeleteKeepAliveResource();
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 PUT 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, uint32_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()
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()
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     uint32_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->requestId,
425                                            .resourceHandle = g_keepAliveHandle };
426     OICStrcpy(ehResponse.resourceUri, sizeof(ehResponse.resourceUri), KEEPALIVE_RESOURCE_URI);
427
428     // Send response message.
429     return OCDoResponse(&ehResponse);
430 }
431
432 OCEntityHandlerResult HandleKeepAliveGETRequest(OCServerRequest *request,
433                                                 const OCResource *resource)
434 {
435     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
436     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
437
438     OIC_LOG_V(DEBUG, TAG, "Find Ping resource [%s]", request->resourceUrl);
439
440     OCResource *resourcePtr = FindResourceByUri(request->resourceUrl);
441     if (!resourcePtr)
442     {
443         // Resource URL not specified
444         OIC_LOG_V(DEBUG, TAG, "There is no Ping resource [%s]", request->resourceUrl);
445         return OC_EH_RESOURCE_NOT_FOUND;
446     }
447
448     return OC_EH_OK;
449 }
450
451 OCEntityHandlerResult HandleKeepAlivePOSTRequest(OCServerRequest *request,
452                                                  const OCResource *resource)
453 {
454     VERIFY_NON_NULL(request, FATAL, OC_STACK_INVALID_PARAM);
455     VERIFY_NON_NULL(resource, FATAL, OC_STACK_INVALID_PARAM);
456
457     // Get entry from KeepAlive table.
458     CAEndpoint_t endpoint = { .adapter = CA_DEFAULT_ADAPTER };
459     CopyDevAddrToEndpoint(&request->devAddr, &endpoint);
460
461     uint32_t index = 0;
462     KeepAliveEntry_t *entry = GetEntryFromEndpoint(&endpoint, &index);
463     if (!entry)
464     {
465         OIC_LOG(ERROR, TAG, "Received the first keepalive message from client");
466         entry = AddKeepAliveEntry(&endpoint, OC_SERVER, NULL);
467         if (!entry)
468         {
469             OIC_LOG(ERROR, TAG, "Failed to add new keepalive entry");
470             return OC_EH_INTERNAL_SERVER_ERROR;
471         }
472     }
473
474     OCPayload *ocPayload = NULL;
475     OCParsePayload(&ocPayload, PAYLOAD_TYPE_REPRESENTATION,
476                    request->payload, request->payloadSize);
477     OCRepPayload *repPayload = (OCRepPayload *)ocPayload;
478
479     int64_t interval = 0;
480     OCRepPayloadGetPropInt(repPayload, INTERVAL, &interval);
481     entry->interval = interval;
482     OIC_LOG_V(DEBUG, TAG, "Received interval is [%" PRId64 "]", entry->interval);
483     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
484
485     OCPayloadDestroy(ocPayload);
486
487     return OC_EH_OK;
488 }
489
490 OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint,
491                                       OCStackResult responseCode,
492                                       const OCRepPayload *respPayload)
493 {
494     VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM);
495
496     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse IN");
497
498     // Get entry from KeepAlive table.
499     uint32_t index = 0;
500     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index);
501     if (!entry)
502     {
503         // Receive response message about find /oic/ping request.
504         OIC_LOG(ERROR, TAG, "There is no connection info in KeepAlive table");
505
506         if (OC_STACK_NO_RESOURCE == responseCode)
507         {
508             OIC_LOG(ERROR, TAG, "Server doesn't have a ping resource");
509             return OC_STACK_ERROR;
510         }
511         else if (OC_STACK_OK == responseCode)
512         {
513             int64_t *recvInterval = NULL;
514             size_t dimensions[MAX_REP_ARRAY_DEPTH] = { 0 };
515             OCRepPayloadGetIntArray(respPayload, INTERVAL_ARRAY, &recvInterval, dimensions);
516             size_t serverIntervalSize = calcDimTotal(dimensions);
517
518             entry = AddKeepAliveEntry(endPoint, OC_CLIENT, recvInterval);
519             if (!entry)
520             {
521                 OIC_LOG(ERROR, TAG, "Failed to add new KeepAlive entry");
522                 return OC_STACK_ERROR;
523             }
524
525             if (serverIntervalSize)
526             {
527                 // update interval size with received size of server.
528                 entry->intervalSize = serverIntervalSize;
529             }
530
531             // Send first ping message
532             return SendPingMessage(entry);
533         }
534     }
535     else
536     {
537         // Set sentPingMsg values with false.
538         entry->sentPingMsg = false;
539
540         // Check the received interval value.
541         int64_t interval = 0;
542         OCRepPayloadGetPropInt(respPayload, INTERVAL, &interval);
543         OIC_LOG_V(DEBUG, TAG, "Received interval is [%" PRId64 "]", entry->interval);
544     }
545
546     OIC_LOG(DEBUG, TAG, "HandleKeepAliveResponse OUT");
547     return OC_STACK_OK;
548 }
549
550 void ProcessKeepAlive()
551 {
552     if (!g_isKeepAliveInitialized)
553     {
554         OIC_LOG(ERROR, TAG, "KeepAlive not initialized");
555         return;
556     }
557
558     uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
559
560     for (uint32_t i = 0; i < len; i++)
561     {
562         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
563                                                                       i);
564         if (NULL == entry)
565         {
566             continue;
567         }
568
569         uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
570         if (OC_CLIENT == entry->mode)
571         {
572             if (entry->sentPingMsg)
573             {
574                 /*
575                  * If an OIC Client does not receive the response within 1 minutes,
576                  * terminate the connection.
577                  * In this case the timeStamp means last time sent ping message.
578                  */
579                 if ((KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) <= currentTime - entry->timeStamp)
580                 {
581                     OIC_LOG(DEBUG, TAG, "Client does not receive the response within 1 minutes.");
582
583                     // Send message to disconnect session.
584                     SendDisconnectMessage(entry);
585                 }
586             }
587             else
588             {
589                 if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
590                         <= currentTime - entry->timeStamp)
591                 {
592                     // Increase interval value.
593                     IncreaseInterval(entry);
594
595                     OCStackResult result = SendPingMessage(entry);
596                     if (OC_STACK_OK != result)
597                     {
598                         OIC_LOG(ERROR, TAG, "Failed to send ping request");
599                         continue;
600                     }
601                 }
602             }
603         }
604         else if (OC_SERVER == entry->mode)
605         {
606             /*
607              * If an OIC Server does not receive a PUT request to ping resource
608              * within the specified interval time, terminate the connection.
609              * In this case the timeStamp means last time received ping message.
610              */
611             if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC)
612                     <= currentTime - entry->timeStamp)
613             {
614                 OIC_LOG(DEBUG, TAG, "Server does not receive a PUT request.");
615                 SendDisconnectMessage(entry);
616             }
617         }
618     }
619 }
620
621 void IncreaseInterval(KeepAliveEntry_t *entry)
622 {
623     VERIFY_NON_NULL_NR(entry, FATAL);
624
625     OIC_LOG_V(DEBUG, TAG, "Total interval counts: %zu", entry->intervalSize);
626     if (entry->intervalSize > (size_t)entry->currIndex + 1)
627     {
628         entry->currIndex++;
629         entry->interval = entry->intervalInfo[entry->currIndex];
630         OIC_LOG_V(DEBUG, TAG, "increase interval value [%" PRId64 "]", entry->interval);
631     }
632 }
633
634 OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry)
635 {
636     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
637
638     /*
639      * Send empty message to disconnect a connection.
640      * If CA get the empty message from RI, CA will disconnect a connection.
641      */
642
643     OCStackResult result = RemoveKeepAliveEntry(&entry->remoteAddr);
644     if (result != OC_STACK_OK)
645     {
646         return result;
647     }
648
649     CARequestInfo_t requestInfo = { .method = CA_POST };
650     result = CASendRequest(&entry->remoteAddr, &requestInfo);
651     return CAResultToOCResult(result);
652 }
653
654 OCStackResult SendPingMessage(KeepAliveEntry_t *entry)
655 {
656     VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM);
657
658     // Send ping message.
659     OCCallbackData pingData = { .context = NULL, .cb = PingRequestCallback };
660     OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
661     CopyEndpointToDevAddr(&(entry->remoteAddr), &devAddr);
662
663     OCRepPayload *payload = CreateKeepAlivePayload(entry->interval);
664     OCStackResult result = OCDoResource(NULL, OC_REST_POST, KEEPALIVE_RESOURCE_URI, &devAddr,
665                                         (OCPayload *) payload, CT_ADAPTER_TCP, OC_LOW_QOS,
666                                         &pingData, NULL, 0);
667     if (OC_STACK_OK != result)
668     {
669         OIC_LOG(ERROR, TAG, "OCDoResource has failed");
670         return result;
671     }
672
673     // Update timeStamp with time sent ping message for next ping message.
674     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
675     entry->sentPingMsg = true;
676
677     OIC_LOG_V(DEBUG, TAG, "Client sent ping message, interval [%" PRId64 "]", entry->interval);
678
679     return OC_STACK_OK;
680 }
681
682 OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle,
683                                              OCClientResponse *clientResponse)
684 {
685     (void) ctx;
686     (void) handle;
687     if (NULL == clientResponse)
688     {
689         OIC_LOG(ERROR, TAG, "clientResponse is NULL");
690         return OC_STACK_DELETE_TRANSACTION;
691     }
692
693     CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP };
694     CopyDevAddrToEndpoint(&(clientResponse->devAddr), &endpoint);
695
696     HandleKeepAliveResponse(&endpoint, clientResponse->result,
697                             (OCRepPayload *)clientResponse->payload);
698
699     return OC_STACK_DELETE_TRANSACTION;
700 }
701
702 KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index)
703 {
704     if (!g_keepAliveConnectionTable)
705     {
706         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
707         return NULL;
708     }
709
710     uint32_t len = u_arraylist_length(g_keepAliveConnectionTable);
711
712     for (uint32_t i = 0; i < len; i++)
713     {
714         KeepAliveEntry_t *entry = (KeepAliveEntry_t *)u_arraylist_get(g_keepAliveConnectionTable,
715                                                                       i);
716         if (NULL == entry)
717         {
718             continue;
719         }
720
721         if (!strncmp(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr))
722                 && (entry->remoteAddr.port == endpoint->port))
723         {
724             OIC_LOG(DEBUG, TAG, "Connection Info found in KeepAlive table");
725             *index = i;
726             return entry;
727         }
728     }
729
730     return NULL;
731 }
732
733 KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode,
734                                     int64_t *intervalInfo)
735 {
736     if (!endpoint)
737     {
738         OIC_LOG(ERROR, TAG, "endpoint is NULL");
739         return NULL;
740     }
741
742     if (!g_keepAliveConnectionTable)
743     {
744         OIC_LOG(ERROR, TAG, "KeepAlive Table was not Created.");
745         return NULL;
746     }
747
748     KeepAliveEntry_t *entry = (KeepAliveEntry_t *) OICCalloc(1, sizeof(KeepAliveEntry_t));
749     if (NULL == entry)
750     {
751         OIC_LOG(ERROR, TAG, "Failed to Calloc KeepAlive Entry");
752         return NULL;
753     }
754
755     entry->mode = mode;
756     entry->timeStamp = OICGetCurrentTime(TIME_IN_US);
757     entry->remoteAddr.adapter = endpoint->adapter;
758     entry->remoteAddr.flags = endpoint->flags;
759     entry->remoteAddr.ifindex = endpoint->ifindex;
760     entry->remoteAddr.port = endpoint->port;
761     strncpy(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr));
762
763     entry->intervalSize = DEFAULT_INTERVAL_COUNT;
764     entry->intervalInfo = intervalInfo;
765     if (!entry->intervalInfo)
766     {
767         entry->intervalInfo = (int64_t*) OICMalloc(entry->intervalSize * sizeof(int64_t));
768         for (size_t i = 0; i < entry->intervalSize; i++)
769         {
770             entry->intervalInfo[i] = KEEPALIVE_MIN_INTERVAL << i;
771         }
772     }
773     entry->interval = entry->intervalInfo[0];
774
775     bool result = u_arraylist_add(g_keepAliveConnectionTable, (void *)entry);
776     if (!result)
777     {
778         OIC_LOG(ERROR, TAG, "Adding node to head failed");
779         OICFree(entry->intervalInfo);
780         OICFree(entry);
781         return NULL;
782     }
783
784     return entry;
785 }
786
787 OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint)
788 {
789     VERIFY_NON_NULL(endpoint, FATAL, OC_STACK_INVALID_PARAM);
790
791     uint32_t index = 0;
792     KeepAliveEntry_t *entry = GetEntryFromEndpoint(endpoint, &index);
793     if (!entry)
794     {
795         OIC_LOG(ERROR, TAG, "There is no entry in keepalive table.");
796         return OC_STACK_ERROR;
797     }
798
799     KeepAliveEntry_t *removedEntry = (KeepAliveEntry_t *)
800                                         u_arraylist_remove(g_keepAliveConnectionTable, index);
801     if (NULL == removedEntry)
802     {
803         OIC_LOG(ERROR, TAG, "Removed Entry is NULL");
804         return OC_STACK_ERROR;
805     }
806
807     OIC_LOG_V(DEBUG, TAG, "Remove Connection Info from KeepAlive table, "
808              "remote addr=%s port:%d", removedEntry->remoteAddr.addr,
809              removedEntry->remoteAddr.port);
810
811     OICFree(entry->intervalInfo);
812     OICFree(removedEntry);
813
814     return OC_STACK_OK;
815 }
816
817 void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint, bool isConnected)
818 {
819     VERIFY_NON_NULL_NR(endpoint, FATAL);
820
821     if (isConnected)
822     {
823         OIC_LOG(DEBUG, TAG, "Received the connected device information from CA");
824
825         // Send discover message to find ping resource
826         OCCallbackData pingData = {.context = NULL, .cb = PingRequestCallback };
827         OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP };
828         CopyEndpointToDevAddr(endpoint, &devAddr);
829
830         OCStackResult result = OCDoResource(NULL, OC_REST_DISCOVER, KEEPALIVE_RESOURCE_URI,
831                                             &devAddr, NULL, CT_ADAPTER_TCP, OC_HIGH_QOS,
832                                             &pingData, NULL, 0);
833         if (OC_STACK_OK != result)
834         {
835             OIC_LOG(ERROR, TAG, "OCDoResource has failed");
836             return;
837         }
838     }
839     else
840     {
841         OIC_LOG(DEBUG, TAG, "Received the disconnected device information from CA");
842
843         OCStackResult result = RemoveKeepAliveEntry(endpoint);
844         if(result != OC_STACK_OK)
845         {
846             return;
847         }
848     }
849 }
850
851 OCRepPayload *CreateKeepAlivePayload(int64_t interval)
852 {
853     OIC_LOG_V(DEBUG, TAG, "Create KeepAlive Payload, interval is [%" PRId64 "]", interval);
854
855     OCRepPayload *payload = OCRepPayloadCreate();
856     if (!payload)
857     {
858         OIC_LOG(ERROR, TAG, "Failed to allocate Payload");
859         return NULL;
860     }
861     payload->base.type = PAYLOAD_TYPE_REPRESENTATION;
862     OCRepPayloadSetPropInt(payload, INTERVAL, interval);
863
864     return payload;
865 }
866
867 OCStackResult AddResourceTypeNameToPayload(OCRepPayload *payload)
868 {
869     uint8_t numElement = 0;
870     OCStackResult res = OCGetNumberOfResourceTypes(g_keepAliveHandle, &numElement);
871     if (OC_STACK_OK == res)
872     {
873         size_t rtDim[MAX_REP_ARRAY_DEPTH] = {numElement, 0, 0};
874         char **rt = (char **)OICMalloc(sizeof(char *) * numElement);
875         for (uint8_t i = 0; i < numElement; ++i)
876         {
877             const char *value = OCGetResourceTypeName(g_keepAliveHandle, i);
878             OIC_LOG_V(DEBUG, TAG, "value: %s", value);
879             rt[i] = OICStrdup(value);
880             if (NULL == rt[i])
881             {
882                 OIC_LOG_V(ERROR, TAG, "Creating duplicate string for rt failed!");
883                 for (uint8_t j = 0; j < i; ++j)
884                 {
885                     OICFree(rt[j]);
886                 }
887                 OICFree(rt);
888                 return OC_STACK_NO_MEMORY;
889             }
890         }
891         OCRepPayloadSetStringArray(payload, OC_RSRVD_RESOURCE_TYPE, (const char **) rt, rtDim);
892         for (uint8_t i = 0; i < numElement; ++i)
893         {
894             OICFree(rt[i]);
895         }
896         OICFree(rt);
897     }
898
899     return res;
900 }
901
902 OCStackResult AddResourceInterfaceNameToPayload(OCRepPayload *payload)
903 {
904     uint8_t numElement = 0;
905     OCStackResult res = OCGetNumberOfResourceInterfaces(g_keepAliveHandle, &numElement);
906     if (OC_STACK_OK == res)
907     {
908         size_t ifDim[MAX_REP_ARRAY_DEPTH] = {numElement, 0, 0};
909         char **itf = (char **)OICMalloc(sizeof(char *) * numElement);
910         for (uint8_t i = 0; i < numElement; ++i)
911         {
912             const char *value = OCGetResourceInterfaceName(g_keepAliveHandle, i);
913             OIC_LOG_V(DEBUG, TAG, "value: %s", value);
914             itf[i] = OICStrdup(value);
915             if (NULL == itf[i])
916             {
917                 OIC_LOG_V(ERROR, TAG, "Creating duplicate string for itf failed!");
918                 for (uint8_t j = 0; j < i; ++j)
919                 {
920                     OICFree(itf[j]);
921                 }
922                 OICFree(itf);
923                 return OC_STACK_NO_MEMORY;
924             }
925         }
926         OCRepPayloadSetStringArray(payload, OC_RSRVD_INTERFACE, (const char **) itf, ifDim);
927         for (uint8_t i = 0; i < numElement; ++i)
928         {
929             OICFree(itf[i]);
930         }
931         OICFree(itf);
932     }
933
934     return res;
935 }