Add CHIP's controller API and update the standalone example to use it (#666)

* Add CHIP's controller API and update the standalone example to use it

* Fix class name in documentation

* Fix inclusion guards

* Address review comments and fix build

* Fix DIST

* Add tests for Chip Connection

* Actually fix distcheck

* Rename deviceMgr to deviceController

* Update test to follow new test structure

* Address review comments and clean up Ref counting

* Make DriveIO static

* Use an enum state for ChipConnection

* Add init guards for ChipDeviceController

* Fix compile
diff --git a/config/ios/CHIPProjectConfig.h b/config/ios/CHIPProjectConfig.h
index fec5a21..e33adb7 100644
--- a/config/ios/CHIPProjectConfig.h
+++ b/config/ios/CHIPProjectConfig.h
@@ -29,8 +29,8 @@
 // Enable use of an ephemeral UDP source port for locally initiated CHIP exchanges.
 #define CHIP_CONFIG_ENABLE_EPHEMERAL_UDP_PORT 1
 
-// Enable UDP listening on demand in the CHIPDeviceManager
-#define CHIP_CONFIG_DEVICE_MGR_DEMAND_ENABLE_UDP 1
+// Enable UDP listening on demand in the CHIPDeviceController
+#define CHIP_CONFIG_DEVICE_CONTROLLER_DEMAND_ENABLE_UDP 1
 
 #define INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT 0
 
diff --git a/examples/wifi-echo/standalone/CHIPManager.cpp b/examples/wifi-echo/standalone/CHIPManager.cpp
deleted file mode 100644
index 5964f2a..0000000
--- a/examples/wifi-echo/standalone/CHIPManager.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- *    Copyright (c) 2020 Project CHIP Authors
- *    All rights reserved.
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- */
-
-/**
- *     @file
- *          Implementation for the CHIP Manager class.
- *
- *          This class provides various facilities through CHIP's inner
- *          implementation
- *
- */
-
-#include <errno.h>
-
-#include "CHIPManager.h"
-
-#include <support/CodeUtils.h>
-#include <support/ErrorStr.h>
-
-CHIP_ERROR ChipManager::InitLayers()
-{
-    CHIP_ERROR err = CHIP_NO_ERROR;
-
-    // Initialize the CHIP System Layer.
-    err = mSystem->Init(NULL);
-    if (err != CHIP_SYSTEM_NO_ERROR)
-    {
-        ChipLogError(DeviceManager, "SystemLayer initialization failed: %s", ErrorStr(err));
-    }
-    SuccessOrExit(err);
-
-    // Initialize the CHIP Inet layer.
-    err = mInet->Init(*mSystem, NULL);
-    if (err != INET_NO_ERROR)
-    {
-        ChipLogError(DeviceManager, "InetLayer initialization failed: %s", ErrorStr(err));
-    }
-    SuccessOrExit(err);
-
-exit:
-    return err;
-}
-
-System::Layer * ChipManager::SystemLayer()
-{
-    return this->mSystem;
-}
-
-Inet::InetLayer * ChipManager::InetLayer()
-{
-    return this->mInet;
-}
-
-void ChipManager::ServiceEvents()
-{
-
-    // Set the select timeout to 100ms
-    struct timeval aSleepTime;
-    aSleepTime.tv_sec  = 0;
-    aSleepTime.tv_usec = 100 * 1000;
-
-    static bool printed = false;
-
-    if (!printed)
-    {
-        {
-            ChipLogProgress(DeviceManager, "CHIP node ready to service events; PID: %d; PPID: %d\n", getpid(), getppid());
-            printed = true;
-        }
-    }
-    fd_set readFDs, writeFDs, exceptFDs;
-    int numFDs = 0;
-
-    FD_ZERO(&readFDs);
-    FD_ZERO(&writeFDs);
-    FD_ZERO(&exceptFDs);
-
-    if (mSystem->State() == System::kLayerState_Initialized)
-        mSystem->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
-
-    if (mInet->State == Inet::InetLayer::kState_Initialized)
-        mInet->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
-
-    int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime);
-    if (selectRes < 0)
-    {
-        ChipLogError(DeviceManager, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
-        return;
-    }
-
-    if (mSystem->State() == System::kLayerState_Initialized)
-    {
-        mSystem->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
-    }
-
-    if (mInet->State == Inet::InetLayer::kState_Initialized)
-    {
-        mInet->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
-    }
-}
diff --git a/examples/wifi-echo/standalone/Makefile b/examples/wifi-echo/standalone/Makefile
index a5654c0..8951aa6 100644
--- a/examples/wifi-echo/standalone/Makefile
+++ b/examples/wifi-echo/standalone/Makefile
@@ -34,7 +34,6 @@
 
 SRCS = \
     $(PROJECT_ROOT)/main.cpp \
-    $(PROJECT_ROOT)/CHIPManager.cpp \
     $(NULL)
 
 INC_DIRS = \
@@ -43,6 +42,7 @@
     $(CHIP_ROOT)/src/lib \
     $(CHIP_ROOT)/src/ \
     $(CHIP_ROOT)/src/system \
+    $(CHIP_ROOT)/src/controller \
     $(CHIP_ROOT)/config/standalone \
     $(NULL)
 
diff --git a/examples/wifi-echo/standalone/include/CHIPManager.h b/examples/wifi-echo/standalone/include/CHIPManager.h
deleted file mode 100644
index 996313f..0000000
--- a/examples/wifi-echo/standalone/include/CHIPManager.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *    Copyright (c) 2020 Project CHIP Authors
- *    All rights reserved.
- *
- *    Licensed under the Apache License, Version 2.0 (the "License");
- *    you may not use this file except in compliance with the License.
- *    You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing, software
- *    distributed under the License is distributed on an "AS IS" BASIS,
- *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *    See the License for the specific language governing permissions and
- *    limitations under the License.
- */
-
-/**
- *    @file
- *          Header for the CHIP Manager class.
- *
- *          This class provides various facilities through CHIP's inner
- *          implementation. It should be removed when a more accessible
- *          CHIP api is ready.
- *
- */
-
-#ifndef CHIP_MANAGER_H
-#define CHIP_MANAGER_H
-
-#include <new>
-
-#include <core/CHIPError.h>
-#include <inet/InetLayer.h>
-#include <inet/UDPEndPoint.h>
-#include <support/CHIPLogging.h>
-#include <system/SystemLayer.h>
-
-using namespace chip;
-
-class ChipManager
-{
-public:
-    // Initialize the System Layer and Init Layer
-    CHIP_ERROR InitLayers();
-    System::Layer * SystemLayer();
-    Inet::InetLayer * InetLayer();
-    void ServiceEvents();
-
-    ChipManager() : mSystem(new System::Layer()), mInet(new Inet::InetLayer()) {}
-
-private:
-    System::Layer * mSystem;
-    Inet::InetLayer * mInet;
-};
-
-#endif // CHIP_MANAGER_H
diff --git a/examples/wifi-echo/standalone/main.cpp b/examples/wifi-echo/standalone/main.cpp
index 2771ab8..7422624 100644
--- a/examples/wifi-echo/standalone/main.cpp
+++ b/examples/wifi-echo/standalone/main.cpp
@@ -18,7 +18,7 @@
 #include <support/CodeUtils.h>
 #include <inet/UDPEndPoint.h>
 
