Merge pull request #512 from didishe90/persistSerDir

Persistent service directory
diff --git a/src/lib/core/WeaveConfig.h b/src/lib/core/WeaveConfig.h
index 3ae8835..52540bc 100644
--- a/src/lib/core/WeaveConfig.h
+++ b/src/lib/core/WeaveConfig.h
@@ -1905,6 +1905,23 @@
 #endif // WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
 
 /**
+ *  @def WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+ *
+ *  @brief
+ *    If set to (1), persistent service directory is enabled.
+ *    Default value is (0) or disabled.
+ *
+ *  @note
+ *    Enabling this feature flag persists the service directory
+ *    cache retrieved from the directory server and use it to
+ *    populate the in-memory cache at initialization time.
+ *
+ */
+#ifndef WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+#define WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY               0
+#endif // WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
+/**
  *  @def WEAVE_CONFIG_SERVICE_DIR_CONNECT_TIMEOUT_MSECS
  *
  *  @brief
diff --git a/src/lib/profiles/service-directory/ServiceDirectory.cpp b/src/lib/profiles/service-directory/ServiceDirectory.cpp
index 86e6eb6..ecfba9c 100644
--- a/src/lib/profiles/service-directory/ServiceDirectory.cpp
+++ b/src/lib/profiles/service-directory/ServiceDirectory.cpp
@@ -358,6 +358,30 @@
 
     clearCacheState();
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
+    if (Platform::IsPersistentServiceDirPresent(kPersistedServiceDirVersion))
+    {
+        WeaveLogProgress(ServiceDirectory, "Persistent service directory exists");
+
+        err = loadPersistentServiceDirIntoCache();
+
+        if (err != WEAVE_NO_ERROR)
+        {
+            Platform::ClearPersistentServiceDir();
+            WeaveLogProgress(ServiceDirectory, "Restore error, persistent service directory cleared");
+
+        }
+        else
+        {
+            mCacheState = kServiceMgrState_Resolved;
+            WeaveLogProgress(ServiceDirectory, "Persistent service directory successfully restored");
+        }
+
+    }
+
+#endif // WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
     finalizeConnectRequests();
 
 exit:
@@ -428,6 +452,10 @@
          * service config and install it.
          */
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+        Platform::ClearPersistentServiceDir();
+#endif // WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
         err = mAccessor(mCache.base, mCache.length);
         SuccessOrExit(err);
 
@@ -903,6 +931,11 @@
 
     if (mCacheState > kServiceMgrState_Resolving)
     {
+
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+        Platform::ClearPersistentServiceDir();
+#endif //WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
         cleanupExchangeContext();
 
         mCacheState = kServiceMgrState_Resolving;
@@ -949,6 +982,10 @@
     clearWorkingState();
     clearCacheState();
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+    Platform::ClearPersistentServiceDir();
+#endif //WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
     finalizeConnectRequests();
 }
 
@@ -1114,6 +1151,10 @@
 
         clearWorkingState();
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+        Platform::ClearPersistentServiceDir();
+#endif
+
         mCacheState = kServiceMgrState_Initial;
 
         transactionsReportStatus(report);
@@ -1133,79 +1174,19 @@
         VerifyOrExit(aMsgType == kMsgType_ServiceEndpointResponse, err = WEAVE_ERROR_INVALID_MESSAGE_TYPE);
         VerifyOrExit(mCacheState == kServiceMgrState_Waiting, err = WEAVE_ERROR_INCORRECT_STATE);
 
