Added first implementaion of notification subscription methods
authorAndriy Gudz <a.gudz@samsung.com>
Fri, 12 May 2017 06:29:51 +0000 (09:29 +0300)
committerAndriy Gudz <a.gudz@samsung.com>
Fri, 12 May 2017 06:29:51 +0000 (09:29 +0300)
network-manager/nmlib/CMakeLists.txt
network-manager/nmlib/IoT/src/iotivity.cpp
network-manager/nmlib/ctrl_app/src/iot_dev_manager.cpp
network-manager/nmlib/include/iotivity.h
network-manager/nmlib/include/nmlib.h
network-manager/test/test_IoT.cpp
network-manager/test/test_nmlibapi.cpp

index a03b9c3..43cf37d 100644 (file)
@@ -14,11 +14,11 @@ include_directories(
        easy-setup/enrollee/inc
        json_policy/inc
        easy-setup/mediator/richsdk/inc
+       ../secserver
 )
 
 #add_subdirectory(easy-setup/enrollee)
 add_subdirectory(easy-setup/mediator/richsdk)
-#add_subdirectory(IoT)
 
 FILE(GLOB CTRL_APP_SUPPORT_SRCS ctrl_app/src/*.cpp)
 
index 19400ae..2fe4189 100644 (file)
@@ -15,6 +15,8 @@
 #pragma GCC diagnostic pop
 #include <logging.h>
 
+#include "notification_resource.h"
+
 using namespace std;
 using namespace OC;
 #define TAG "NetworkManager"
@@ -26,6 +28,9 @@ namespace
 const std::string SIGNUP = "Sign up";
 const std::string SIGNIN = "Sign in";
 const std::string SIGNOUT = "Sign out";
+const std::string NOTIF_FIND = "Notification findResource()";
+const std::string NOTIF_REQUEST = string(OC_RSRVD_WELL_KNOWN_URI) + "?rt=" + NOTIFICATION_TYPE;
+
 const std::string REP_LIST{"prslist"};
 
 void printRepresentation(OCRepresentation rep)
@@ -76,7 +81,7 @@ namespace NetworkManager
 {
 
 const std::string IoTivity::DEFAULT_PROVIDER = "samsung";
-const int IoTivity::DEFAULT_TIMEOUT = 10;
+const int IoTivity::DEFAULT_TIMEOUT = 5;
 
 struct Params
 {
@@ -175,6 +180,12 @@ inline void guardErrorCode(OCStackResult resultCode, std::string message)
         throw IoTInternalError(message + " error:" + std::to_string(resultCode), EC_IOTIVITY_ERROR);
 }
 
+void IoTivity::guardUnauthorized()
+{
+    if (!signedIn)
+        throw IoTInternalError("UNAUTHORIZED error", EC_UNAUTHORIZED);
+}
+
 void IoTivity::signIn(const std::string& host, const std::string& login, const std::string& password)
 {
     if (host.empty() || login.empty() || password.empty())
@@ -220,7 +231,7 @@ void IoTivity::signIn(const std::string& host, const std::string& login, const s
 
     guardErrorCode(accountMgr->signUp(DEFAULT_PROVIDER, authcode, signUpCb), SIGNUP);
 
-    condVar.wait_for(lock, std::chrono::seconds(10));
+    condVar.wait_for(lock, std::chrono::seconds(DEFAULT_TIMEOUT));
     guardTimeout(signUpTimeout, SIGNUP);
     guardSignErrorCode(resultCodeCb, SIGNUP);
 
@@ -237,7 +248,7 @@ void IoTivity::signIn(const std::string& host, const std::string& login, const s
 
     guardErrorCode(accountMgr->signIn(oAuthCred.userId, oAuthCred.accessToken, signInCb), SIGNIN);
 
-    condVar.wait_for(lock, std::chrono::seconds(10));
+    condVar.wait_for(lock, std::chrono::seconds(DEFAULT_TIMEOUT));
     guardTimeout(signInTimeout, SIGNIN);
     guardSignErrorCode(resultCodeCb, SIGNIN);
 
@@ -276,7 +287,7 @@ void IoTivity::signOut()
     };
 
     guardErrorCode(accountMgr->signOut(oAuthCred.accessToken, signOutCb), SIGNOUT);
-    condVar.wait_for(lock, std::chrono::seconds(10));
+    condVar.wait_for(lock, std::chrono::seconds(DEFAULT_TIMEOUT));
     guardTimeout(signOutTimeout, SIGNOUT);
     guardSignErrorCode(resultCodeCb, SIGNOUT);
 
@@ -430,13 +441,73 @@ string IoTivity::getDeviceID()
 
 }
 
-void IoTivity::subscribeNotifications(NotificationCallback callback)
+void IoTivity::subscribeNotifications(NM_NotificationCb callback, void* user_data)
 {
-    throw IoTInternalError("subscribeNotifications() not implemented", EC_NOT_IMPLEMENTED_YET);
+    guardUnauthorized();
+
+    std::mutex mtx;
+    std::mutex curResourceLock;
+    std::unique_lock<std::mutex> lock(mtx);
+    std::condition_variable condVar;
+
+    bool findResourceTimeout = true;
+
+    std::shared_ptr<OCResource> curResource;
+
+    auto observCb = [callback, user_data](const HeaderOptions,
+                                          const OCRepresentation & rep,
+                                          const int& eCode,
+                                          const int& sequenceNumber)
+    {
+        string messageString = rep.getValueToString("message");
+        NM_NotificationData data
+        {
+            rep.getValue<int>("code"),
+            messageString.c_str(),
+            rep.getValue<int>("time")
+        };
+        callback(data, user_data);
+    };
+
+    auto foundCb = [&](std::shared_ptr<OCResource> resource)
+    {
+        lock_guard<std::mutex> lock(curResourceLock);
+        findResourceTimeout = false;
+        std::string resourceURI = resource->uri();
+        std::string resourceDuid = resource->sid();
+        std::stringstream searchedUri;
+        searchedUri << "/oic/route/" << resourceDuid << NOTIFICATION_URI;
+
+        //cout << searchedUri.str() << endl << flush;
+
+        if(searchedUri.str() == resourceURI && !curResource)
+        {
+            std::string resourceHost = resource->host();
+
+            curResource = resource;
+            cout << "\tFound: " << resourceURI << " "
+                 << resourceHost << " " << resourceDuid << endl << flush;
+            resource->observe(ObserveType::ObserveAll, QueryParamsMap(), observCb);
+            condVar.notify_all();
+        }
+    };
+
+    auto errorCb = [&](const std::string & resourceUri, const int ecode)
+    {
+        findResourceTimeout = false;
+        cout << "Error" << resourceUri << " " << ecode << endl << flush;
+        condVar.notify_all();
+    };
+
+    guardErrorCode(OCPlatform::findResource(oAuthCred.host, NOTIF_REQUEST, CT_DEFAULT, foundCb, errorCb), NOTIF_FIND);
+
+    condVar.wait_for(lock, std::chrono::seconds(DEFAULT_TIMEOUT));
+    guardTimeout(findResourceTimeout, NOTIF_FIND);
 }
 
 void IoTivity::unsubscribeNotifications()
 {
+    guardUnauthorized();
     throw IoTInternalError("unsubscribeNotifications() not implemented", EC_NOT_IMPLEMENTED_YET);
 }
 
index 685d502..b54d864 100644 (file)
@@ -314,3 +314,41 @@ NM_ErrorCode NM_unOwnDevice(NM_hDeviceList dev_list, const char* dev_id)
 {
     return EC_NOT_IMPLEMENTED_YET;
 }
+
+NM_ErrorCode NM_subscribeNotifications(NM_hContext ctx, NM_NotificationCb callback, void* user_data)
+{
+    if (ctx == nullptr) return EC_NULL_POINTER;
+    try
+    {
+        ctx->instance->subscribeNotifications(callback, user_data);
+    }
+    catch (NMexception& e)
+    {
+        LOG_E(TAG, "Subscribe notifications error: %s", e.what());
+        return (NM_ErrorCode) e.errorCode();
+    }
+    catch (std::exception& e)
+    {
+        LOG_E(TAG, "Subscribe notifications error: %s", e.what());
+        return EC_INTERNAL_ERROR;
+    }
+
+    return EC_OK;
+}
+
+void NM_unsubscribeNotifications(NM_hContext ctx)
+{
+    if (ctx == nullptr) return;
+    try
+    {
+        ctx->instance->unsubscribeNotifications();
+    }
+    catch (NMexception& e)
+    {
+        LOG_E(TAG, "Unsubscribe notifications error: %s, error code %d", e.what(), e.errorCode());
+    }
+    catch (std::exception& e)
+    {
+        LOG_E(TAG, "Unsubscribe notifications error: %s", e.what());
+    }
+}
index b824cd6..f373323 100644 (file)
@@ -15,8 +15,6 @@ typedef std::map<std::string, std::shared_ptr<IoTDevice> > IoTDevicesMap;
 
 typedef std::function<void(const std::string&, bool)> PresenceHook;
 
-typedef std::function<void(const std::string)> NotificationCallback;
-
 /**
  * @brief The OAuthCredential struct contains information about current session with cloud server. Filled after signIn().
  */
@@ -107,11 +105,12 @@ public:
     std::string getDeviceID();
 
     /**
-     * @brief subscribeNotifications subscribes to secure server notifications.
+     * @brief subscribeNotifications subscribes to secure server notifications
      * Only one subscription allowed.
      * @param callback method to receive notifications after subscription
+     * @param user_data pointer to the user defined data
      */
-    void subscribeNotifications(NotificationCallback callback);
+    void subscribeNotifications(NM_NotificationCb callback, void* user_data);
 
     /**
      * @brief unsubscribeNotifications unsubscribes to current secure server notifications
@@ -131,6 +130,8 @@ public:
     std::string getPersistentStoragePath();
 
 private:
+    void guardUnauthorized();
+
     static const std::string DEFAULT_PROVIDER;
     static const int DEFAULT_TIMEOUT;
 
index 9108669..c25efc3 100644 (file)
@@ -29,6 +29,7 @@ typedef enum  {
     EC_ITEM_NOT_FOUND,
     EC_TIMEOUT_ERROR,
     EC_IOTIVITY_ERROR,
+    EC_UNAUTHORIZED,
     // ...
 } NM_ErrorCode;
 
@@ -50,6 +51,13 @@ typedef struct
     DeviceState state;
 } NM_DeviceInfo;
 
+typedef struct
+{
+    int code;
+    const char* message;
+    int time;
+} NM_NotificationData;
+
 #define NULL_HANDLE (void*)0
 
 typedef struct NM_Context* NM_hContext;
@@ -73,6 +81,12 @@ typedef void (*NM_deviceStateChangedCb)(const char* id, DeviceState state, void*
 typedef void (*NM_deviceEnumerationCb)(NM_hDeviceList dev_list, const char* id, void* user_data);
 
 /**
+ * @brief Callback to subscribe on notifications from secure server
+ * @param data  [in] notification details: code number, message string, time
+ */
+typedef void (*NM_NotificationCb)(NM_NotificationData data, void* user_data);
+
+/**
  * @brief IoTivity initialization
  * @param  ctx context handle for internal data storage
  * @return error code
@@ -237,6 +251,22 @@ NM_ErrorCode NM_unOwnDevice(NM_hDeviceList dev_list, const char* dev_id);
  */
 //NM_ErrorCode NM_setDevicePolicy(NM_hDeviceList dev_list, const char* dev_id, const char* policy);
 
+/**
+ * @brief subscribeNotifications subscribes to secure server notifications.
+ * Triggered by secure server to notify user.
+ * @param ctx context handle for internal data storage
+ * @param callback method to receive notifications after subscription
+ * @param user_data pointer to the user defined data
+ * @return error code
+ */
+NM_ErrorCode NM_subscribeNotifications(NM_hContext ctx, NM_NotificationCb callback, void* user_data);
+
+/**
+ * @brief unsubscribeNotifications unsubscribes to current secure server notifications
+ * @param ctx context handle for internal data storage
+ */
+void NM_unsubscribeNotifications(NM_hContext ctx);
+
 #ifdef __cplusplus
 }
 #endif