-#include "CHIPManager.h"
+#include <controller/CHIPDeviceController.h>
 
 #define SEND_DELAY 5
 
@@ -27,51 +27,40 @@
 
 static const char * PAYLOAD = "Message from Standalone CHIP echo client!";
 
-// UDP Endpoint Callbacks
-static void EchoResponse(IPEndPointBasis * endpoint, System::PacketBuffer * buffer, const IPPacketInfo * packet_info)
+// Device Manager Callbacks
+static void EchoResponse(chip::DeviceController::ChipDeviceController * deviceController, void * appReqState,
+                         System::PacketBuffer * buffer, const IPPacketInfo * packet_info)
 {
-    bool status = endpoint != NULL && buffer != NULL && packet_info != NULL;
+    char src_addr[INET_ADDRSTRLEN];
+    char dest_addr[INET_ADDRSTRLEN];
+    size_t data_len = buffer->DataLength();
 
-    if (status)
+    packet_info->SrcAddress.ToString(src_addr, sizeof(src_addr));
+    packet_info->DestAddress.ToString(dest_addr, sizeof(dest_addr));
+
+    printf("UDP packet received from %s:%u to %s:%u (%zu bytes)\n", src_addr, packet_info->SrcPort, dest_addr,
+           packet_info->DestPort, static_cast<size_t>(buffer->DataLength()));
+
+    // attempt to print the incoming message
+    char msg_buffer[data_len];
+    msg_buffer[data_len] = 0; // Null-terminate whatever we received and treat like a string...
+    memcpy(msg_buffer, buffer->Start(), data_len);
+    int compare = strncmp(msg_buffer, PAYLOAD, data_len);
+    if (compare == 0)
     {
-        char src_addr[INET_ADDRSTRLEN];
-        char dest_addr[INET_ADDRSTRLEN];
-        size_t data_len = buffer->DataLength();
-
-        packet_info->SrcAddress.ToString(src_addr, sizeof(src_addr));
-        packet_info->DestAddress.ToString(dest_addr, sizeof(dest_addr));
-
-        printf("UDP packet received from %s:%u to %s:%u (%zu bytes)\n", src_addr, packet_info->SrcPort, dest_addr,
-               packet_info->DestPort, static_cast<size_t>(buffer->DataLength()));
-
-        // attempt to print the incoming message
-        char msg_buffer[data_len];
-        msg_buffer[data_len] = 0; // Null-terminate whatever we received and treat like a string...
-        memcpy(msg_buffer, buffer->Start(), data_len);
-        int compare = strncmp(msg_buffer, PAYLOAD, data_len);
-        if (compare == 0)
-        {
-            printf("Got expected Message...\n");
-        }
-        else
-        {
-            printf("Didn't get the expected Echo. Compare: %d\n", compare);
-            printf("\nSend: %s \nRecv: %s\n", PAYLOAD, msg_buffer);
-        }
+        printf("Got expected Message...\n");
+    }
+    else
+    {
+        printf("Didn't get the expected Echo. Compare: %d\n", compare);
+        printf("\nSend: %s \nRecv: %s\n", PAYLOAD, msg_buffer);
     }
 
-    if (!status)
-    {
-        printf("Received data but couldn't process it...\n");
-    }
-
-    if (buffer != NULL)
-    {
-        System::PacketBuffer::Free(buffer);
-    }
+    System::PacketBuffer::Free(buffer);
 }
 
-static void Error(IPEndPointBasis * ep, INET_ERROR error, const IPPacketInfo * pi)
+static void ReceiveError(chip::DeviceController::ChipDeviceController * deviceController, void * appReqState, CHIP_ERROR error,
+                         const IPPacketInfo * pi)
 {
     printf("ERROR: %s\n Got UDP error\n", ErrorStr(error));
 }
@@ -88,9 +77,6 @@
     snprintf((char *) buffer->Start(), payload_len + 1, "%s", PAYLOAD);
     buffer->SetDataLength(payload_len);
 
-    CHIP_ERROR err;
-    UDPEndPoint * endpoint;
-
     std::string host_ip;
     std::cout << "Please, enter the Echo Host's IP Address: ";
     std::getline(std::cin, host_ip);
@@ -116,42 +102,20 @@
         return -1;
     }
 
-    ChipManager chipMgr = ChipManager();
-    chipMgr.InitLayers();
+    chip::DeviceController::ChipDeviceController * controller = new chip::DeviceController::ChipDeviceController();
+    controller->Init();
 