-        /*
-         * this block unpacks the service directory message.
-         */
+        err = unpackPacketBuffer(aMsg, true, &redir);
+        SuccessOrExit(err);
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+        if (!redir)
         {
-            MessageIterator i(aMsg);
-
-            uint16_t msgLen = aMsg->DataLength();
-            uint8_t dirCtrl;
-            uint8_t *writePtr;
-            uint8_t dirLen;
-            bool suffixesPresent;
-            uint8_t aLength;
-            bool timePresent = false;
-
-            err = i.readByte(&dirCtrl);
+            WeaveLogProgress(ServiceDirectory, "Persisting service directory response");
+            err = Platform::StorePersistentServiceDir(aMsg->Start(),
+                                                      aMsg->DataLength(),
+                                                      kPersistedServiceDirVersion);
             SuccessOrExit(err);
-
-            dirLen = dirCtrl & kMask_DirectoryLen;
-            redir = (dirCtrl & kMask_Redirect) != 0;
-            suffixesPresent = (dirCtrl & kMask_SuffixTablePresent) != 0;
-            timePresent = (dirCtrl & kMask_TimeFieldsPresent) != 0;
-
-            if (((msgLen > mCache.length) && !timePresent) || (msgLen > (mCache.length + (sizeof(uint64_t) + sizeof(uint32_t)))))
-            {
-                WeaveLogProgress(ServiceDirectory, "message length error: %d m.len:%d", msgLen, mCache.length);
-
-                err = WEAVE_ERROR_MESSAGE_TOO_LONG;
-            }
-            SuccessOrExit(err);
-
-            /*
-             * here we have directory information beyond the root directory but
-             * we're not done yet.
-             */
-
-            mDirectory.length = dirLen;
-            writePtr = mDirectory.base = mCache.base;
-
-            err = cacheDirectory(i, mDirectory.length, writePtr);
-            SuccessOrExit(err);
-
-            if (suffixesPresent)
-            {
-                WeaveLogProgress(ServiceDirectory, "suffixesPresent");
-
-                err = i.readByte(&aLength);
-                SuccessOrExit(err);
-
-                mSuffixTable.length = aLength;
-                writePtr += 1;
-                mSuffixTable.base = writePtr;
-
-                mDirAndSuffTableSize++;
-
-                err = cacheSuffixes(i, mSuffixTable.length, writePtr);
-                SuccessOrExit(err);
-            }
-
-            else
-            {
-                mSuffixTable.length = 0;
-                mSuffixTable.base = NULL;
-            }
-
-            if (timePresent)
-            {
-                WeaveLogProgress(ServiceDirectory, "timePresent");
-
-                err = handleTimeInfo(i);
-                SuccessOrExit(err);
-            }
         }
