1 //******************************************************************
3 // Copyright 2014 Intel Mobile Communications GmbH 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 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
23 #include "ocstackconfig.h"
24 #include "ocstackinternal.h"
25 #include "ocobserve.h"
26 #include "ocresourcehandler.h"
28 #include "oic_malloc.h"
29 #include "oic_string.h"
30 #include "ocpayload.h"
31 #include "ocserverrequest.h"
38 #define MOD_NAME "ocobserve"
40 #define TAG "OCStackObserve"
42 #define VERIFY_NON_NULL(arg) { if (!arg) {OC_LOG(FATAL, TAG, #arg " is NULL"); goto exit;} }
44 static struct ResourceObserver * serverObsList = NULL;
46 * Determine observe QOS based on the QOS of the request.
47 * The qos passed as a parameter overrides what the client requested.
48 * If we want the client preference taking high priority make:
49 * qos = resourceObserver->qos;
51 * @param method RESTful method.
52 * @param resourceObserver Observer.
53 * @param appQoS Quality of service.
54 * @return The quality of service of the observer.
56 static OCQualityOfService DetermineObserverQoS(OCMethod method,
57 ResourceObserver * resourceObserver, OCQualityOfService appQoS)
61 OC_LOG(ERROR, TAG, "DetermineObserverQoS called with invalid resourceObserver");
65 OCQualityOfService decidedQoS = appQoS;
66 if(appQoS == OC_NA_QOS)
68 decidedQoS = resourceObserver->qos;
71 if(appQoS != OC_HIGH_QOS)
73 OC_LOG_V(INFO, TAG, "Current NON count for this observer is %d",
74 resourceObserver->lowQosCount);
76 if((resourceObserver->forceHighQos \
77 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT) \
78 && method != OC_REST_PRESENCE)
80 if(resourceObserver->forceHighQos \
81 || resourceObserver->lowQosCount >= MAX_OBSERVER_NON_COUNT)
84 resourceObserver->lowQosCount = 0;
85 // at some point we have to to send CON to check on the
86 // availability of observer
87 OC_LOG(INFO, TAG, "This time we are sending the notification as High qos");
88 decidedQoS = OC_HIGH_QOS;
92 (resourceObserver->lowQosCount)++;
99 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
100 OCPresenceTrigger trigger, OCResourceType *resourceType, OCQualityOfService qos)
102 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
103 OCQualityOfService qos)
106 OC_LOG(INFO, TAG, "Entering SendObserverNotification");
109 return OC_STACK_INVALID_PARAM;
112 OCStackResult result = OC_STACK_ERROR;
113 ResourceObserver * resourceObserver = serverObsList;
115 OCServerRequest * request = NULL;
116 OCEntityHandlerRequest ehRequest = {0};
117 OCEntityHandlerResult ehResult = OC_EH_ERROR;
118 bool observeErrorFlag = false;
120 // Find clients that are observing this resource
121 while (resourceObserver)
123 if (resourceObserver->resource == resPtr)
127 if(method != OC_REST_PRESENCE)
130 qos = DetermineObserverQoS(method, resourceObserver, qos);
132 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
133 0, resPtr->sequenceNum, qos, resourceObserver->query,
135 resourceObserver->token, resourceObserver->tokenLength,
136 resourceObserver->resUri, 0, resourceObserver->acceptFormat,
137 &resourceObserver->devAddr);
141 request->observeResult = OC_STACK_OK;
142 if(result == OC_STACK_OK)
144 result = FormOCEntityHandlerRequest(
146 (OCRequestHandle) request,
149 (OCResourceHandle) resPtr,
152 request->payloadSize,
153 request->numRcvdVendorSpecificHeaderOptions,
154 request->rcvdVendorSpecificHeaderOptions,
155 OC_OBSERVE_NO_OPTION,
157 if(result == OC_STACK_OK)
159 ehResult = resPtr->entityHandler(OC_REQUEST_FLAG, &ehRequest,
160 resPtr->entityHandlerCallbackParam);
161 if(ehResult == OC_EH_ERROR)
163 FindAndDeleteServerRequest(request);
166 OCPayloadDestroy(ehRequest.payload);
173 OCEntityHandlerResponse ehResponse = {0};
175 //This is effectively the implementation for the presence entity handler.
176 OC_LOG(DEBUG, TAG, "This notification is for Presence");
177 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
178 0, resPtr->sequenceNum, qos, resourceObserver->query,
180 resourceObserver->token, resourceObserver->tokenLength,
181 resourceObserver->resUri, 0, resourceObserver->acceptFormat,
182 &resourceObserver->devAddr);
184 if(result == OC_STACK_OK)
186 OCPresencePayload* presenceResBuf = OCPresencePayloadCreate(
187 resPtr->sequenceNum, maxAge, trigger,
188 resourceType ? resourceType->resourcetypename : NULL);
192 return OC_STACK_NO_MEMORY;
195 if(result == OC_STACK_OK)
197 ehResponse.ehResult = OC_EH_OK;
198 ehResponse.payload = (OCPayload*)presenceResBuf;
199 ehResponse.persistentBufferFlag = 0;
200 ehResponse.requestHandle = (OCRequestHandle) request;
201 ehResponse.resourceHandle = (OCResourceHandle) resPtr;
202 OICStrcpy(ehResponse.resourceUri, sizeof(ehResponse.resourceUri),
203 resourceObserver->resUri);
204 result = OCDoResponse(&ehResponse);
207 OCPresencePayloadDestroy(presenceResBuf);
212 // Since we are in a loop, set an error flag to indicate at least one error occurred.
213 if (result != OC_STACK_OK)
215 observeErrorFlag = true;
218 resourceObserver = resourceObserver->next;
223 OC_LOG(INFO, TAG, "Resource has no observers");
224 result = OC_STACK_NO_OBSERVERS;
226 else if (observeErrorFlag)
228 OC_LOG(ERROR, TAG, "Observer notification error");
229 result = OC_STACK_ERROR;
234 OCStackResult SendListObserverNotification (OCResource * resource,
235 OCObservationId *obsIdList, uint8_t numberOfIds,
236 const OCRepPayload *payload,
238 OCQualityOfService qos)
241 if(!resource || !obsIdList || !payload)
243 return OC_STACK_INVALID_PARAM;
246 uint8_t numIds = numberOfIds;
247 ResourceObserver *observer = NULL;
248 uint8_t numSentNotification = 0;
249 OCServerRequest * request = NULL;
250 OCStackResult result = OC_STACK_ERROR;
251 bool observeErrorFlag = false;
253 OC_LOG(INFO, TAG, "Entering SendListObserverNotification");
256 observer = GetObserverUsingId (*obsIdList);
259 // Found observer - verify if it matches the resource handle
260 if (observer->resource == resource)
262 qos = DetermineObserverQoS(OC_REST_GET, observer, qos);
265 result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
266 0, resource->sequenceNum, qos, observer->query,
267 NULL, NULL, observer->token, observer->tokenLength,
268 observer->resUri, 0, observer->acceptFormat,
273 request->observeResult = OC_STACK_OK;
274 if(result == OC_STACK_OK)
276 OCEntityHandlerResponse ehResponse = {0};
277 ehResponse.ehResult = OC_EH_OK;
278 ehResponse.payload = (OCPayload*)OCRepPayloadCreate();
279 if(!ehResponse.payload)
281 FindAndDeleteServerRequest(request);
284 memcpy(ehResponse.payload, payload, sizeof(*payload));
285 ehResponse.persistentBufferFlag = 0;
286 ehResponse.requestHandle = (OCRequestHandle) request;
287 ehResponse.resourceHandle = (OCResourceHandle) resource;
288 result = OCDoResponse(&ehResponse);
289 if(result == OC_STACK_OK)
291 OC_LOG_V(INFO, TAG, "Observer id %d notified.", *obsIdList);
293 // Increment only if OCDoResponse is successful
294 numSentNotification++;
296 OICFree(ehResponse.payload);
297 FindAndDeleteServerRequest(request);
301 OC_LOG_V(INFO, TAG, "Error notifying observer id %d.", *obsIdList);
306 FindAndDeleteServerRequest(request);
309 // Since we are in a loop, set an error flag to indicate
310 // at least one error occurred.
311 if (result != OC_STACK_OK)
313 observeErrorFlag = true;
321 if(numSentNotification == numberOfIds && !observeErrorFlag)
325 else if(numSentNotification == 0)
327 return OC_STACK_NO_OBSERVERS;
331 OC_LOG(ERROR, TAG, "Observer notification error");
332 return OC_STACK_ERROR;
336 OCStackResult GenerateObserverId (OCObservationId *observationId)
338 ResourceObserver *resObs = NULL;
340 OC_LOG(INFO, TAG, "Entering GenerateObserverId");
341 VERIFY_NON_NULL (observationId);
345 *observationId = OCGetRandomByte();
346 // Check if observation Id already exists
347 resObs = GetObserverUsingId (*observationId);
348 } while (NULL != resObs);
350 OC_LOG_V(INFO, TAG, "GeneratedObservation ID is %u", *observationId);
354 return OC_STACK_ERROR;
357 OCStackResult AddObserver (const char *resUri,
359 OCObservationId obsId,
362 OCResource *resHandle,
363 OCQualityOfService qos,
364 OCPayloadFormat acceptFormat,
365 const OCDevAddr *devAddr)
367 // Check if resource exists and is observable.
370 return OC_STACK_INVALID_PARAM;
372 if (!(resHandle->resourceProperties & OC_OBSERVABLE))
374 return OC_STACK_RESOURCE_ERROR;
376 ResourceObserver *obsNode = NULL;
378 if(!resUri || !token || !*token)
380 return OC_STACK_INVALID_PARAM;
383 obsNode = (ResourceObserver *) OICCalloc(1, sizeof(ResourceObserver));
386 obsNode->observeId = obsId;
388 obsNode->resUri = OICStrdup(resUri);
389 VERIFY_NON_NULL (obsNode->resUri);
392 obsNode->acceptFormat = acceptFormat;
395 obsNode->query = OICStrdup(query);
396 VERIFY_NON_NULL (obsNode->query);
398 // If tokenLength is zero, the return value depends on the
399 // particular library implementation (it may or may not be a null pointer).
402 obsNode->token = (CAToken_t)OICMalloc(tokenLength);
403 VERIFY_NON_NULL (obsNode->token);
404 memcpy(obsNode->token, token, tokenLength);
406 obsNode->tokenLength = tokenLength;
408 obsNode->devAddr = *devAddr;
409 obsNode->resource = resHandle;
411 LL_APPEND (serverObsList, obsNode);
419 OICFree(obsNode->resUri);
420 OICFree(obsNode->query);
423 return OC_STACK_NO_MEMORY;
426 ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
428 ResourceObserver *out = NULL;
432 LL_FOREACH (serverObsList, out)
434 if (out->observeId == observeId)
440 OC_LOG(INFO, TAG, "Observer node not found!!");
444 ResourceObserver* GetObserverUsingToken (const CAToken_t token, uint8_t tokenLength)
446 ResourceObserver *out = NULL;
450 OC_LOG(INFO, TAG, "Looking for token");
451 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)token, tokenLength);
452 OC_LOG(INFO, TAG, "\tFound token:");
454 LL_FOREACH (serverObsList, out)
456 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)out->token, tokenLength);
457 if((memcmp(out->token, token, tokenLength) == 0))
465 OC_LOG(ERROR, TAG, "Passed in NULL token");
468 OC_LOG(INFO, TAG, "Observer node not found!!");
472 OCStackResult DeleteObserverUsingToken (CAToken_t token, uint8_t tokenLength)
474 if(!token || !*token)
476 return OC_STACK_INVALID_PARAM;
479 ResourceObserver *obsNode = NULL;
481 obsNode = GetObserverUsingToken (token, tokenLength);
484 OC_LOG_V(INFO, TAG, "deleting observer id %u with token", obsNode->observeId);
485 OC_LOG_BUFFER(INFO, TAG, (const uint8_t *)obsNode->token, tokenLength);
486 LL_DELETE (serverObsList, obsNode);
487 OICFree(obsNode->resUri);
488 OICFree(obsNode->query);
489 OICFree(obsNode->token);
492 // it is ok if we did not find the observer...
496 void DeleteObserverList()
498 ResourceObserver *out = NULL;
499 ResourceObserver *tmp = NULL;
500 LL_FOREACH_SAFE (serverObsList, out, tmp)
504 DeleteObserverUsingToken ((out->token), out->tokenLength);
507 serverObsList = NULL;
511 * CA layer expects observe registration/de-reg/notiifcations to be passed as a header
512 * option, which breaks the protocol abstraction requirement between RI & CA, and
513 * has to be fixed in the future. The function below adds the header option for observe.
514 * It should be noted that the observe header option is assumed to be the first option
515 * in the list of user defined header options and hence it is inserted at the front
516 * of the header options list and number of options adjusted accordingly.
519 CreateObserveHeaderOption (CAHeaderOption_t **caHdrOpt,
520 OCHeaderOption *ocHdrOpt,
526 return OC_STACK_INVALID_PARAM;
529 CAHeaderOption_t *tmpHdrOpt = NULL;
531 tmpHdrOpt = (CAHeaderOption_t *) OICCalloc ((numOptions+1), sizeof(CAHeaderOption_t));
532 if (NULL == tmpHdrOpt)
534 return OC_STACK_NO_MEMORY;
536 tmpHdrOpt[0].protocolID = CA_COAP_ID;
537 tmpHdrOpt[0].optionID = COAP_OPTION_OBSERVE;
538 tmpHdrOpt[0].optionLength = sizeof(uint8_t);
539 tmpHdrOpt[0].optionData[0] = observeFlag;
540 for (uint8_t i = 0; i < numOptions; i++)
542 memcpy (&(tmpHdrOpt[i+1]), &(ocHdrOpt[i]), sizeof(CAHeaderOption_t));
545 *caHdrOpt = tmpHdrOpt;
550 * CA layer passes observe information to the RI layer as a header option, which
551 * breaks the protocol abstraction requirement between RI & CA, and has to be fixed
552 * in the future. The function below removes the observe header option and processes it.
553 * It should be noted that the observe header option is always assumed to be the first
554 * option in the list of user defined header options and hence it is deleted from the
555 * front of the header options list and the number of options is adjusted accordingly.
558 GetObserveHeaderOption (uint32_t * observationOption,
559 CAHeaderOption_t *options,
560 uint8_t * numOptions)
562 if(!observationOption)
564 return OC_STACK_INVALID_PARAM;
566 *observationOption = OC_OBSERVE_NO_OPTION;
568 if(!options || !numOptions)
570 return OC_STACK_INVALID_PARAM;
573 for(uint8_t i = 0; i < *numOptions; i++)
575 if(options[i].protocolID == CA_COAP_ID &&
576 options[i].optionID == COAP_OPTION_OBSERVE)
578 *observationOption = options[i].optionData[0];
579 for(uint8_t c = i; c < *numOptions-1; c++)
581 options[i] = options[i+1];