-    err = chipMgr.InetLayer()->NewUDPEndPoint(&endpoint);
-    if (err != CHIP_NO_ERROR)
-    {
-        printf("ERROR: %s\n Couldn't create UDP Endpoint, server will not start\n", ErrorStr(err));
-        return -1;
-    }
-
-    err = endpoint->Bind(kIPAddressType_IPv4, IPAddress::Any, port);
-    if (err != CHIP_NO_ERROR)
-    {
-        printf("ERROR: %s\n Bind failed\n", ErrorStr(err));
-        return -1;
-    }
-
-    err = endpoint->Listen();
-    if (err != CHIP_NO_ERROR)
-    {
-        printf("ERROR: %s\n Listen failed\n", ErrorStr(err));
-        return -1;
-    }
-
-    endpoint->OnMessageReceived = EchoResponse;
-    endpoint->OnReceiveError    = Error;
+    controller->ConnectDevice(1, host_addr, NULL, EchoResponse, ReceiveError, port);
 
     // Run the client
     while (1)
     {
         // Send calls release on this buffer, so bump up the ref because we want to reuse it
         buffer->AddRef();
-        endpoint->SendTo(host_addr, port, buffer);
+        controller->SendMessage(NULL, buffer);
         printf("Msg sent to server at %s:%d\n", host_ip.c_str(), port);
 
-        chipMgr.ServiceEvents();
+        controller->ServiceEvents();
 
         sleep(SEND_DELAY);
     }
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
new file mode 100644
index 0000000..8f89ad5
--- /dev/null
+++ b/src/controller/CHIPDeviceController.cpp
@@ -0,0 +1,276 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *    Copyright (c) 2013-2017 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      Implementation of CHIP Device Controller, a common class
+ *      that implements discovery, pairing and provisioning of Weave
+ *      devices.
+ *
+ */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <time.h>
+
+#include <core/CHIPCore.h>
+#include <support/Base64.h>
+#include <support/CodeUtils.h>
+#include <core/CHIPEncoding.h>
+
+#include <controller/CHIPDeviceController.h>
+#include <support/logging/CHIPLogging.h>
+#include <support/ErrorStr.h>
+#include <support/TimeUtils.h>
+
+namespace chip {
+namespace DeviceController {
+
+using namespace chip::Encoding;
+
+ChipDeviceController::ChipDeviceController()
+{
+    mState      = kState_NotInitialized;
+    AppState    = NULL;
+    mConState   = kConnectionState_NotConnected;
+    mDeviceCon  = NULL;
+    mCurReqMsg  = NULL;
+    mOnError    = NULL;
+    mDeviceAddr = IPAddress::Any;
+    mDevicePort = CHIP_PORT;
+    mDeviceId   = 0;
+    memset(&mOnComplete, 0, sizeof(mOnComplete));
+}
+
+CHIP_ERROR ChipDeviceController::Init()
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    VerifyOrExit(mState == kState_NotInitialized, err = CHIP_ERROR_INCORRECT_STATE);
+
+    mSystemLayer = new System::Layer();
+    mInetLayer   = new Inet::InetLayer();
+
+    // Initialize the CHIP System Layer.
+    err = mSystemLayer->Init(NULL);
+    if (err != CHIP_SYSTEM_NO_ERROR)
+    {
+        ChipLogError(Controller, "SystemLayer initialization failed: %s", ErrorStr(err));
+    }
+    SuccessOrExit(err);
+
+    // Initialize the CHIP Inet layer.
+    err = mInetLayer->Init(*mSystemLayer, NULL);
+    if (err != INET_NO_ERROR)
+    {
+        ChipLogError(Controller, "InetLayer initialization failed: %s", ErrorStr(err));
+    }
+    SuccessOrExit(err);
+
+    mState = kState_Initialized;
+
+exit:
+    return err;
+}
+
+CHIP_ERROR ChipDeviceController::Shutdown()
+{
+    if (mState != kState_Initialized)
+    {
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+
+    CHIP_ERROR err = CHIP_NO_ERROR;
+    mState         = kState_NotInitialized;
+
+    if (mDeviceCon != NULL)
+    {
+        mDeviceCon->Close();
+        delete mDeviceCon;
+        mDeviceCon = NULL;
+    }
+    mSystemLayer->Shutdown();
+    mInetLayer->Shutdown();
+    delete mSystemLayer;
+    delete mInetLayer;
+    mSystemLayer = NULL;
+    mInetLayer   = NULL;
+
+    mConState = kConnectionState_NotConnected;
+    memset(&mOnComplete, 0, sizeof(mOnComplete));
+    mOnError = NULL;
+
+    return err;
+}
+
+CHIP_ERROR ChipDeviceController::ConnectDevice(uint64_t deviceId, IPAddress deviceAddr, void * appReqState,
+                                               MessageReceiveHandler onMessageReceived, ErrorHandler onError, uint16_t devicePort)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    if (mState != kState_Initialized || mDeviceCon != NULL || mConState != kConnectionState_NotConnected)
+    {
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+
+    mDeviceId    = deviceId;
+    mDeviceAddr  = deviceAddr;
+    mDevicePort  = devicePort;
+    mAppReqState = appReqState;
+    mDeviceCon   = new ChipConnection();
+
+    mDeviceCon->Init(mInetLayer);
+    err = mDeviceCon->Connect(mDeviceId, mDeviceAddr, mDevicePort);
+    SuccessOrExit(err);
+
+    mDeviceCon->OnMessageReceived = OnReceiveMessage;
+    mDeviceCon->OnReceiveError    = OnReceiveError;
+    mDeviceCon->AppState          = this;
+
+    mOnComplete.Response = onMessageReceived;
+    mOnError             = onError;
+
+    mConState = kConnectionState_Connected;
+
+exit:
+    if (err != CHIP_NO_ERROR && mDeviceCon != NULL)
+    {
+        mDeviceCon->Close();
+        delete mDeviceCon;
+        mDeviceCon = NULL;
+    }
+    return err;
+}
+
+CHIP_ERROR ChipDeviceController::DisconnectDevice()
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    if (mState != kState_Initialized || mConState != kConnectionState_Connected)
+    {
+        return CHIP_ERROR_INCORRECT_STATE;
+    }
+
+    err = mDeviceCon->Close();
+    delete mDeviceCon;
+    mDeviceCon = NULL;
+    mConState  = kConnectionState_NotConnected;
+    return err;
+};
+
+CHIP_ERROR ChipDeviceController::SendMessage(void * appReqState, PacketBuffer * buffer)
+{
+    CHIP_ERROR err = CHIP_ERROR_INCORRECT_STATE;
+
+    mAppReqState = appReqState;
+    if (mConState == kConnectionState_Connected)
+    {
+        err = mDeviceCon->SendMessage(buffer);
+    }
+
+    return err;
+}
+
+void ChipDeviceController::ServiceEvents()
+{
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+
+    if (mState != kState_Initialized)
+    {
+        return;
+    }
+    // Set the select timeout to 100ms
+    struct timeval aSleepTime;
+    aSleepTime.tv_sec  = 0;
+    aSleepTime.tv_usec = 100 * 1000;
+
+    static bool printed = false;
+
+    if (!printed)
+    {
+        {
+            ChipLogProgress(Controller, "CHIP node ready to service events; PID: %d; PPID: %d\n", getpid(), getppid());
+            printed = true;
+        }
+    }
+    fd_set readFDs, writeFDs, exceptFDs;
+    int numFDs = 0;
+
+    FD_ZERO(&readFDs);
+    FD_ZERO(&writeFDs);
+    FD_ZERO(&exceptFDs);
+
+    if (mSystemLayer->State() == System::kLayerState_Initialized)
+        mSystemLayer->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
+
+    if (mInetLayer->State == Inet::InetLayer::kState_Initialized)
+        mInetLayer->PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
+
+    int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime);
+    if (selectRes < 0)
+    {
+        ChipLogError(Controller, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
+        return;
+    }
+
+    if (mSystemLayer->State() == System::kLayerState_Initialized)
+    {
+        mSystemLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
+    }
+
+    if (mInetLayer->State == Inet::InetLayer::kState_Initialized)
+    {
+        mInetLayer->HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
+    }
+#endif
+}
+
+void ChipDeviceController::ClearRequestState()
+{
+    if (mCurReqMsg != NULL)
+    {
+        PacketBuffer::Free(mCurReqMsg);
+        mCurReqMsg = NULL;
+    }
+}
+
+void ChipDeviceController::OnReceiveMessage(ChipConnection * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo)
+{
+    ChipDeviceController * mgr = (ChipDeviceController *) con->AppState;
+    if (mgr->mConState == kConnectionState_Connected && mgr->mOnComplete.Response != NULL && pktInfo != NULL)
+    {
+        mgr->mOnComplete.Response(mgr, mgr->mAppReqState, msgBuf, pktInfo);
+    }
+}
+
+void ChipDeviceController::OnReceiveError(ChipConnection * con, CHIP_ERROR err, const IPPacketInfo * pktInfo)
+{
+    ChipDeviceController * mgr = (ChipDeviceController *) con->AppState;
+    if (mgr->mConState == kConnectionState_Connected && mgr->mOnError != NULL && pktInfo != NULL)
+    {
+        mgr->mOnError(mgr, mgr->mAppReqState, err, pktInfo);
+    }
+}
+
+} // namespace DeviceController
+} // namespace chip
diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
new file mode 100644
index 0000000..9aacf8c
--- /dev/null
+++ b/src/controller/CHIPDeviceController.h
@@ -0,0 +1,147 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *    Copyright (c) 2013-2017 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      Declaration of CHIP Device Controller, a common class
+ *      that implements connecting and messaging and will later
+ *      be expanded to support discovery, pairing and
+ *      provisioning of CHIP  devices.
+ *
+ */
+
+#ifndef __CHIPDEVICECONTROLLER_H
+#define __CHIPDEVICECONTROLLER_H
+
+#include <support/DLLUtil.h>
+#include <core/CHIPCore.h>
+#include <core/CHIPTLV.h>
+#include <core/CHIPConnection.h>
+
+namespace chip {
+namespace DeviceController {
+
+class ChipDeviceController;
+
+extern "C" {
+typedef void (*CompleteHandler)(ChipDeviceController * deviceController, void * appReqState);
+typedef void (*ErrorHandler)(ChipDeviceController * deviceController, void * appReqState, CHIP_ERROR err,
+                             const IPPacketInfo * pktInfo);
+typedef void (*MessageReceiveHandler)(ChipDeviceController * deviceController, void * appReqState, PacketBuffer * payload,
+                                      const IPPacketInfo * pktInfo);
+};
+
+class DLL_EXPORT ChipDeviceController
+{
+public:
+    ChipDeviceController();
+
+    void * AppState;
+
+    CHIP_ERROR Init();
+    CHIP_ERROR Shutdown();
+
+    // ----- Connection Management -----
+    /**
+     * @brief
+     *   Connect to a CHIP device at a given address and an optional port
+     *
+     * @param deviceId              A device identifier. Currently unused and can be set to any value
+     * @param deviceAddr            The IPAddress of the requested Device
+     * @param appReqState           Application specific context to be passed back when a message is received or on error
+     * @param onMessageReceived     Callback for when a message is received
+     * @param onError               Callback for when an error occurs
+     * @param devicePort            [Optional] The CHIP Device's port, defaults to CHIP_PORT
+     * @return CHIP_ERROR           The connection status
+     */
+    CHIP_ERROR ConnectDevice(uint64_t deviceId, IPAddress deviceAddr, void * appReqState, MessageReceiveHandler onMessageReceived,
+                             ErrorHandler onError, uint16_t devicePort = CHIP_PORT);
+
+    // ----- Messaging -----
+    /**
+     * @brief
+     *   Send a message to a connected CHIP device
+     *
+     * @param appReqState   Application specific context to be passed back when a message is received or on error
+     * @param buffer        The Data Buffer to trasmit to the deviec
+     * @return CHIP_ERROR   The return status
+     */
+    CHIP_ERROR SendMessage(void * appReqState, PacketBuffer * buffer);
+
+    /**
+     * @brief
+     *   Disconnect from a connected device
+     *
+     * @return CHIP_ERROR   If the device was disconnected successfully
+     */
+    CHIP_ERROR DisconnectDevice();
+
+    // ----- IO -----
+    /**
+     * @brief
+     *   Allow the CHIP Stack to process any pending events
+     *   This can be called in an event handler loop to tigger callbacks within the CHIP stack
+     *   Note - Some platforms might need to implement their own event handler
+     */
+    void ServiceEvents();
+
+private:
+    enum
+    {
+        kState_NotInitialized = 0,
+        kState_Initialized    = 1
+    } mState;
+
+    enum ConnectionState
+    {
+        kConnectionState_NotConnected = 0,
+        kConnectionState_Connected    = 1,
+    };
+
+    System::Layer * mSystemLayer;
+    Inet::InetLayer * mInetLayer;
+    ChipConnection * mDeviceCon;
+
+    ConnectionState mConState;
+    void * mAppReqState;
+
+    union
+    {
+        CompleteHandler General;
+        MessageReceiveHandler Response;
+    } mOnComplete;
+
+    ErrorHandler mOnError;
+    PacketBuffer * mCurReqMsg;
+
+    uint64_t mDeviceId;
+    IPAddress mDeviceAddr;
+    uint16_t mDevicePort;
+
+    void ClearRequestState();
+    void ClearOpState();
+
+    static void OnReceiveMessage(ChipConnection * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo);
+    static void OnReceiveError(ChipConnection * con, CHIP_ERROR err, const IPPacketInfo * pktInfo);
+};
+
+} // namespace DeviceController
+} // namespace chip
+
+#endif // __CHIPDEVICECONTROLLER_H
diff --git a/src/controller/DeviceController.am b/src/controller/DeviceController.am
new file mode 100644
index 0000000..306a5a6
--- /dev/null
+++ b/src/controller/DeviceController.am
@@ -0,0 +1,47 @@
+#
+#    Copyright (c) 2020 Project CHIP Authors
+#    Copyright (c) 2014-2017 Nest Labs, Inc.
+#    All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License");
+#    you may not use this file except in compliance with the License.
+#    You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+#
+
+#
+#    Description:
+#      This file is the GNU automake header for the CHIP DeviceController
+#      library sources.
+#
+
+# The ChipDeviceController is designed for use in client applications (e.g.
+# mobiles) or within resource rich devices (typically linux based).  As
+# such it depends on the presense of a generalized heap (malloc el al).
+# In contexts where this isn't available, simply skip building the Device
+# Controller code altogether.
+
+CHIP_BUILD_DEVICE_CONTROLLER_SOURCE_FILES                                       = \
+    $(NULL)
+
+CHIP_BUILD_DEVICE_CONTROLLER_HEADER_FILES                                       = \
+$(NULL)
+
+if CONFIG_HAVE_HEAP
+
+CHIP_BUILD_DEVICE_CONTROLLER_SOURCE_FILES                                      += \
+    @top_builddir@/src/controller/CHIPDeviceController.cpp   \
+    $(NULL)
+
+CHIP_BUILD_DEVICE_CONTROLLER_HEADER_FILES                                      += \
+    @top_builddir@/src/controller/CHIPDeviceController.h   \
+    $(NULL)
+
+endif # CONFIG_HAVE_HEAP
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 9104897..2d29d90 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -30,15 +30,17 @@
 
 # Pull in the sources that comprise the CHIP library.
 