index 40cb59a..ac090c5 100644 (file)
@@ -1,5 +1,16 @@
+/**
+ * @brief  Tests for iotivity.h API methods
+ * @date   Created 12.05.2017
+ * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract
+ *                between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
+ *                and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea).
+ *         Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
+ * @author Mail to: <A HREF="mailto:a.gudz@samsung.com">Andriy Gudz, a.gudz@samsung.com</A>
+ */
 #include <iostream>
 #include <stdexcept>
+#include <mutex>
+#include <condition_variable>
 
 #include <gtest/gtest.h>
 
@@ -38,27 +49,51 @@ TEST(test_IoT, signInIncorrectInput)
     std::string password("password");
     std::string host("coap+tcp://106.125.46.44:5683");
 
-    ASSERT_ANY_THROW(IoTivity::getInstance()->signIn("", login, password));
-    ASSERT_ANY_THROW(IoTivity::getInstance()->signIn(host, "", password));
-    ASSERT_ANY_THROW(IoTivity::getInstance()->signIn(host, login, ""));
+    auto iot = IoTivity::getInstance();
+
+    ASSERT_ANY_THROW(iot->signIn("", login, password));
+    ASSERT_ANY_THROW(iot->signIn(host, "", password));
+    ASSERT_ANY_THROW(iot->signIn(host, login, ""));
 }
 