+#endif // WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
 
         /*
          * Release the received message buffer so that any code we
@@ -1576,6 +1557,128 @@
     return err;
 }
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+/**
+ *  @brief
+ *    This method loads the persistent service directory to cache.
+ */
+WEAVE_ERROR WeaveServiceManager::loadPersistentServiceDirIntoCache()
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    PacketBuffer* msgBuf = NULL;
+    uint16_t len;
+
+    msgBuf = PacketBuffer::New();
+    err = Platform::LoadPersistentServiceDir(msgBuf->Start(),
+                                             msgBuf->AvailableDataLength(),
+                                             len,
+                                             kPersistedServiceDirVersion);
+    SuccessOrExit(err);
+
+    msgBuf->SetDataLength(len);
+
+    err = unpackPacketBuffer(msgBuf, false);
+    SuccessOrExit(err);
+
+exit:
+
+    if (msgBuf != NULL)
+        PacketBuffer::Free(msgBuf);
+
+    return err;
+}
+#endif //WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
+
+/**
+ *  @brief
+ *    This method unpacks the packetbuffer to cache.
+ *
+ *  @param [in] aMsg         The content of this message.
+ *  @param [in] useTimeInfo If need to handle time info
+ *  @param [in] redir        Return whether need to redirect
+ */
+WEAVE_ERROR WeaveServiceManager::unpackPacketBuffer(PacketBuffer *aMsg, bool useTimeInfo, bool *redir)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    WeaveLogProgress(ServiceDirectory, "Unpacks the packetBuffer to cache");
+
+    {
+        MessageIterator i(aMsg);
+
+        uint16_t msgLen = aMsg->DataLength();
+        uint8_t dirCtrl;
+        uint8_t *writePtr;
+        uint8_t dirLen;
+        bool suffixesPresent;
+        uint8_t aLength;
+        bool timePresent = false;
+
+        err = i.readByte(&dirCtrl);
+        SuccessOrExit(err);
+        dirLen = dirCtrl & kMask_DirectoryLen;
+        if (redir != NULL)
+        {
+          *redir = (dirCtrl & kMask_Redirect) != 0;
+        }
+        suffixesPresent = (dirCtrl & kMask_SuffixTablePresent) != 0;
+        timePresent = (dirCtrl & kMask_TimeFieldsPresent) != 0;
+
+        if (((msgLen > mCache.length) && !timePresent) || (msgLen > (mCache.length + (sizeof(uint64_t) + sizeof(uint32_t)))))
+        {
+            WeaveLogProgress(ServiceDirectory, "message length error: %d m.len:%d", msgLen, mCache.length);
+
+            err = WEAVE_ERROR_MESSAGE_TOO_LONG;
+        }
+        SuccessOrExit(err);
+
+        /*
+         * here we have directory information beyond the root directory but
+         * we're not done yet.
+         */
+
+        mDirectory.length = dirLen;
+        writePtr = mDirectory.base = mCache.base;
+
+        err = cacheDirectory(i, mDirectory.length, writePtr);
+        SuccessOrExit(err);
+
+        if (suffixesPresent)
+        {
+            WeaveLogProgress(ServiceDirectory, "suffixesPresent");
+
+            err = i.readByte(&aLength);
+            SuccessOrExit(err);
+
+            mSuffixTable.length = aLength;
+            writePtr += 1;
+            mSuffixTable.base = writePtr;
+
+            mDirAndSuffTableSize++;
+
+            err = cacheSuffixes(i, mSuffixTable.length, writePtr);
+            SuccessOrExit(err);
+        }
+
+        else
+        {
+            mSuffixTable.length = 0;
+            mSuffixTable.base = NULL;
+        }
+
+        if (timePresent && useTimeInfo)
+        {
+            WeaveLogProgress(ServiceDirectory, "timePresent");
+
+            err = handleTimeInfo(i);
+            SuccessOrExit(err);
+        }
+    }
+
+exit:
+
+    return err;
+}
 
 /**
  *  @brief
@@ -1763,6 +1866,10 @@
     clearWorkingState();
     clearCacheState();
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+    Platform::ClearPersistentServiceDir();
+#endif
+
     transactionsErrorOut(aError);
 }
 
@@ -1922,6 +2029,10 @@
     {
         clearWorkingState();
         clearCacheState();
+
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+        Platform::ClearPersistentServiceDir();
+#endif
     }
 }
 #endif //WEAVE_CONFIG_ENABLE_SERVICE_DIRECTORY
diff --git a/src/lib/profiles/service-directory/ServiceDirectory.h b/src/lib/profiles/service-directory/ServiceDirectory.h
index 479101b..582c9c1 100644
--- a/src/lib/profiles/service-directory/ServiceDirectory.h
+++ b/src/lib/profiles/service-directory/ServiceDirectory.h
@@ -133,6 +133,27 @@
     kServiceMgrState_Resolved =             3
 };
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+enum
+{
+    /** Version number associated with persisted service directory data
+     *
+     * By convention, the version number of persisted service directory
+     * data is the same as the message type number of the SD response
+     * message.  This reflects the fact that the persisted data is an
+     * exact copy of the payload of that message.
+     */
+    kPersistedServiceDirVersion = kMsgType_ServiceEndpointResponse
+};
+
+namespace Platform {
+    extern WEAVE_ERROR LoadPersistentServiceDir(uint8_t *buf, uint16_t bufsize, uint16_t &len, uint8_t version);
+    extern WEAVE_ERROR StorePersistentServiceDir(uint8_t *buf, uint16_t len, uint8_t version);
+    extern WEAVE_ERROR ClearPersistentServiceDir();
+    extern bool IsPersistentServiceDirPresent(uint8_t version);
+}
+#endif // WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+
 #define kServiceEndpoint_Directory              (0x18B4300200000001ull)     ///< Directory profile endpoint
 #define kServiceEndpoint_SoftwareUpdate         (0x18B4300200000002ull)     ///< Software update profile endpoint
 #define kServiceEndpoint_Data_Management        (0x18B4300200000003ull)     ///< Core Weave data management protocol endpoint
@@ -411,6 +432,10 @@
                                  const uint32_t aConnectTimeoutMsecs = 0,
                                  const InterfaceId aConnectIntf = INET_NULL_INTERFACEID);
 
+#if WEAVE_CONFIG_PERSIST_SERVICE_DIRECTORY
+    WEAVE_ERROR loadPersistentServiceDirIntoCache();
+#endif
+    WEAVE_ERROR unpackPacketBuffer(PacketBuffer *aMsg, bool useTimePresent, bool *redir = NULL);
     WEAVE_ERROR cacheDirectory(MessageIterator &, uint8_t, uint8_t *&);
     WEAVE_ERROR cacheSuffixes(MessageIterator &, uint8_t, uint8_t *&);
     WEAVE_ERROR calculateEntryLength(uint8_t *entryStart, uint8_t entryCtrlByte, uint16_t *entryLen);