-include ../system/SystemLayer.am
-include ../inet/InetLayer.am
 include ../ble/BleLayer.am
+include ../controller/DeviceController.am
+include ../inet/InetLayer.am
+include ../system/SystemLayer.am
 include core/CoreLayer.am
 include support/SupportLayer.am
 
 EXTRA_DIST                          =                         \
     $(CHIP_BUILD_CORE_LAYER_HEADER_FILES)                     \
-    $(CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES)
+    $(CHIP_BUILD_SUPPORT_LAYER_HEADER_FILES)                  \
+    $(CHIP_BUILD_DEVICE_CONTROLLER_HEADER_FILES)
 
 lib_LIBRARIES                       = libCHIP.a
 
@@ -61,6 +63,7 @@
 libCHIP_a_SOURCES                  += $(CHIP_BUILD_INET_LAYER_SOURCE_FILES)
 libCHIP_a_SOURCES                  += $(CHIP_BUILD_CORE_LAYER_SOURCE_FILES)
 libCHIP_a_SOURCES                  += $(CHIP_BUILD_SUPPORT_LAYER_SOURCE_FILES)
+libCHIP_a_SOURCES                  += $(CHIP_BUILD_DEVICE_CONTROLLER_SOURCE_FILES)
 
 if CONFIG_NETWORK_LAYER_BLE
 libCHIP_a_SOURCES                  += $(CHIP_BUILD_BLE_LAYER_SOURCE_FILES)