-static void notificationCb(std::string notifMessage) {
-    cout << "notificationCb()" << endl << flush;
+static std::condition_variable notificationCV;
+static bool callbackFired = false;
+static char* callbackUserDataCheck = NULL;
+
+static void notificationCb(NM_NotificationData data, void* user_data)
+{
+    callbackFired = true;
+    callbackUserDataCheck = (char*) user_data;
+    notificationCV.notify_all();
 }
 
 /**
- * Test check correct work of IoTivity::subscribeNotifications()
+ * Test checks correct work of IoTivity::subscribeNotifications()
+ * 1. Check correct sign in
+ * 2. Call subscribe method and wait 3 sec for notification callback
+ * 3. Check correct sign out
+ * 4. Check callback was called
+ * 5. Check user data was transferd
  */
 TEST(test_IoT, notificationCorrect)
 {
     std::string login("login");
     std::string password("password");
     std::string host("coap+tcp://106.125.46.44:5683");
+    char userDataCheck[] = "userDataCheck";
+
+    static std::mutex notificationMtx;
+    static std::unique_lock<std::mutex> notificationLock(notificationMtx);
+
+    auto iot = IoTivity::getInstance();
+
+    ASSERT_NO_THROW(iot->signIn(host, login, password));
+    EXPECT_NO_THROW(iot->subscribeNotifications(notificationCb, userDataCheck));
+    notificationCV.wait_for(notificationLock, std::chrono::seconds(3));
+    EXPECT_NO_THROW(iot->signOut());
 
-    ASSERT_NO_THROW(IoTivity::getInstance()->signIn(host, login, password));
-    EXPECT_ANY_THROW(IoTivity::getInstance()->subscribeNotifications(notificationCb));
-    EXPECT_NO_THROW(IoTivity::getInstance()->signOut());
+    ASSERT_TRUE(callbackFired);
+    ASSERT_EQ(userDataCheck, callbackUserDataCheck);
 }
 
 /**
index be03c33..d8cc96f 100644 (file)
@@ -1,10 +1,22 @@
+/**
+ * @brief  Tests for nmlib.h API methods
+ * @date   Created 12.05.2017
+ * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract
+ *                between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
+ *                and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea).
+ *         Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
+ * @author Mail to: <A HREF="mailto:a.gudz@samsung.com">Andriy Gudz, a.gudz@samsung.com</A>
+ */
 #include <iostream>
 #include <stdexcept>
+#include <mutex>
+#include <condition_variable>
 
 #include <gtest/gtest.h>
 
 #include "nmlib.h"
-#include "iotivity.h"
+
+using namespace std;
 
 /**
  * Performs init, signin, sign out and cleanup
@@ -26,3 +38,53 @@ TEST(test_nmlibapi, signInCorrect)
 
     ASSERT_NO_THROW(NM_cleanup(&ctx));
 }
+
+static std::condition_variable notificationCV;
+static bool callbackFired = false;
+static char* callbackUserDataCheck = NULL;
+
+static void notificationCb(NM_NotificationData data, void* user_data)
+{
+    cout << "notificationCb() " << data.code << " "
+         << data.message << " "
+         << data.time << " "
+         << endl << flush;
+    callbackFired = true;
+    callbackUserDataCheck = (char*) user_data;
+    notificationCV.notify_all();
+}
+
+/**
+ * Test checks correct work of IoTivity::subscribeNotifications()
+ * 1. Check correct sign in
+ * 2. Call subscribe method and wait no more than 3 sec for notification callback
+ * 3. Check callback was called
+ * 4. Check user data was transferd
+ */
+TEST(test_nmlibapi, notificationCorrect)
+{
+    std::string login("login");
+    std::string password("password");
+    std::string host("coap+tcp://106.125.46.44:5683");
+    char userDataCheck[] = "userDataCheck";
+
+    static std::mutex notificationMtx;
+    static std::unique_lock<std::mutex> notificationLock(notificationMtx);
+
+    NM_hContext ctx;
+    ASSERT_EQ(EC_OK, NM_init(&ctx));
+
+    /* 1. Check correct sign in */
+    ASSERT_EQ(EC_OK, NM_signIn(ctx, host.c_str(), login.c_str(), password.c_str()));
+
+    /* 2. Call subscribe method and wait no more than 3 sec for notification callback */
+    ASSERT_EQ(EC_OK, NM_subscribeNotifications(ctx, notificationCb, userDataCheck));
+    notificationCV.wait_for(notificationLock, std::chrono::seconds(3));
+    NM_signOut(ctx);
+
+    /* 3. Check callback was called */
+    ASSERT_TRUE(callbackFired);
+
+    /* 4. Check user data was transferd */
+    ASSERT_EQ(userDataCheck, callbackUserDataCheck);
+}