RESET/ACK messages requirement to add additional data from CA .
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / caretransmission.c
1 /******************************************************************
2  *
3  * Copyright 2014 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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <glib.h>
26
27 #ifdef __ANDROID__
28 #include <linux/time.h>
29 #endif
30 #include "coap.h"
31 #include "caretransmission.h"
32 #include "caremotehandler.h"
33 #include "caprotocolmessage.h"
34 #include "oic_malloc.h"
35 #include "logger.h"
36
37 #define TAG PCF("CA")
38
39 typedef struct
40 {
41     /** last sent time. microseconds **/
42     uint64_t timeStamp;
43     /** timeout value. milliseconds **/
44     uint32_t timeout;
45     /** retransmission count **/
46     uint8_t triedCount;
47     /** coap PDU message id **/
48     uint16_t messageId;
49     /** remote endpoint **/
50     CARemoteEndpoint_t *endpoint;
51     /** coap PDU **/
52     void *pdu;
53     /** coap PDU size**/
54     uint32_t size;
55 } CARetransmissionData_t;
56
57 /**
58  * getCurrent monotonic time
59  *
60  * microseconds
61  */
62 uint64_t getCurrentTimeInMicroSeconds();
63
64 /**
65  * @brief   check timeout routine
66  * @param   currentTime     [IN]microseconds
67  * @param   timeStamp       [IN]microseconds
68  * @param   timeoutValue    [IN]milliseconds
69  * @param   triedCount      [IN]
70  * @return  CA_TRUE(timeout) or CA_FALSE
71  *
72  * microseconds
73  */
74 static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint32_t timeoutValue,
75         uint8_t triedCount)
76 {
77     // #1. calculate timeout
78     uint64_t timeout = (timeoutValue << triedCount) * (uint64_t) 1000;
79
80     if (currentTime >= timeStamp + timeout)
81     {
82         OIC_LOG_V(DEBUG, TAG, "%d milliseconds time out!!, tried count(%d)",
83             (timeoutValue << triedCount), triedCount);
84         return CA_TRUE;
85     }
86
87     return CA_FALSE;
88 }
89
90 /**
91  * @brief   timeout value is
92  *          between DEFAULT_ACK_TIMEOUT and (DEFAULT_ACK_TIMEOUT * DEFAULT_RANDOM_FACTOR) second.
93  *          DEFAULT_RANDOM_FACTOR       1.5 (CoAP)
94  * @return  milliseconds.
95  */
96 static uint32_t CAGetTimeoutValue()
97 {
98     return (DEFAULT_ACK_TIMEOUT * 1000) + ((1000 * (rand() & 0xFF)) >> 8);
99 }
100
101 static void CACheckRetransmissionList(CARetransmission_t *context)
102 {
103     // mutex lock
104     u_mutex_lock(context->threadMutex);
105     uint64_t currentTime = 0;
106
107     uint32_t i = 0;
108     uint32_t len = u_arraylist_length(context->dataList);
109
110     for (i = 0; i < len; i++)
111     {
112         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
113
114         if (retData == NULL)
115             continue;
116
117         currentTime = getCurrentTimeInMicroSeconds();
118
119         if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
120         {
121             // #2. if time's up, send the data.
122             if (context->dataSendMethod != NULL)
123             {
124                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
125                           retData->messageId);
126                 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
127             }
128
129             // #3. increase the retransmission count and update timestamp.
130             retData->timeStamp = currentTime;
131             retData->triedCount++;
132         }
133
134         // #4. if tried count is max, remove the retransmission data from list.
135         if (retData->triedCount >= context->config.tryingCount)
136         {
137             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
138
139             if (removedData != NULL)
140             {
141                 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
142                         message id(%d)", removedData->messageId);
143
144                 // callback for retransmit timeout
145                 if (context->timeoutCallback != NULL)
146                 {
147                     context->timeoutCallback(removedData->endpoint, removedData->pdu,
148                             removedData->size);
149                 }
150
151                 CADestroyRemoteEndpointInternal(removedData->endpoint);
152                 OICFree(removedData->pdu);
153
154                 OICFree(removedData);
155
156                 // modify loop value.
157                 len = u_arraylist_length(context->dataList);
158                 --i;
159             }
160             else
161             {
162                 OIC_LOG_V(DEBUG, TAG, "arraylist remove error");
163             }
164
165         }
166     }
167
168     // mutex unlock
169     u_mutex_unlock(context->threadMutex);
170 }
171
172 static void CARetransmissionBaseRoutine(void *threadValue)
173 {
174     OIC_LOG_V(DEBUG, TAG, "retransmission main thread start..");
175
176     CARetransmission_t *context = (CARetransmission_t *) threadValue;
177
178     if (context == NULL)
179     {
180         OIC_LOG_V(DEBUG, TAG, "thread data passing error!!");
181
182         return;
183     }
184
185     while (!context->isStop)
186     {
187         // mutex lock
188         u_mutex_lock(context->threadMutex);
189
190         if (u_arraylist_length(context->dataList) <= 0)
191         {
192             // if list is empty, thread will wait
193             OIC_LOG_V(DEBUG, TAG, "wait..there is no retransmission data.");
194
195             // wait
196             u_cond_wait(context->threadCond, context->threadMutex);
197
198             OIC_LOG_V(DEBUG, TAG, "wake up..");
199         }
200         else
201         {
202             // check each RETRANSMISSION_CHECK_PERIOD time.
203             OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds", RETRANSMISSION_CHECK_PERIOD);
204
205             // wait
206             u_cond_timed_wait(context->threadCond, context->threadMutex,
207                               RETRANSMISSION_CHECK_PERIOD);
208         }
209
210         // mutex unlock
211         u_mutex_unlock(context->threadMutex);
212
213         // check stop flag
214         if (context->isStop)
215             continue;
216
217         CACheckRetransmissionList(context);
218     }
219
220     u_cond_signal(context->threadCond);
221
222     OIC_LOG_V(DEBUG, TAG, "retransmission main thread end..");
223
224 }
225
226 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
227         CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
228         CARetransmissionConfig_t* config)
229 {
230     if (context == NULL)
231     {
232         OIC_LOG_V(DEBUG, TAG, "thread instance is empty..");
233         return CA_STATUS_FAILED;
234     }
235
236     if (handle == NULL)
237     {
238         OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
239         return CA_STATUS_FAILED;
240     }
241
242     OIC_LOG_V(DEBUG, TAG, "thread initialize..");
243
244     memset(context, 0, sizeof(CARetransmission_t));
245
246     CARetransmissionConfig_t cfg;
247     memset(&cfg, 0, sizeof(CARetransmissionConfig_t));
248
249     if (config == NULL)
250     {
251         // setDefault
252         cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
253         cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
254     }
255     else
256     {
257         cfg = *config;
258     }
259
260     // mutex init
261     u_mutex_init();
262
263     // set send thread data
264     context->threadPool = handle;
265     context->threadMutex = u_mutex_new();
266     context->threadCond = u_cond_new();
267     context->dataSendMethod = retransmissionSendMethod;
268     context->timeoutCallback = timeoutCallback;
269     context->config = cfg;
270     context->isStop = CA_FALSE;
271     context->dataList = u_arraylist_create();
272
273     return CA_STATUS_OK;
274 }
275
276 CAResult_t CARetransmissionStart(CARetransmission_t *context)
277 {
278     if (context == NULL)
279     {
280         OIC_LOG_V(DEBUG, TAG, "context is empty..");
281         return CA_STATUS_FAILED;
282     }
283
284     if (context->threadPool == NULL)
285     {
286         OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
287         return CA_STATUS_FAILED;
288     }
289
290     CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
291                                             context);
292
293     if (res != CA_STATUS_OK)
294     {
295         OIC_LOG_V(DEBUG, TAG, "thread pool add task error(send thread).");
296         return res;
297     }
298
299     return res;
300 }
301
302 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
303     const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
304 {
305     if (context == NULL || endpoint == NULL || pdu == NULL)
306     {
307         OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
308         return CA_STATUS_INVALID_PARAM;
309     }
310
311     // #0. check support connectivity type
312     if (!(context->config.supportType & endpoint->connectivityType))
313     {
314         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
315                   endpoint->connectivityType);
316         return CA_STATUS_OK;
317     }
318
319     // #1. check PDU method type and get message id.
320     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
321     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
322
323     OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
324
325     if (type != CA_MSG_CONFIRM)
326     {
327         return CA_STATUS_OK;
328     }
329
330     // create retransmission data
331     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICMalloc(
332                                           sizeof(CARetransmissionData_t));
333
334     if (retData == NULL)
335     {
336         OIC_LOG_V(DEBUG, TAG, "memory error!!");
337         return CA_MEMORY_ALLOC_FAILED;
338     }
339     memset(retData, 0, sizeof(CARetransmissionData_t));
340
341     // copy PDU data
342     void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
343     if (pduData == NULL)
344     {
345         OICFree(retData);
346         OIC_LOG_V(DEBUG, TAG, "memory error!!");
347         return CA_MEMORY_ALLOC_FAILED;
348     }
349     memset(pduData, 0, sizeof(int8_t) * size);
350     memcpy(pduData, pdu, sizeof(int8_t) * size);
351
352     // clone remote endpoint
353     CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
354     if (remoteEndpoint == NULL)
355     {
356         OICFree(retData);
357         OICFree(pduData);
358         OIC_LOG_V(DEBUG, TAG, "memory error!!");
359         return CA_MEMORY_ALLOC_FAILED;
360     }
361
362     // #2. add additional information. (time stamp, retransmission count...)
363     retData->timeStamp = getCurrentTimeInMicroSeconds();
364     retData->timeout = CAGetTimeoutValue();
365     retData->triedCount = 0;
366     retData->messageId = messageId;
367     retData->endpoint = remoteEndpoint;
368     retData->pdu = pduData;
369     retData->size = size;
370
371     // mutex lock
372     u_mutex_lock(context->threadMutex);
373
374     // #3. add data into list
375     u_arraylist_add(context->dataList, (void *) retData);
376
377     // notity the thread
378     u_cond_signal(context->threadCond);
379
380     // mutex unlock
381     u_mutex_unlock(context->threadMutex);
382
383     return CA_STATUS_OK;
384 }
385
386 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
387                                         const CARemoteEndpoint_t *endpoint,
388                                         const void *pdu, uint32_t size,
389                                         void **retransmissionPdu)
390 {
391     OIC_LOG_V(DEBUG, TAG, "IN - CARetransmissionReceivedData");
392     if (context == NULL || endpoint == NULL || pdu == NULL)
393     {
394         OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
395         return CA_STATUS_INVALID_PARAM;
396     }
397
398     // #0. check support connectivity type
399     if (!(context->config.supportType & endpoint->connectivityType))
400     {
401         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
402                   endpoint->connectivityType);
403         return CA_STATUS_OK;
404     }
405
406     // #1. check PDU method type and get message id.
407     // ACK, RST --> remove the CON data
408     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
409     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
410
411     OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
412
413     if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
414     {
415         return CA_STATUS_OK;
416     }
417
418     // mutex lock
419     u_mutex_lock(context->threadMutex);
420
421     uint32_t i = 0;
422     uint32_t len = u_arraylist_length(context->dataList);
423
424     // find index
425     for (i = 0; i < len; i++)
426     {
427         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
428
429         if (retData == NULL)
430             continue;
431
432         // found index
433         if ((retData->endpoint->connectivityType == endpoint->connectivityType)
434             && retData->messageId == messageId)
435         {
436             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
437             // if retransmission was finish..token will be unavailable.
438             if(CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
439             {
440                 OIC_LOG_V(DEBUG, TAG, "code is CA_EMPTY..");
441
442                 if(NULL == retData->pdu)
443                 {
444                     OIC_LOG_V(DEBUG, TAG, "retData->pdu is null");
445                 }
446
447                 // copy PDU data
448                 (*retransmissionPdu) = (void *) OICMalloc(sizeof(int8_t) * retData->size);
449                 if ((*retransmissionPdu) == NULL)
450                 {
451                     OICFree(retData);
452                     OIC_LOG_V(DEBUG, TAG, "memory error!!");
453                     return CA_MEMORY_ALLOC_FAILED;
454                 }
455                 memset((*retransmissionPdu), 0, sizeof(int8_t) * retData->size);
456                 memcpy((*retransmissionPdu), retData->pdu, sizeof(int8_t) * retData->size);
457             }
458
459             break;
460         }
461     }
462
463     // #2. remove data from list
464     if (i < len)
465     {
466         CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
467
468         if (removedData != NULL)
469         {
470             OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)", messageId);
471
472             CADestroyRemoteEndpointInternal(removedData->endpoint);
473             OICFree(removedData->pdu);
474
475             OICFree(removedData);
476         }
477     }
478
479     // mutex unlock
480     u_mutex_unlock(context->threadMutex);
481
482     OIC_LOG_V(DEBUG, TAG, "OUT - CARetransmissionReceivedData");
483     return CA_STATUS_OK;
484 }
485
486 CAResult_t CARetransmissionStop(CARetransmission_t *context)
487 {
488     if (context == NULL)
489     {
490         OIC_LOG_V(DEBUG, TAG, "context is empty..");
491         return CA_STATUS_FAILED;
492     }
493
494     OIC_LOG_V(DEBUG, TAG, "retransmission stop request!!");
495
496     // mutex lock
497     u_mutex_lock(context->threadMutex);
498
499     // set stop flag
500     context->isStop = CA_TRUE;
501
502     // notity the thread
503     u_cond_signal(context->threadCond);
504
505     u_cond_wait(context->threadCond, context->threadMutex);
506
507     // mutex unlock
508     u_mutex_unlock(context->threadMutex);
509
510     return CA_STATUS_OK;
511 }
512
513 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
514 {
515     if (context == NULL)
516     {
517         OIC_LOG_V(DEBUG, TAG, "context is empty..");
518         return CA_STATUS_FAILED;
519     }
520
521     OIC_LOG_V(DEBUG, TAG, "retransmission context destroy..");
522
523     u_mutex_free(context->threadMutex);
524     context->threadMutex = NULL;
525     u_cond_free(context->threadCond);
526     u_arraylist_free(&context->dataList);
527
528     return CA_STATUS_OK;
529 }
530
531 uint64_t getCurrentTimeInMicroSeconds()
532 {
533     uint64_t currentTime = 0;
534
535 #ifdef __ANDROID__
536     struct timespec getTs;
537
538     memset(&getTs, 0, sizeof(getTs));
539     clock_gettime(CLOCK_MONOTONIC, &getTs);
540
541     currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
542     OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
543 #else
544     currentTime = g_get_monotonic_time();
545 #endif
546     return currentTime;
547 }