diff --git a/src/lib/core/CHIPConnection.cpp b/src/lib/core/CHIPConnection.cpp
new file mode 100644
index 0000000..5c0febe
--- /dev/null
+++ b/src/lib/core/CHIPConnection.cpp
@@ -0,0 +1,258 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *    Copyright (c) 2013-2017 Nest Labs, Inc.
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements the CHIP Connection object that maintains a UDP connection.
+ *      TODO This class should be extended to support TCP as well...
+ *
+ */
+
+#include <core/CHIPConnection.h>
+#include <support/CodeUtils.h>
+#include <support/logging/CHIPLogging.h>
+
+#include <inttypes.h>
+
+namespace chip {
+
+ChipConnection::ChipConnection() : mState(kState_NotReady), mRefCount(1)
+{
+    mState            = kState_NotReady;
+    mRefCount         = 1;
+    mUDPEndPoint      = NULL;
+    mRefCount         = 1;
+    OnMessageReceived = NULL;
+    OnReceiveError    = NULL;
+    mPeerNodeId       = 0;
+    mPeerAddr         = IPAddress::Any;
+    mPeerPort         = 0;
+}
+
+void ChipConnection::Init(Inet::InetLayer * inetLayer)
+{
+    if (mState != kState_NotReady)
+    {
+        return;
+    }
+
+    mInetLayer = inetLayer;
+    mState     = kState_ReadyToConnect;
+
+} // namespace chip
+
+CHIP_ERROR ChipConnection::Connect(uint64_t peerNodeId, const IPAddress & peerAddr, uint16_t peerPort)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    VerifyOrExit(mState == kState_ReadyToConnect, err = CHIP_ERROR_INCORRECT_STATE);
+
+    mPeerNodeId = peerNodeId;
+    mPeerAddr   = peerAddr;
+    mPeerPort   = (peerPort != 0) ? peerPort : CHIP_PORT;
+
+    // Bump the reference count when we start the connection process. The corresponding decrement happens when the
+    // DoClose() method is called. This ensures the object stays live while there's the possibility of a callback
+    // happening from an underlying layer.
+    mRefCount++;
+
+    ChipLogProgress(Inet, "Connection start %016llX", peerNodeId);
+    err = DoConnect();
+exit:
+    return err;
+}
+
+CHIP_ERROR ChipConnection::DoConnect()
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    err = mInetLayer->NewUDPEndPoint(&mUDPEndPoint);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogProgress(Inet, "Error: %s\n Couldn't create connection\n", ErrorStr(err));
+        return err;
+    }
+
+    err = mUDPEndPoint->Bind(mPeerAddr.Type(), IPAddress::Any, CHIP_PORT);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogProgress(Inet, "Error: %s\n Bind failed\n", ErrorStr(err));
+        return err;
+    }
+
+    err = mUDPEndPoint->Listen();
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogProgress(Inet, "Error: %s\n Listen failed\n", ErrorStr(err));
+        return err;
+    }
+
+    mUDPEndPoint->AppState          = this;
+    mUDPEndPoint->OnMessageReceived = HandleDataReceived;
+    mUDPEndPoint->OnReceiveError    = HandleReceiveError;
+
+#if CHIP_PROGRESS_LOGGING
+    {
+        char ipAddrStr[64];
+        mPeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+        ChipLogProgress(Inet, "Connection started %s %d", ipAddrStr, (int) mPeerPort);
+    }
+#endif
+    mState = kState_Connected;
+
+    return err;
+}
+
+CHIP_ERROR ChipConnection::SendMessage(PacketBuffer * msgBuf)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    VerifyOrExit(StateAllowsSend(), err = CHIP_ERROR_INCORRECT_STATE);
+
+    IPPacketInfo addrInfo;
+    addrInfo.Clear();
+    addrInfo.DestAddress = mPeerAddr;
+    addrInfo.DestPort    = mPeerPort;
+
+    err    = mUDPEndPoint->SendMsg(&addrInfo, msgBuf);
+    msgBuf = NULL;
+
+exit:
+    if (msgBuf != NULL)
+    {
+        PacketBuffer::Free(msgBuf);
+        msgBuf = NULL;
+    }
+
+    return err;
+}
+
+void ChipConnection::HandleDataReceived(IPEndPointBasis * endPoint, chip::System::PacketBuffer * msg, const IPPacketInfo * pktInfo)
+{
+    UDPEndPoint * udpEndPoint   = static_cast<UDPEndPoint *>(endPoint);
+    ChipConnection * connection = (ChipConnection *) udpEndPoint->AppState;
+
+    // TODO this where where messages should be decoded
+    if (connection->StateAllowsReceive() && msg != NULL)
+    {
+        connection->OnMessageReceived(connection, msg, pktInfo);
+    }
+}
+
+void ChipConnection::HandleReceiveError(IPEndPointBasis * endPoint, CHIP_ERROR err, const IPPacketInfo * pktInfo)
+{
+    UDPEndPoint * udpEndPoint   = static_cast<UDPEndPoint *>(endPoint);
+    ChipConnection * connection = (ChipConnection *) udpEndPoint->AppState;
+    if (connection->StateAllowsReceive())
+    {
+        connection->OnReceiveError(connection, err, pktInfo);
+    }
+}
+/**
+ *  Performs a non-blocking graceful close of the UDP based ChipConnection, delivering any
+ *  remaining outgoing data before resetting the connection.
+ *
+ *  This method provides no strong guarantee that any outgoing message not acknowledged at the application
+ *  protocol level has been received by the remote peer.
+ *
+ *  Once Close() has been called, the ChipConnection object can no longer be used for further communication.
+ *
+ *  Calling Close() decrements the reference count associated with the ChipConnection object, whether or not
+ *  the connection is open/active at the time the method is called.  If this results in the reference count
+ *  reaching zero, the resources associated with the connection object are freed.  When this happens, the
+ *  application must have no further interactions with the object.
+ *
+ *  @sa Shutdown(), Abort(), Retain() and Release().
+ *
+ *  @return #CHIP_NO_ERROR unconditionally.
+ *
+ */
+CHIP_ERROR ChipConnection::Close()
+{
+    // Perform a graceful close.
+    DoClose(CHIP_NO_ERROR);
+
+    // Decrement the ref count that was added when the ChipConnection object
+    // was allocated.
+    VerifyOrDie(mRefCount != 0);
+    mRefCount--;
+
+    return CHIP_NO_ERROR;
+}
+
+void ChipConnection::DoClose(CHIP_ERROR err)
+{
+    if (mState != kState_Closed)
+    {
+        if (mUDPEndPoint != NULL)
+        {
+            if (err == CHIP_NO_ERROR)
+            {
+                mUDPEndPoint->Close();
+            }
+            mUDPEndPoint->Free();
+            mUDPEndPoint = NULL;
+        }
+    }
+    uint8_t oldState = mState;
+    mState           = kState_Closed;
+    ChipLogProgress(Inet, "Connection closed %ld", (long) err);
+
+    // Decrement the ref count that was added when the connection started.
+    if (oldState != kState_ReadyToConnect && oldState != kState_Closed)
+    {
+        VerifyOrDie(mRefCount != 0);
+        mRefCount--;
+    }
+}
+
+/**
+ * Reserve a reference to the ChipConnection object.
+ *
+ * The Retain() method increments the reference count associated with the ChipConnection object.  For every
+ * call to Retain(), the application is responsible for making a corresponding call to either Release(), Close()
+ * or Abort().
+ */
+void ChipConnection::Retain()
+{
+    VerifyOrDie(mRefCount < UINT8_MAX);
+    ++mRefCount;
+}
+
+/**
+ *  Decrement the reference count on the ChipConnection object.
+ *
+ *  The Release() method decrements the reference count associated with the ChipConnection object.  If
+ *  this results in the reference count reaching zero, the connection is closed and the connection object
+ *  is freed.  When this happens, the application must have no further interactions with the object.
+ */
+void ChipConnection::Release()
+{
+    // If the only reference that will remain after this call is the one that was automatically added
+    // when the connection started, close the connection.
+    if (mRefCount == 2 && mState != kState_ReadyToConnect && mState != kState_Closed)
+    {
+        DoClose(CHIP_NO_ERROR);
+    }
+
+    VerifyOrDie(mRefCount != 0);
+    mRefCount--;
+}
+
+} // namespace chip
\ No newline at end of file
diff --git a/src/lib/core/CHIPConnection.h b/src/lib/core/CHIPConnection.h
new file mode 100644
index 0000000..74e2f7f
--- /dev/null
+++ b/src/lib/core/CHIPConnection.h
@@ -0,0 +1,152 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file defines the CHIP Connection object that maintains a UDP connection.
+ *      It binds to any avaiable local addr and port and begins listening.
+ *      TODO This class should be extended to support TCP as well...
+ *
+ */
+
+#ifndef __CHIPCONNECTION_H__
+#define __CHIPCONNECTION_H__
+
+#include <core/CHIPCore.h>
+#include <inet/IPAddress.h>
+#include <inet/IPEndPointBasis.h>
+
+namespace chip {
+
+using namespace System;
+
+class DLL_EXPORT ChipConnection
+{
+public:
+    /**
+     *  @enum State
+     *
+     *  @brief
+     *    The State of the CHIP connection object.
+     *
+     */
+    enum State
+    {
+        // TODO need more modes when TCP support is added
+        kState_NotReady       = 0, /**< State before initialization. */
+        kState_ReadyToConnect = 1, /**< State after initialization of the CHIP connection. */
+        kState_Connected      = 2, /**< State when the connection has been established. */
+        kState_Closed         = 3  /**< State when the connection is closed. */
+    };
+
+    void * AppState; /**< A pointer to the application-specific state object. */
+
+    /**
+     * @brief
+     *   Initialize a CHIP Connection
+     *
+     * @param inetLayer     A pointer to the <tt>chip::Inet::InetLayer</tt>
+     */
+    void Init(Inet::InetLayer * inetLayer);
+
+    /**
+     * @brief
+     *   Attempt to establish a connection to the given peer
+     *
+     * @param peerNodeId    Currently unused; a NodeId to identify this peer
+     * @param peerAddr      The <tt>chip::Inet::IPAddress</tt> of the requested peer
+     * @param peerPort      The port of the requested peer
+     * @return CHIP_ERROR   The connection result
+     */
+    CHIP_ERROR Connect(uint64_t peerNodeId, const IPAddress & peerAddr, uint16_t peerPort = 0);
+
+    /**
+     * @brief
+     *   Send a message to the currently connected peer
+     *
+     * @param msgBuf        A PacketBuffer containing the message to be sent
+     * @return CHIP_ERROR   The send result
+     *
+     * @details
+     *   This method calls <tt>chip::System::PacketBuffer::Free</tt> on
+     *   behalf of the caller regardless of the return status.
+     */
+    CHIP_ERROR SendMessage(PacketBuffer * msgBuf);
+
+    /**
+     * @brief
+     *   Close an existing connection. Once close is called, the ChipConnection object can no longer be used
+     *
+     * @return CHIP_ERROR   The close result
+     */
+    CHIP_ERROR Close(void);
+
+    void Retain(void);
+    void Release(void);
+
+    /**
+     *  This function is the application callback that is invoked when a message is received over a
+     *  Chip connection.
+     *
+     *  @param[in]    con           A pointer to the ChipConnection object.
+     *
+     *  @param[in]    msgBuf        A pointer to the PacketBuffer object holding the message.
+     *
+     *  @param[in]    pktInfo       A pointer to the IPPacketInfo object carrying sender details.
+     *
+     */
+    typedef void (*MessageReceiveHandler)(ChipConnection * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo);
+    MessageReceiveHandler OnMessageReceived;
+
+    /**
+     *  This function is the application callback invoked upon encountering an error when receiving
+     *  a Chip message.
+     *
+     *  @param[in]     con            A pointer to the ChipConnection object.
+     *
+     *  @param[in]     err            The CHIP_ERROR encountered when receiving data over the connection.
+     *
+     *  @param[in]    pktInfo         A pointer to the IPPacketInfo object carrying sender details.
+     *
+     */
+    typedef void (*ReceiveErrorHandler)(ChipConnection * con, CHIP_ERROR err, const IPPacketInfo * pktInfo);
+    ReceiveErrorHandler OnReceiveError;
+
+    ChipConnection();
+
+private:
+    Inet::InetLayer * mInetLayer;
+    UDPEndPoint * mUDPEndPoint;
+    uint64_t mPeerNodeId;
+    IPAddress mPeerAddr;
+    uint16_t mPeerPort;
+    State mState;
+    uint8_t mRefCount;
+
+    CHIP_ERROR DoConnect();
+    void DoClose(CHIP_ERROR err);
+    bool StateAllowsSend(void) const { return mState == kState_Connected; }
+    bool StateAllowsReceive(void) const { return mState == kState_Connected; }
+
+    static void HandleDataReceived(IPEndPointBasis * endPoint, chip::System::PacketBuffer * msg, const IPPacketInfo * pktInfo);
+    static void HandleReceiveError(IPEndPointBasis * endPoint, INET_ERROR err, const IPPacketInfo * pktInfo);
+};
+
+} // namespace chip
+
+#endif // __CHIPCONNECTION_H__
\ No newline at end of file
diff --git a/src/lib/core/CoreLayer.am b/src/lib/core/CoreLayer.am
index 4fd1b8c..4b2a4a5 100644
--- a/src/lib/core/CoreLayer.am
+++ b/src/lib/core/CoreLayer.am
@@ -32,6 +32,7 @@
     core/CHIPTLVWriter.cpp                                  \
     core/CHIPTLVUpdater.cpp                                 \
     core/CHIPKeyIds.cpp                                     \
+    core/CHIPConnection.cpp                                 \
     $(NULL)
 
 CHIP_BUILD_CORE_LAYER_HEADER_FILES                        = \
@@ -50,4 +51,5 @@
     core/CHIPTimeConfig.h                                   \
     core/CHIPTunnelConfig.h                                 \
     core/CHIPKeyIds.h                                       \
+    core/CHIPConnection.h                                   \
     $(NULL)
diff --git a/src/lib/core/tests/Makefile.am b/src/lib/core/tests/Makefile.am
index b7c5d31..45a899b 100644
--- a/src/lib/core/tests/Makefile.am
+++ b/src/lib/core/tests/Makefile.am
@@ -43,6 +43,7 @@
 libCoreTests_a_SOURCES                                = \
     TestCHIPErrorStr.cpp                                \
     TestCHIPTLV.cpp                                     \
+    TestCHIPConnection.cpp                              \
     $(NULL)
 
 libCoreTests_adir                                     = $(includedir)
@@ -86,6 +87,7 @@
 check_PROGRAMS                                        = \
     TestCHIPErrorStr                                    \
     TestCHIPTLV                                         \
+    TestCHIPConnection                                  \
     $(NULL)
 
 # Test applications and scripts that should be built and run when the
@@ -109,6 +111,8 @@
 TestCHIPTLV_SOURCES                                   = TestCHIPTLVDriver.cpp
 TestCHIPTLV_LDADD                                     = $(COMMON_LDADD)
 
+TestCHIPConnection_SOURCES                            = TestCHIPConnectionDriver.cpp
+TestCHIPConnection_LDADD                              = $(COMMON_LDADD)
 #
 # Foreign make dependencies
 #
diff --git a/src/lib/core/tests/TestCHIPConnection.cpp b/src/lib/core/tests/TestCHIPConnection.cpp
new file mode 100644
index 0000000..db9ffd2
--- /dev/null
+++ b/src/lib/core/tests/TestCHIPConnection.cpp
@@ -0,0 +1,260 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements unit tests for the CHIPConnection implementation.
+ */
+
+#include "TestCore.h"
+
+#include <nlbyteorder.h>
+#include <nlunit-test.h>
+#include <errno.h>
+
+#include <core/CHIPCore.h>
+#include <core/CHIPConnection.h>
+
+#include <support/CodeUtils.h>
+
+using namespace chip;
+
+static int Initialize(void * aContext);
+static int Finalize(void * aContext);
+
+struct TestContext
+{
+    nlTestSuite * mSuite;
+    System::Layer mSystemLayer;
+    InetLayer mInetLayer;
+};
+
+struct TestContext sContext;
+
+static const char PAYLOAD[] = "Hello!";
+
+static void MessageReceiveHandler(ChipConnection * con, PacketBuffer * msgBuf, const IPPacketInfo * pktInfo)
+{
+    size_t data_len = msgBuf->DataLength();
+
+    int compare = memcmp(msgBuf->Start(), PAYLOAD, data_len);
+    NL_TEST_ASSERT(reinterpret_cast<nlTestSuite *>(con->AppState), compare == 0);
+};
+
+static void ReceiveErrorHandler(ChipConnection * con, CHIP_ERROR err, const IPPacketInfo * pktInfo)
+{
+    NL_TEST_ASSERT(reinterpret_cast<nlTestSuite *>(con->AppState), false);
+};
+
+static void DriveIO(TestContext & ctx)
+{
+#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
+    // Set the select timeout to 100ms
+    struct timeval aSleepTime;
+    aSleepTime.tv_sec  = 0;
+    aSleepTime.tv_usec = 100 * 1000;
+
+    fd_set readFDs, writeFDs, exceptFDs;
+    int numFDs = 0;
+
+    FD_ZERO(&readFDs);
+    FD_ZERO(&writeFDs);
+    FD_ZERO(&exceptFDs);
+
+    if (ctx.mSystemLayer.State() == System::kLayerState_Initialized)
+        ctx.mSystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
+
+    if (ctx.mInetLayer.State == Inet::InetLayer::kState_Initialized)
+        ctx.mInetLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime);
+
+    int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime);
+    if (selectRes < 0)
+    {
+        printf("select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno)));
+        NL_TEST_ASSERT(ctx.mSuite, false);
+        return;
+    }
+
+    if (ctx.mSystemLayer.State() == System::kLayerState_Initialized)
+    {
+        ctx.mSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
+    }
+
+    if (ctx.mInetLayer.State == Inet::InetLayer::kState_Initialized)
+    {
+        ctx.mInetLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs);
+    }
+#endif
+}
+
+CHIP_ERROR InitLayers(System::Layer & systemLayer, InetLayer & inetLayer)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+    // Initialize the CHIP System Layer.
+    err = systemLayer.Init(NULL);
+    SuccessOrExit(err);
+
+    // Initialize the CHIP Inet layer.
+    err = inetLayer.Init(systemLayer, NULL);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext)
+{
+    TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
+
+    ChipConnection conn;
+    conn.Init(&ctx.mInetLayer);
+    CHIP_ERROR err = conn.Close();
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+}
+
+void CheckSimpleConnectTest(nlTestSuite * inSuite, void * inContext)
+{
+    TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
+
+    IPAddress addr;
+    IPAddress::FromString("127.0.0.1", addr);
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    ChipConnection conn;
+    conn.Init(&ctx.mInetLayer);
+    err = conn.Connect(0, addr, 0);
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+    err = conn.Connect(0, addr, 0);
+    NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INCORRECT_STATE);
+
+    err = conn.Close();
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+}
+
+void CheckMessageTest(nlTestSuite * inSuite, void * inContext)
+{
+    TestContext & ctx = *reinterpret_cast<TestContext *>(inContext);
+
+    size_t payload_len = sizeof(PAYLOAD);
+
+    chip::System::PacketBuffer * buffer = chip::System::PacketBuffer::NewWithAvailableSize(payload_len);
+    memmove(buffer->Start(), PAYLOAD, payload_len);
+    buffer->SetDataLength(payload_len);
+
+    IPAddress addr;
+    IPAddress::FromString("127.0.0.1", addr);
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    ChipConnection conn;
+    conn.Init(&ctx.mInetLayer);
+    conn.AppState          = inSuite;
+    conn.OnMessageReceived = MessageReceiveHandler;
+    conn.OnReceiveError    = ReceiveErrorHandler;
+
+    err = conn.Connect(0, addr, 0);
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+
+    // Should be able to send a message to itself by just calling send.
+    conn.SendMessage(buffer);
+
+    // allow the send and recv enough time
+    DriveIO(ctx);
+    sleep(1);
+    DriveIO(ctx);
+
+    err = conn.Close();
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
+}
+
+// Test Suite
+
+/**
+ *  Test Suite that lists all the test functions.
+ */
+// clang-format off
+static const nlTest sTests[] =
+{
+    NL_TEST_DEF("Simple Init Test",              CheckSimpleInitTest),
+    NL_TEST_DEF("Simple Connect Test",           CheckSimpleConnectTest),
+    NL_TEST_DEF("Message Self Test",             CheckMessageTest),
+
+    NL_TEST_SENTINEL()
+};
+// clang-format on
+
+// clang-format off
+static nlTestSuite sSuite =
+{
+    "Test-CHIP-Connection",
+    &sTests[0],
+    Initialize,
+    Finalize
+};
+// clang-format on
+
+/**
+ *  Initialize the test suite.
+ */
+static int Initialize(void * aContext)
+{
+    TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
+
+    CHIP_ERROR err = InitLayers(lContext.mSystemLayer, lContext.mInetLayer);
+    if (err != CHIP_NO_ERROR)
+    {
+        return FAILURE;
+    }
+    lContext.mSuite = &sSuite;
+
+    return SUCCESS;
+}
+
+/**
+ *  Finalize the test suite.
+ */
+static int Finalize(void * aContext)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    TestContext & lContext = *reinterpret_cast<TestContext *>(aContext);
+
+    lContext.mSuite = NULL;
+
+    err = lContext.mSystemLayer.Shutdown();
+    if (err != CHIP_NO_ERROR)
+    {
+        return FAILURE;
+    }
+    err = lContext.mInetLayer.Shutdown();
+    if (err != CHIP_NO_ERROR)
+    {
+        return FAILURE;
+    }
+    return SUCCESS;
+}
+
+/**
+ *  Main
+ */
+int TestCHIPConnection()
+{
+    // Run test suit against one context
+    nlTestRunner(&sSuite, &sContext);
+
+    return (nlTestRunnerStats(&sSuite));
+}
diff --git a/src/lib/core/tests/TestCHIPConnectionDriver.cpp b/src/lib/core/tests/TestCHIPConnectionDriver.cpp
new file mode 100644
index 0000000..09af2d8
--- /dev/null
+++ b/src/lib/core/tests/TestCHIPConnectionDriver.cpp
@@ -0,0 +1,35 @@
+/*
+ *
+ *    Copyright (c) 2020 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+/**
+ *    @file
+ *      This file implements a standalone/native program executable
+ *      test driver for the CHIP core library CHIP Connection tests.
+ *
+ */
+
+#include "TestCore.h"
+
+#include <nlunit-test.h>
+
+int main(void)
+{
+    // Generate machine-readable, comma-separated value (CSV) output.
+    nlTestSetOutputStyle(OUTPUT_CSV);
+
+    return (TestCHIPConnection());
+}
\ No newline at end of file
diff --git a/src/lib/core/tests/TestCore.h b/src/lib/core/tests/TestCore.h
index ffb95d6..3426af0 100644
--- a/src/lib/core/tests/TestCore.h
+++ b/src/lib/core/tests/TestCore.h
@@ -31,6 +31,7 @@
 
 int TestCHIPErrorStr(void);
 int TestCHIPTLV(void);
+int TestCHIPConnection(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/lib/support/logging/CHIPLogging.cpp b/src/lib/support/logging/CHIPLogging.cpp
index 8b31250..d5d176b 100644
--- a/src/lib/support/logging/CHIPLogging.cpp
+++ b/src/lib/support/logging/CHIPLogging.cpp
@@ -64,7 +64,7 @@
                                   "TLV"   // TLV
                                   "ASN"   // ASN1
                                   "CR\0"  // Crypto
-                                  "DM\0"  // DeviceManager
+                                  "CTL\0" // Controller
                                   "AL\0"  // Alarm
                                   "BDX"   // BulkDataTransfer
                                   "DMG"   // DataManagement
diff --git a/src/lib/support/logging/CHIPLogging.h b/src/lib/support/logging/CHIPLogging.h
index 5973a82..203eb12 100644
--- a/src/lib/support/logging/CHIPLogging.h
+++ b/src/lib/support/logging/CHIPLogging.h
@@ -86,7 +86,7 @@
     kLogModule_TLV,
     kLogModule_ASN1,
     kLogModule_Crypto,
-    kLogModule_DeviceManager,
+    kLogModule_Controller,
     kLogModule_Alarm,
     kLogModule_BDX,
     kLogModule_DataManagement,