Import ble code
diff --git a/src/ble/BLEEndPoint.cpp b/src/ble/BLEEndPoint.cpp
new file mode 100644
index 0000000..cb437dc
--- /dev/null
+++ b/src/ble/BLEEndPoint.cpp
@@ -0,0 +1,1748 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file implements a Bluetooth Low Energy (BLE) connection
+ *      endpoint abstraction for the byte-streaming,
+ *      connection-oriented Weave over Bluetooth Low Energy (WoBLE)
+ *      Bluetooth Transport Protocol (BTP).
+ *
+ */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+#include <string.h>
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+#include <Weave/Core/WeaveConfig.h>
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Support/FlagUtils.hpp>
+#include <Weave/Support/WeaveFaultInjection.h>
+
+#include <BleLayer/BLEEndPoint.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include "WoBleTest.h"
+#endif
+
+// clang-format off
+
+// Define below to enable extremely verbose, BLE end point-specific debug logging.
+#undef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+
+#ifdef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+#define WeaveLogDebugBleEndPoint(MOD, MSG, ...) WeaveLogError(MOD, MSG, ## __VA_ARGS__)
+#else
+#define WeaveLogDebugBleEndPoint(MOD, MSG, ...)
+#endif
+
+/**
+ *  @def BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD
+ *
+ *  @brief
+ *    If an end point's receive window drops equal to or below this value, it will send an immediate acknowledgement
+ *    packet to re-open its window instead of waiting for the send-ack timer to expire.
+ *
+ */
+#define BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD                   1
+
+/**
+ * @def BLE_CONNECT_TIMEOUT_MS
+ *
+ * @brief
+ *   This is the amount of time, in milliseconds, after a BLE end point initiates a transport protocol connection
+ *   or receives the initial portion of a connect request before the end point will automatically release its BLE
+ *   connection and free itself if the transport connection has not been established.
+ *
+ */
+#define BLE_CONNECT_TIMEOUT_MS                                5000 // 5 seconds
+
+/**
+ *  @def BLE_UNSUBSCRIBE_TIMEOUT_MS
+ *
+ *  @brief
+ *    This is amount of time, in milliseconds, which a BLE end point will wait for an unsubscribe operation to complete
+ *    before it automatically releases its BLE connection and frees itself. The default value of 5 seconds is arbitary.
+ *
+ */
+#define BLE_UNSUBSCRIBE_TIMEOUT_MS                            5000 // 5 seconds
+
+#define BTP_ACK_RECEIVED_TIMEOUT_MS                          15000 // 15 seconds
+#define BTP_ACK_SEND_TIMEOUT_MS                               2500 // 2.5 seconds
+
+#define BTP_WINDOW_NO_ACK_SEND_THRESHOLD                         1 // Data fragments may only be sent without piggybacked
+                                                                   // acks if receiver's window size is above this threshold.
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+BLE_ERROR BLEEndPoint::StartConnect()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    BleTransportCapabilitiesRequestMessage req;
+    PacketBuffer * buf = NULL;
+    int i;
+    int numVersions;
+
+    // Ensure we're in the correct state.
+    VerifyOrExit(mState == kState_Ready, err = BLE_ERROR_INCORRECT_STATE);
+    mState = kState_Connecting;
+
+    // Build BLE transport protocol capabilities request.
+    buf = PacketBuffer::New();
+    VerifyOrExit(buf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+    // Zero-initialize BLE transport capabilities request.
+    memset(&req, 0, sizeof(req));
+
+    req.mMtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
+
+    req.mWindowSize = BLE_MAX_RECEIVE_WINDOW_SIZE;
+
+    // Populate request with highest supported protocol versions
+    numVersions = NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION + 1;
+    VerifyOrExit(numVersions <= NUM_SUPPORTED_PROTOCOL_VERSIONS, err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS);
+    for (i = 0; i < numVersions; i++)
+    {
+        req.SetSupportedProtocolVersion(i, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION - i);
+    }
+
+    err = req.Encode(buf);
+    SuccessOrExit(err);
+
+    // Start connect timer. Canceled when end point freed or connection established.
+    err = StartConnectTimer();
+    SuccessOrExit(err);
+
+    // Send BLE transport capabilities request to peripheral via GATT write.
+    if (!SendWrite(buf))
+    {
+        err = BLE_ERROR_GATT_WRITE_FAILED;
+        ExitNow();
+    }
+
+    // Free request buffer on write confirmation. Stash a reference to it in mSendQueue, which we don't use anyway
+    // until the connection has been set up.
+    QueueTx(buf, kType_Data);
+    buf = NULL;
+
+exit:
+    if (buf != NULL)
+    {
+        PacketBuffer::Free(buf);
+    }
+
+    // If we failed to initiate the connection, close the end point.
+    if (err != BLE_NO_ERROR)
+    {
+        StopConnectTimer();
+        DoClose(kBleCloseFlag_AbortTransmission, err);
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleConnectComplete()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    mState = kState_Connected;
+
+    // Cancel the connect timer.
+    StopConnectTimer();
+
+    // We've successfully completed the BLE transport protocol handshake, so let the application know we're open for business.
+    if (OnConnectComplete != NULL)
+    {
+        // Indicate connect complete to next-higher layer.
+        OnConnectComplete(this, BLE_NO_ERROR);
+    }
+    else
+    {
+        // If no connect complete callback has been set up, close the end point.
+        err = BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK;
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleReceiveConnectionComplete()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    WeaveLogDebugBleEndPoint(Ble, "entered HandleReceiveConnectionComplete");
+    mState = kState_Connected;
+
+    // Cancel receive connection timer.
+    StopReceiveConnectionTimer();
+
+    // We've successfully completed the BLE transport protocol handshake, so let the application know we're open for business.
+    if (mBle->OnWeaveBleConnectReceived != NULL)
+    {
+        // Indicate BLE transport protocol connection received to next-higher layer.
+        mBle->OnWeaveBleConnectReceived(this);
+    }
+    else
+    {
+        err = BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK;
+    }
+
+    return err;
+}
+
+void BLEEndPoint::HandleSubscribeReceived()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    VerifyOrExit(mState == kState_Connecting || mState == kState_Aborting, err = BLE_ERROR_INCORRECT_STATE);
+    VerifyOrExit(mSendQueue != NULL, err = BLE_ERROR_INCORRECT_STATE);
+
+    // Send BTP capabilities response to peripheral via GATT indication.
+#if WEAVE_ENABLE_WOBLE_TEST
+    VerifyOrExit(mWoBle.PopPacketTag(mSendQueue) == kType_Data, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+#endif
+    if (!SendIndication(mSendQueue))
+    {
+        // Ensure transmit queue is empty and set to NULL.
+        QueueTxLock();
+        PacketBuffer::Free(mSendQueue);
+        mSendQueue = NULL;
+        QueueTxUnlock();
+
+        WeaveLogError(Ble, "cap resp ind failed");
+        err = BLE_ERROR_GATT_INDICATE_FAILED;
+        ExitNow();
+    }
+
+    // Shrink remote receive window counter by 1, since we've sent an indication which requires acknowledgement.
+    mRemoteReceiveWindowSize -= 1;
+    WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+
+    // Start ack recvd timer for handshake indication.
+    err = StartAckReceivedTimer();
+    SuccessOrExit(err);
+
+    WeaveLogDebugBleEndPoint(Ble, "got subscribe, sent indication w/ capabilities response");
+
+    // If SendIndication returns true, mSendQueue is freed on indication confirmation, or on close in case of
+    // connection error.
+
+    if (mState != kState_Aborting)
+    {
+        // If peripheral accepted the BTP connection, its end point must enter the connected state here, i.e. before it
+        // receives a GATT confirmation for the capabilities response indication. This behavior is required to handle the
+        // case where a peripheral's BLE controller passes up the central's first message fragment write before the
+        // capabilities response indication confirmation. If the end point waited for this indication confirmation before
+        // it entered the connected state, it'd be in the wrong state to receive the central's first data write, and drop
+        // the corresponding message fragment.
+        err = HandleReceiveConnectionComplete();
+        SuccessOrExit(err);
+    } // Else State == kState_Aborting, so we'll close end point when indication confirmation received.
+
+exit:
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, err);
+    }
+
+    return;
+}
+
+void BLEEndPoint::HandleSubscribeComplete()
+{
+    WeaveLogProgress(Ble, "subscribe complete, ep = %p", this);
+    SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, false);
+
+    BLE_ERROR err = DriveSending();
+
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(kBleCloseFlag_AbortTransmission, BLE_NO_ERROR);
+    }
+}
+
+void BLEEndPoint::HandleUnsubscribeComplete()
+{
+    // Don't bother to clear GattOperationInFlight, we're about to free the end point anyway.
+    Free();
+}
+
+bool BLEEndPoint::IsConnected(uint8_t state) const
+{
+    return (state == kState_Connected || state == kState_Closing);
+}
+
+bool BLEEndPoint::IsUnsubscribePending() const
+{
+    return (GetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning));
+}
+
+void BLEEndPoint::Abort()
+{
+    // No more callbacks after this point, since application explicitly called Abort().
+    OnConnectComplete  = NULL;
+    OnConnectionClosed = NULL;
+    OnMessageReceived  = NULL;
+#if WEAVE_ENABLE_WOBLE_TEST
+    OnCommandReceived = NULL;
+#endif
+
+    DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, BLE_NO_ERROR);
+}
+
+void BLEEndPoint::Close()
+{
+    // No more callbacks after this point, since application explicitly called Close().
+    OnConnectComplete  = NULL;
+    OnConnectionClosed = NULL;
+    OnMessageReceived  = NULL;
+#if WEAVE_ENABLE_WOBLE_TEST
+    OnCommandReceived = NULL;
+#endif
+
+    DoClose(kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+}
+
+void BLEEndPoint::DoClose(uint8_t flags, BLE_ERROR err)
+{
+    uint8_t oldState = mState;
+
+    // If end point is not closed or closing, OR end point was closing gracefully, but tx abort has been specified...
+    if ((mState != kState_Closed && mState != kState_Closing) ||
+        (mState == kState_Closing && (flags & kBleCloseFlag_AbortTransmission)))
+    {
+        // Cancel Connect and ReceiveConnect timers if they are running.
+        // Check role first to avoid needless iteration over timer pool.
+        if (mRole == kBleRole_Central)
+        {
+            StopConnectTimer();
+        }
+        else // (mRole == kBleRole_Peripheral), verified on Init
+        {
+            StopReceiveConnectionTimer();
+        }
+
+        // If transmit buffer is empty or a transmission abort was specified...
+        if (mWoBle.TxState() == WoBle::kState_Idle || (flags & kBleCloseFlag_AbortTransmission))
+        {
+            FinalizeClose(oldState, flags, err);
+        }
+        else
+        {
+            // Wait for send queue and fragmenter's tx buffer to become empty, to ensure all pending messages have been
+            // sent. Only free end point and tell platform it can throw away the underlying BLE connection once all
+            // pending messages have been sent and acknowledged by the remote WoBLE stack, or once the remote stack
+            // closes the WoBLE connection.
+            //
+            // In so doing, BLEEndPoint attempts to emulate the level of reliability afforded by TCPEndPoint and TCP
+            // sockets in general with a typical default SO_LINGER option. That said, there is no hard guarantee that
+            // pending messages will be sent once (Do)Close() is called, so developers should use application-level
+            // messages to confirm the receipt of all data sent prior to a Close() call.
+            mState = kState_Closing;
+
+            if ((flags & kBleCloseFlag_SuppressCallback) == 0)
+            {
+                DoCloseCallback(oldState, flags, err);
+            }
+        }
+    }
+}
+
+void BLEEndPoint::FinalizeClose(uint8_t oldState, uint8_t flags, BLE_ERROR err)
+{
+    mState = kState_Closed;
+
+    // Ensure transmit queue is empty and set to NULL.
+    QueueTxLock();
+    PacketBuffer::Free(mSendQueue);
+    mSendQueue = NULL;
+    QueueTxUnlock();
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    PacketBuffer::Free(mWoBleTest.mCommandReceiveQueue);
+    mWoBleTest.mCommandReceiveQueue = NULL;
+#endif
+
+    // Fire application's close callback if we haven't already, and it's not suppressed.
+    if (oldState != kState_Closing && (flags & kBleCloseFlag_SuppressCallback) == 0)
+    {
+        DoCloseCallback(oldState, flags, err);
+    }
+
+    // If underlying BLE connection has closed, connection object is invalid, so just free the end point and return.
+    if (err == BLE_ERROR_REMOTE_DEVICE_DISCONNECTED || err == BLE_ERROR_APP_CLOSED_CONNECTION)
+    {
+        mConnObj = BLE_CONNECTION_UNINITIALIZED; // Clear handle to BLE connection, so we don't double-close it.
+        Free();
+    }
+    else // Otherwise, try to signal close to remote device before end point releases BLE connection and frees itself.
+    {
+        if (mRole == kBleRole_Central && GetFlag(mConnStateFlags, kConnState_DidBeginSubscribe))
+        {
+            // Cancel send and receive-ack timers, if running.
+            StopAckReceivedTimer();
+            StopSendAckTimer();
+
+            // Indicate close of WeaveConnection to peripheral via GATT unsubscribe. Keep end point allocated until
+            // unsubscribe completes or times out, so platform doesn't close underlying BLE connection before
+            // we're really sure the unsubscribe request has been sent.
+            if (!mBle->mPlatformDelegate->UnsubscribeCharacteristic(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID))
+            {
+                WeaveLogError(Ble, "WoBle unsub failed");
+
+                // If unsubscribe fails, release BLE connection and free end point immediately.
+                Free();
+            }
+            else if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
+            {
+                // Unsubscribe request was sent successfully, and a confirmation wasn't spontaneously generated or
+                // received in the downcall to UnsubscribeCharacteristic, so set timer for the unsubscribe to complete.
+                err = StartUnsubscribeTimer();
+
+                if (err != BLE_NO_ERROR)
+                {
+                    Free();
+                }
+
+                // Mark unsubscribe GATT operation in progress.
+                SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+            }
+        }
+        else // mRole == kBleRole_Peripheral, OR GetFlag(mTimerStateFlags, kConnState_DidBeginSubscribe) == false...
+        {
+            Free();
+        }
+    }
+}
+
+void BLEEndPoint::DoCloseCallback(uint8_t state, uint8_t flags, BLE_ERROR err)
+{
+    if (state == kState_Connecting)
+    {
+        if (OnConnectComplete != NULL)
+        {
+            OnConnectComplete(this, err);
+        }
+    }
+    else
+    {
+        if (OnConnectionClosed != NULL)
+        {
+            OnConnectionClosed(this, err);
+        }
+    }
+
+    // Callback fires once per end point lifetime.
+    OnConnectComplete  = NULL;
+    OnConnectionClosed = NULL;
+}
+
+void BLEEndPoint::ReleaseBleConnection()
+{
+    if (mConnObj != BLE_CONNECTION_UNINITIALIZED)
+    {
+        if (GetFlag(mConnStateFlags, kConnState_AutoClose))
+        {
+            WeaveLogProgress(Ble, "Auto-closing end point's BLE connection.");
+            mBle->mPlatformDelegate->CloseConnection(mConnObj);
+        }
+        else
+        {
+            WeaveLogProgress(Ble, "Releasing end point's BLE connection back to application.");
+            mBle->mApplicationDelegate->NotifyWeaveConnectionClosed(mConnObj);
+        }
+
+        // Never release the same BLE connection twice.
+        mConnObj = BLE_CONNECTION_UNINITIALIZED;
+    }
+}
+
+void BLEEndPoint::Free()
+{
+    // Release BLE connection. Will close connection if AutoClose enabled for this end point. Otherwise, informs
+    // application that Weave is done with this BLE connection, and application makes decision about whether to close
+    // and clean up or retain connection.
+    ReleaseBleConnection();
+
+    // Clear fragmentation and reassembly engine's Tx and Rx buffers. Counters will be reset by next engine init.
+    FreeWoBle();
+
+    // Clear pending ack buffer, if any.
+    PacketBuffer::Free(mAckToSend);
+
+    // Cancel all timers.
+    StopConnectTimer();
+    StopReceiveConnectionTimer();
+    StopAckReceivedTimer();
+    StopSendAckTimer();
+    StopUnsubscribeTimer();
+#if WEAVE_ENABLE_WOBLE_TEST
+    mWoBleTest.StopTestTimer();
+    // Clear callback
+    OnCommandReceived = NULL;
+#endif
+
+    // Clear callbacks.
+    OnConnectComplete  = NULL;
+    OnMessageReceived  = NULL;
+    OnConnectionClosed = NULL;
+
+    // Clear handle to underlying BLE connection.
+    mConnObj = BLE_CONNECTION_UNINITIALIZED;
+
+    // Release the AddRef() that happened when the end point was allocated.
+    Release();
+}
+
+void BLEEndPoint::FreeWoBle()
+{
+    PacketBuffer * buf;
+
+    // Free transmit disassembly buffer
+    buf = mWoBle.TxPacket();
+    mWoBle.ClearTxPacket();
+    PacketBuffer::Free(buf);
+
+    // Free receive reassembly buffer
+    buf = mWoBle.RxPacket();
+    mWoBle.ClearRxPacket();
+    PacketBuffer::Free(buf);
+}
+
+BLE_ERROR BLEEndPoint::Init(BleLayer * bleLayer, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    bool expectInitialAck;
+
+    // Fail if already initialized.
+    VerifyOrExit(mBle == NULL, err = BLE_ERROR_INCORRECT_STATE);
+
+    // Validate args.
+    VerifyOrExit(bleLayer != NULL, err = BLE_ERROR_BAD_ARGS);
+    VerifyOrExit(connObj != BLE_CONNECTION_UNINITIALIZED, err = BLE_ERROR_BAD_ARGS);
+    VerifyOrExit((role == kBleRole_Central || role == kBleRole_Peripheral), err = BLE_ERROR_BAD_ARGS);
+
+    // Null-initialize callbacks and data members.
+    //
+    // Beware this line should we ever use virtuals in this class or its
+    // super(s). See similar lines in Weave::System::Layer end points.
+    memset((void *) this, 0, sizeof(*this));
+
+    // If end point plays peripheral role, expect ack for indication sent as last step of BTP handshake.
+    // If central, periperal's handshake indication 'ack's write sent by central to kick off the BTP handshake.
+    expectInitialAck = (role == kBleRole_Peripheral);
+
+    err = mWoBle.Init(this, expectInitialAck);
+    if (err != BLE_NO_ERROR)
+    {
+        WeaveLogError(Ble, "WoBle init failed");
+        ExitNow();
+    }
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    err = (BLE_ERROR) mTxQueueMutex.Init(mTxQueueMutex);
+    if (err != BLE_NO_ERROR)
+    {
+        WeaveLogError(Ble, "%s: Mutex init failed", __FUNCTION__);
+        ExitNow();
+    }
+    err = mWoBleTest.Init(this);
+    if (err != BLE_NO_ERROR)
+    {
+        WeaveLogError(Ble, "WoBleTest init failed");
+        ExitNow();
+    }
+#endif
+
+    // BleLayerObject initialization:
+    mBle      = bleLayer;
+    mRefCount = 1;
+
+    // BLEEndPoint data members:
+    mConnObj         = connObj;
+    mRole            = role;
+    mConnStateFlags  = 0;
+    mTimerStateFlags = 0;
+    SetFlag(mConnStateFlags, kConnState_AutoClose, autoClose);
+    mLocalReceiveWindowSize  = 0;
+    mRemoteReceiveWindowSize = 0;
+    mReceiveWindowMaxSize    = 0;
+    mSendQueue               = NULL;
+    mAckToSend               = NULL;
+
+    WeaveLogDebugBleEndPoint(Ble, "initialized local rx window, size = %u", mLocalReceiveWindowSize);
+
+    // End point is ready to connect or receive a connection.
+    mState = kState_Ready;
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::SendCharacteristic(PacketBuffer * buf)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    if (mRole == kBleRole_Central)
+    {
+        if (!SendWrite(buf))
+        {
+            err = BLE_ERROR_GATT_WRITE_FAILED;
+        }
+        else
+        {
+            // Write succeeded, so shrink remote receive window counter by 1.
+            mRemoteReceiveWindowSize -= 1;
+            WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+        }
+    }
+    else // (mRole == kBleRole_Peripheral), verified on Init
+    {
+        if (!SendIndication(buf))
+        {
+            err = BLE_ERROR_GATT_INDICATE_FAILED;
+        }
+        else
+        {
+            // Indication succeeded, so shrink remote receive window counter by 1.
+            mRemoteReceiveWindowSize -= 1;
+            WeaveLogDebugBleEndPoint(Ble, "decremented remote rx window, new size = %u", mRemoteReceiveWindowSize);
+        }
+    }
+
+    return err;
+}
+
+/*
+ *  Routine to queue the Tx packet with a packet type
+ *  kType_Data(0)       - data packet
+ *  kType_Control(1)    - control packet
+ */
+void BLEEndPoint::QueueTx(PacketBuffer * data, PacketType_t type)
+{
+#if WEAVE_ENABLE_WOBLE_TEST
+    WeaveLogDebugBleEndPoint(Ble, "%s: data->%p, type %d, len %d", __FUNCTION__, data, type, data->DataLength());
+    mWoBle.PushPacketTag(data, type);
+#endif
+
+    QueueTxLock();
+
+    if (mSendQueue == NULL)
+    {
+        mSendQueue = data;
+        WeaveLogDebugBleEndPoint(Ble, "%s: Set data as new mSendQueue %p, type %d", __FUNCTION__, mSendQueue, type);
+    }
+    else
+    {
+        mSendQueue->AddToEnd(data);
+        WeaveLogDebugBleEndPoint(Ble, "%s: Append data to mSendQueue %p, type %d", __FUNCTION__, mSendQueue, type);
+    }
+
+    QueueTxUnlock();
+}
+
+BLE_ERROR BLEEndPoint::Send(PacketBuffer * data)
+{
+    WeaveLogDebugBleEndPoint(Ble, "entered Send");
+
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+    VerifyOrExit(IsConnected(mState), err = BLE_ERROR_INCORRECT_STATE);
+
+    // Ensure outgoing message fits in a single contiguous PacketBuffer, as currently required by the
+    // message fragmentation and reassembly engine.
+    if (data->Next() != NULL)
+    {
+        data->CompactHead();
+
+        if (data->Next() != NULL)
+        {
+            err = BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG;
+            ExitNow();
+        }
+    }
+
+    // Add new message to send queue.
+    QueueTx(data, kType_Data);
+    data = NULL; // Buffer freed when send queue freed on close, or on completion of current message transmission.
+
+    // Send first fragment of new message, if we can.
+    err = DriveSending();
+    SuccessOrExit(err);
+
+exit:
+    WeaveLogDebugBleEndPoint(Ble, "exiting Send");
+
+    if (data != NULL)
+    {
+        PacketBuffer::Free(data);
+    }
+
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(kBleCloseFlag_AbortTransmission, err);
+    }
+
+    return err;
+}
+
+bool BLEEndPoint::PrepareNextFragment(PacketBuffer * data, bool & sentAck)
+{
+    // If we have a pending fragment acknowledgement to send, piggyback it on the fragment we're about to transmit.
+    if (GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning))
+    {
+        // Reset local receive window counter.
+        mLocalReceiveWindowSize = mReceiveWindowMaxSize;
+        WeaveLogDebugBleEndPoint(Ble, "reset local rx window on piggyback ack tx, size = %u", mLocalReceiveWindowSize);
+
+        // Tell caller AND fragmenter we have an ack to piggyback.
+        sentAck = true;
+    }
+    else
+    {
+        // No ack to piggyback.
+        sentAck = false;
+    }
+
+    return mWoBle.HandleCharacteristicSend(data, sentAck);
+}
+
+BLE_ERROR BLEEndPoint::SendNextMessage()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    bool sentAck;
+
+    // Get the first queued packet to send
+    QueueTxLock();
+#if WEAVE_ENABLE_WOBLE_TEST
+    // Return if tx queue is empty
+    // Note: DetachTail() does not check an empty queue
+    if (mSendQueue == NULL)
+    {
+        QueueTxUnlock();
+        return err;
+    }
+#endif
+
+    PacketBuffer * data = mSendQueue;
+    mSendQueue          = mSendQueue->DetachTail();
+    QueueTxUnlock();
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    // Get and consume the packet tag in message buffer
+    PacketType_t type = mWoBle.PopPacketTag(data);
+    mWoBle.SetTxPacketType(type);
+    mWoBleTest.DoTxTiming(data, WOBLE_TX_START);
+#endif
+
+    // Hand whole message payload to the fragmenter.
+    VerifyOrExit(PrepareNextFragment(data, sentAck), err = BLE_ERROR_WOBLE_PROTOCOL_ABORT);
+    data = NULL; // Ownership passed to fragmenter's tx buf on PrepareNextFragment success.
+
+    // Send first message fragment over the air.
+    WEAVE_FAULT_INJECT(nl::Weave::FaultInjection::kFault_WOBLESend,
+            {
+                if (mRole == kBleRole_Central)
+                {
+                    err = BLE_ERROR_GATT_WRITE_FAILED;
+                } else {
+                    err = BLE_ERROR_GATT_INDICATE_FAILED;
+                }
+                ExitNow();
+            }
+            );
+    err = SendCharacteristic(mWoBle.TxPacket());
+    SuccessOrExit(err);
+
+    if (sentAck)
+    {
+        // If sent piggybacked ack, stop send-ack timer.
+        StopSendAckTimer();
+    }
+
+    // Start ack received timer, if it's not already running.
+    err = StartAckReceivedTimer();
+    SuccessOrExit(err);
+
+exit:
+    if (data != NULL)
+    {
+        PacketBuffer::Free(data);
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::ContinueMessageSend()
+{
+    BLE_ERROR err;
+    bool sentAck;
+
+    if (!PrepareNextFragment(NULL, sentAck))
+    {
+        // Log BTP error
+        WeaveLogError(Ble, "btp fragmenter error on send!");
+        mWoBle.LogState();
+
+        err = BLE_ERROR_WOBLE_PROTOCOL_ABORT;
+        ExitNow();
+    }
+
+    err = SendCharacteristic(mWoBle.TxPacket());
+    SuccessOrExit(err);
+
+    if (sentAck)
+    {
+        // If sent piggybacked ack, stop send-ack timer.
+        StopSendAckTimer();
+    }
+
+    // Start ack received timer, if it's not already running.
+    err = StartAckReceivedTimer();
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleHandshakeConfirmationReceived()
+{
+    WeaveLogDebugBleEndPoint(Ble, "entered HandleHandshakeConfirmationReceived");
+
+    BLE_ERROR err      = BLE_NO_ERROR;
+    uint8_t closeFlags = kBleCloseFlag_AbortTransmission;
+
+    // Free capabilities request/response payload.
+    QueueTxLock();
+    mSendQueue = PacketBuffer::FreeHead(mSendQueue);
+    QueueTxUnlock();
+
+    if (mRole == kBleRole_Central)
+    {
+        // Subscribe to characteristic which peripheral will use to send indications. Prompts peripheral to send
+        // BLE transport capabilities indication.
+        VerifyOrExit(mBle->mPlatformDelegate->SubscribeCharacteristic(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID),
+                     err = BLE_ERROR_GATT_SUBSCRIBE_FAILED);
+
+        // We just sent a GATT subscribe request, so make sure to attempt unsubscribe on close.
+        SetFlag(mConnStateFlags, kConnState_DidBeginSubscribe, true);
+
+        // Mark GATT operation in progress for subscribe request.
+        SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+    }
+    else // (mRole == kBleRole_Peripheral), verified on Init
+    {
+        WeaveLogDebugBleEndPoint(Ble, "got peripheral handshake indication confirmation");
+
+        if (mState == kState_Connected) // If we accepted BTP connection...
+        {
+            // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
+            // pending on which to piggyback an ack, send immediate stand-alone ack.
+            if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD && mSendQueue == NULL)
+            {
+                err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
+                SuccessOrExit(err);
+            }
+            else
+            {
+                // Drive sending in case application callend Send() after we sent the handshake indication, but
+                // before the GATT confirmation for this indication was received.
+                err = DriveSending();
+                SuccessOrExit(err);
+            }
+        }
+        else if (mState == kState_Aborting) // Else, if we rejected BTP connection...
+        {
+            closeFlags |= kBleCloseFlag_SuppressCallback;
+            err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
+            ExitNow();
+        }
+    }
+
+exit:
+    WeaveLogDebugBleEndPoint(Ble, "exiting HandleHandshakeConfirmationReceived");
+
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(closeFlags, err);
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleFragmentConfirmationReceived()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    WeaveLogDebugBleEndPoint(Ble, "entered HandleFragmentConfirmationReceived");
+
+    // Suppress error logging if GATT confirmation overlaps with unsubscribe on final close.
+    if (IsUnsubscribePending())
+    {
+        WeaveLogDebugBleEndPoint(Ble, "send conf rx'd while unsubscribe in flight");
+        ExitNow();
+    }
+
+    // Ensure we're in correct state to receive confirmation of non-handshake GATT send.
+    VerifyOrExit(IsConnected(mState), err = BLE_ERROR_INCORRECT_STATE);
+
+    // TODO PacketBuffer high water mark optimization: if ack pending, but fragmenter state == complete, free fragmenter's
+    // tx buf before sending ack.
+
+    if (GetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight))
+    {
+        // If confirmation was received for stand-alone ack, free its tx buffer.
+        PacketBuffer::Free(mAckToSend);
+        mAckToSend = NULL;
+
+        SetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight, false);
+    }
+
+    // If local receive window size has shrunk to or below immediate ack threshold, AND a message fragment is not
+    // pending on which to piggyback an ack, send immediate stand-alone ack.
+    //
+    // This check covers the case where the local receive window has shrunk between transmission and confirmation of
+    // the stand-alone ack, and also the case where a window size < the immediate ack threshold was detected in
+    // Receive(), but the stand-alone ack was deferred due to a pending outbound message fragment.
+    if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
+        !(mSendQueue != NULL || mWoBle.TxState() == WoBle::kState_InProgress) )
+    {
+        err = DriveStandAloneAck(); // Encode stand-alone ack and drive sending.
+        SuccessOrExit(err);
+    }
+    else
+    {
+        err = DriveSending();
+        SuccessOrExit(err);
+    }
+
+exit:
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(kBleCloseFlag_AbortTransmission, err);
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleGattSendConfirmationReceived()
+{
+    WeaveLogDebugBleEndPoint(Ble, "entered HandleGattSendConfirmationReceived");
+
+    // Mark outstanding GATT operation as finished.
+    SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, false);
+
+    // If confirmation was for outbound portion of BTP connect handshake...
+    if (!GetFlag(mConnStateFlags, kConnState_CapabilitiesConfReceived))
+    {
+        SetFlag(mConnStateFlags, kConnState_CapabilitiesConfReceived, true);
+
+        return HandleHandshakeConfirmationReceived();
+    }
+    else
+    {
+        return HandleFragmentConfirmationReceived();
+    }
+}
+
+BLE_ERROR BLEEndPoint::DriveStandAloneAck()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    // Stop send-ack timer if running.
+    StopSendAckTimer();
+
+    // If stand-alone ack not already pending, allocate new payload buffer here.
+    if (mAckToSend == NULL)
+    {
+        mAckToSend = PacketBuffer::New();
+        VerifyOrExit(mAckToSend != NULL, err = BLE_ERROR_NO_MEMORY);
+    }
+
+    // Attempt to send stand-alone ack.
+    err = DriveSending();
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::DoSendStandAloneAck()
+{
+    WeaveLogDebugBleEndPoint(Ble, "entered DoSendStandAloneAck; sending stand-alone ack");
+
+    // Encode and transmit stand-alone ack.
+    mWoBle.EncodeStandAloneAck(mAckToSend);
+    BLE_ERROR err = SendCharacteristic(mAckToSend);
+    SuccessOrExit(err);
+
+    // Reset local receive window counter.
+    mLocalReceiveWindowSize = mReceiveWindowMaxSize;
+    WeaveLogDebugBleEndPoint(Ble, "reset local rx window on stand-alone ack tx, size = %u", mLocalReceiveWindowSize);
+
+    SetFlag(mConnStateFlags, kConnState_StandAloneAckInFlight, true);
+
+    // Start ack received timer, if it's not already running.
+    err = StartAckReceivedTimer();
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::DriveSending()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    WeaveLogDebugBleEndPoint(Ble, "entered DriveSending");
+
+    // If receiver's window is almost closed and we don't have an ack to send, OR we do have an ack to send but
+    // receiver's window is completely empty, OR another GATT operation is in flight, awaiting confirmation...
+    if ((mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
+         !GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning) && mAckToSend == NULL) ||
+        (mRemoteReceiveWindowSize == 0) || (GetFlag(mConnStateFlags, kConnState_GattOperationInFlight)))
+    {
+#ifdef NL_BLE_END_POINT_DEBUG_LOGGING_ENABLED
+        if (mRemoteReceiveWindowSize <= BTP_WINDOW_NO_ACK_SEND_THRESHOLD &&
+            !GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning) && mAckToSend == NULL)
+        {
+            WeaveLogDebugBleEndPoint(Ble, "NO SEND: receive window almost closed, and no ack to send");
+        }
+
+        if (mRemoteReceiveWindowSize == 0)
+        {
+            WeaveLogDebugBleEndPoint(Ble, "NO SEND: remote receive window closed");
+        }
+
+        if (GetFlag(mConnStateFlags, kConnState_GattOperationInFlight))
+        {
+            WeaveLogDebugBleEndPoint(Ble, "NO SEND: Gatt op in flight");
+        }
+#endif
+
+        // Can't send anything.
+        ExitNow();
+    }
+
+    // Otherwise, let's see what we can send.
+
+    if (mAckToSend != NULL) // If immediate, stand-alone ack is pending, send it.
+    {
+        err = DoSendStandAloneAck();
+        SuccessOrExit(err);
+    }
+    else if (mWoBle.TxState() == WoBle::kState_Idle) // Else send next message fragment, if any.
+    {
+        // Fragmenter's idle, let's see what's in the send queue...
+        if (mSendQueue != NULL)
+        {
+            // Transmit first fragment of next whole message in send queue.
+            err = SendNextMessage();
+            SuccessOrExit(err);
+        }
+        else
+        {
+            // Nothing to send!
+        }
+    }
+    else if (mWoBle.TxState() == WoBle::kState_InProgress)
+    {
+        // Send next fragment of message currently held by fragmenter.
+        err = ContinueMessageSend();
+        SuccessOrExit(err);
+    }
+    else if (mWoBle.TxState() == WoBle::kState_Complete)
+    {
+        // Clear fragmenter's pointer to sent message buffer and reset its Tx state.
+        PacketBuffer * sentBuf = mWoBle.TxPacket();
+#if WEAVE_ENABLE_WOBLE_TEST
+        mWoBleTest.DoTxTiming(sentBuf, WOBLE_TX_DONE);
+#endif // WEAVE_ENABLE_WOBLE_TEST
+        mWoBle.ClearTxPacket();
+
+        // Free sent buffer.
+        PacketBuffer::Free(sentBuf);
+        sentBuf = NULL;
+
+        if (mSendQueue != NULL)
+        {
+            // Transmit first fragment of next whole message in send queue.
+            err = SendNextMessage();
+            SuccessOrExit(err);
+        }
+        else if (mState == kState_Closing && !mWoBle.ExpectingAck()) // and mSendQueue is NULL, per above...
+        {
+            // If end point closing, got last ack, and got out-of-order confirmation for last send, finalize close.
+            FinalizeClose(mState, kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+        }
+        else
+        {
+            // Nothing to send!
+        }
+    }
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleCapabilitiesRequestReceived(PacketBuffer * data)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    BleTransportCapabilitiesRequestMessage req;
+    BleTransportCapabilitiesResponseMessage resp;
+    PacketBuffer * responseBuf = NULL;
+    uint16_t mtu;
+
+    VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+    mState = kState_Connecting;
+
+    // Decode BTP capabilities request.
+    err = BleTransportCapabilitiesRequestMessage::Decode((*data), req);
+    SuccessOrExit(err);
+
+    responseBuf = PacketBuffer::New();
+    VerifyOrExit(responseBuf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+    // Determine BLE connection's negotiated ATT MTU, if possible.
+    if (req.mMtu > 0) // If MTU was observed and provided by central...
+    {
+        mtu = req.mMtu; // Accept central's observation of the MTU.
+    }
+    else // Otherwise, retrieve it via the platform delegate...
+    {
+        mtu = mBle->mPlatformDelegate->GetMTU(mConnObj);
+    }
+
+    // Select fragment size for connection based on ATT MTU.
+    if (mtu > 0) // If one or both device knows connection's MTU...
+    {
+        resp.mFragmentSize =
+            nl::Weave::min(static_cast<uint16_t>(mtu - 3), WoBle::sMaxFragmentSize); // Reserve 3 bytes of MTU for ATT header.
+    }
+    else // Else, if neither device knows MTU...
+    {
+        WeaveLogProgress(Ble, "cannot determine ATT MTU; selecting default fragment size = %u", WoBle::sDefaultFragmentSize);
+        resp.mFragmentSize = WoBle::sDefaultFragmentSize;
+    }
+
+    // Select local and remote max receive window size based on local resources available for both incoming writes AND
+    // GATT confirmations.
+    mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize =
+        nl::Weave::min(req.mWindowSize, static_cast<uint8_t>(BLE_MAX_RECEIVE_WINDOW_SIZE));
+    resp.mWindowSize = mReceiveWindowMaxSize;
+
+    WeaveLogProgress(Ble, "local and remote recv window sizes = %u", resp.mWindowSize);
+
+    // Select BLE transport protocol version from those supported by central, or none if no supported version found.
+    resp.mSelectedProtocolVersion = BleLayer::GetHighestSupportedProtocolVersion(req);
+    WeaveLogProgress(Ble, "selected BTP version %d", resp.mSelectedProtocolVersion);
+
+    if (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_None)
+    {
+        // If BLE transport protocol versions incompatible, prepare to close connection after subscription has been
+        // received and capabilities response has been sent.
+        WeaveLogError(Ble, "incompatible BTP versions; peripheral expected between %d and %d",
+                      NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
+        mState = kState_Aborting;
+    }
+    else if ((resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V1) ||
+             (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V2))
+    {
+        // Set Rx and Tx fragment sizes to the same value
+        mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+        mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+    }
+    else // resp.SelectedProtocolVersion >= kBleTransportProtocolVersion_V3
+    {
+        // This is the peripheral, so set Rx fragment size, and leave Tx at default
+        mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+    }
+    WeaveLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mWoBle.GetRxFragmentSize(), mWoBle.GetTxFragmentSize());
+
+    err = resp.Encode(responseBuf);
+    SuccessOrExit(err);
+
+    // Stash capabilities response payload and wait for subscription from central.
+    QueueTx(responseBuf, kType_Data);
+    responseBuf = NULL;
+
+    // Start receive timer. Canceled when end point freed or connection established.
+    err = StartReceiveConnectionTimer();
+    SuccessOrExit(err);
+
+exit:
+    if (responseBuf != NULL)
+    {
+        PacketBuffer::Free(responseBuf);
+    }
+
+    if (data != NULL)
+    {
+        PacketBuffer::Free(data);
+    }
+
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::HandleCapabilitiesResponseReceived(PacketBuffer * data)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    BleTransportCapabilitiesResponseMessage resp;
+
+    VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+    // Decode BTP capabilities response.
+    err = BleTransportCapabilitiesResponseMessage::Decode((*data), resp);
+    SuccessOrExit(err);
+
+    VerifyOrExit(resp.mFragmentSize > 0, err = BLE_ERROR_INVALID_FRAGMENT_SIZE);
+
+    WeaveLogProgress(Ble, "peripheral chose BTP version %d; central expected between %d and %d", resp.mSelectedProtocolVersion,
+                     NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION, NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION);
+
+    if ((resp.mSelectedProtocolVersion < NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) ||
+        (resp.mSelectedProtocolVersion > NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION))
+    {
+        err = BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS;
+        ExitNow();
+    }
+
+    // Set fragment size as minimum of (reported ATT MTU, WoBLE characteristic size)
+    resp.mFragmentSize = nl::Weave::min(resp.mFragmentSize, WoBle::sMaxFragmentSize);
+
+    if ((resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V1) ||
+        (resp.mSelectedProtocolVersion == kBleTransportProtocolVersion_V2))
+    {
+        mWoBle.SetRxFragmentSize(resp.mFragmentSize);
+        mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+    }
+    else // resp.SelectedProtocolVersion >= kBleTransportProtocolVersion_V3
+    {
+        // This is the central, so set Tx fragement size, and leave Rx at default.
+        mWoBle.SetTxFragmentSize(resp.mFragmentSize);
+    }
+    WeaveLogProgress(Ble, "using BTP fragment sizes rx %d / tx %d.", mWoBle.GetRxFragmentSize(), mWoBle.GetTxFragmentSize());
+
+    // Select local and remote max receive window size based on local resources available for both incoming indications
+    // AND GATT confirmations.
+    mRemoteReceiveWindowSize = mLocalReceiveWindowSize = mReceiveWindowMaxSize = resp.mWindowSize;
+
+    WeaveLogProgress(Ble, "local and remote recv window size = %u", resp.mWindowSize);
+
+    // Shrink local receive window counter by 1, since connect handshake indication requires acknowledgement.
+    mLocalReceiveWindowSize -= 1;
+    WeaveLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
+
+    // Send ack for connection handshake indication when timer expires. Sequence numbers always start at 0,
+    // and the reassembler's "last received seq num" is initialized to 0 and updated when new fragments are
+    // received from the peripheral, so we don't need to explicitly mark the ack num to send here.
+    err = StartSendAckTimer();
+    SuccessOrExit(err);
+
+    // We've sent a capabilities request write and received a compatible response, so the connect
+    // operation has completed successfully.
+    err = HandleConnectComplete();
+    SuccessOrExit(err);
+
+exit:
+    if (data != NULL)
+    {
+        PacketBuffer::Free(data);
+    }
+
+    return err;
+}
+
+// Returns number of open slots in remote receive window given the input values.
+SequenceNumber_t BLEEndPoint::AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
+                                                        SequenceNumber_t newestUnackedSentSeqNum)
+{
+    // Assumption: SequenceNumber_t is uint8_t.
+    // Assumption: Maximum possible sequence number value is UINT8_MAX.
+    // Assumption: Sequence numbers incremented past maximum value wrap to 0.
+    // Assumption: newest unacked sent sequence number never exceeds current (and by extension, new and un-wrapped)
+    //             window boundary, so it never wraps relative to last received ack, if new window boundary would not
+    //             also wrap.
+
+    // Define new window boundary (inclusive) as uint16_t, so its value can temporarily exceed UINT8_MAX.
+    uint16_t newRemoteWindowBoundary = lastReceivedAck + maxRemoteWindowSize;
+
+    if (newRemoteWindowBoundary > UINT8_MAX && newestUnackedSentSeqNum < lastReceivedAck)
+    {
+        // New window boundary WOULD wrap, and latest unacked seq num already HAS wrapped, so add offset to difference.
+        return (newRemoteWindowBoundary - (newestUnackedSentSeqNum + UINT8_MAX));
+    }
+    else
+    {
+        // Neither values would or have wrapped, OR new boundary WOULD wrap but latest unacked seq num does not, so no
+        // offset required.
+        return (newRemoteWindowBoundary - newestUnackedSentSeqNum);
+    }
+}
+
+BLE_ERROR BLEEndPoint::Receive(PacketBuffer * data)
+{
+    WeaveLogDebugBleEndPoint(Ble, "+++++++++++++++++++++ entered receive");
+    BLE_ERROR err                = BLE_NO_ERROR;
+    SequenceNumber_t receivedAck = 0;
+    uint8_t closeFlags           = kBleCloseFlag_AbortTransmission;
+    bool didReceiveAck           = false;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    if (mWoBle.IsCommandPacket(data))
+    {
+        WeaveLogDebugBleEndPoint(Ble, "%s: Received Control frame: Flags %x", __FUNCTION__, *(data->Start()));
+    }
+    else
+#endif
+    { // This is a special handling on the first Woble data packet, the CapabilitiesRequest.
+        // Suppress error logging if peer's send overlaps with our unsubscribe on final close.
+        if (IsUnsubscribePending())
+        {
+            WeaveLogDebugBleEndPoint(Ble, "characteristic rx'd while unsubscribe in flight");
+            ExitNow();
+        }
+
+        // If we're receiving the first inbound packet of a BLE transport connection handshake...
+        if (!GetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived))
+        {
+            if (mRole == kBleRole_Central) // If we're a central receiving a capabilities response indication...
+            {
+                // Ensure end point's in the right state before continuing.
+                VerifyOrExit(mState == kState_Connecting, err = BLE_ERROR_INCORRECT_STATE);
+                SetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived, true);
+
+                err  = HandleCapabilitiesResponseReceived(data);
+                data = NULL;
+                SuccessOrExit(err);
+            }
+            else // Or, a peripheral receiving a capabilities request write...
+            {
+                // Ensure end point's in the right state before continuing.
+                VerifyOrExit(mState == kState_Ready, err = BLE_ERROR_INCORRECT_STATE);
+                SetFlag(mConnStateFlags, kConnState_CapabilitiesMsgReceived, true);
+
+                err  = HandleCapabilitiesRequestReceived(data);
+                data = NULL;
+
+                if (err != BLE_NO_ERROR)
+                {
+                    // If an error occurred decoding and handling the capabilities request, release the BLE connection.
+                    // Central's connect attempt will time out if peripheral's application decides to keep the BLE
+                    // connection open, or fail immediately if the application closes the connection.
+                    closeFlags = closeFlags | kBleCloseFlag_SuppressCallback;
+                    ExitNow();
+                }
+            }
+
+            // If received data was handshake packet, don't feed it to message reassembler.
+            ExitNow();
+        }
+    } // End handling the CapabilitiesRequest
+
+    WeaveLogDebugBleEndPoint(Ble, "prepared to rx post-handshake btp packet");
+
+    // We've received a post-handshake BTP packet.
+    // Ensure end point's in the right state before continuing.
+    if (!IsConnected(mState))
+    {
+        WeaveLogError(Ble, "ep rx'd packet in bad state");
+        err = BLE_ERROR_INCORRECT_STATE;
+
+        ExitNow();
+    }
+
+    WeaveLogDebugBleEndPoint(Ble, "woble about to rx characteristic, state before:");
+    mWoBle.LogStateDebug();
+
+    // Pass received packet into BTP protocol engine.
+    err  = mWoBle.HandleCharacteristicReceived(data, receivedAck, didReceiveAck);
+    data = NULL; // Buffer consumed by protocol engine; either freed or added to message reassembly area.
+
+    WeaveLogDebugBleEndPoint(Ble, "woble rx'd characteristic, state after:");
+    mWoBle.LogStateDebug();
+
+    SuccessOrExit(err);
+
+    // Protocol engine accepted the fragment, so shrink local receive window counter by 1.
+    mLocalReceiveWindowSize -= 1;
+    WeaveLogDebugBleEndPoint(Ble, "decremented local rx window, new size = %u", mLocalReceiveWindowSize);
+
+    // Respond to received ack, if any.
+    if (didReceiveAck)
+    {
+        WeaveLogDebugBleEndPoint(Ble, "got btp ack = %u", receivedAck);
+
+        // If ack was rx'd for neweset unacked sent fragment, stop ack received timer.
+        if (!mWoBle.ExpectingAck())
+        {
+            WeaveLogDebugBleEndPoint(Ble, "got ack for last outstanding fragment");
+            StopAckReceivedTimer();
+
+            if (mState == kState_Closing && mSendQueue == NULL && mWoBle.TxState() == WoBle::kState_Idle)
+            {
+                // If end point closing, got confirmation for last send, and waiting for last ack, finalize close.
+                FinalizeClose(mState, kBleCloseFlag_SuppressCallback, BLE_NO_ERROR);
+                ExitNow();
+            }
+        }
+        else // Else there are still sent fragments for which acks are expected, so restart ack received timer.
+        {
+            WeaveLogDebugBleEndPoint(Ble, "still expecting ack(s), restarting timer...");
+            err = RestartAckReceivedTimer();
+            SuccessOrExit(err);
+        }
+
+        WeaveLogDebugBleEndPoint(Ble, "about to adjust remote rx window; got ack num = %u, newest unacked sent seq num = %u, \
+                old window size = %u, max window size = %u",
+                                 receivedAck, mWoBle.GetNewestUnackedSentSequenceNumber(), mRemoteReceiveWindowSize,
+                                 mReceiveWindowMaxSize);
+
+        // Open remote device's receive window according to sequence number it just acknowledged.
+        mRemoteReceiveWindowSize =
+            AdjustRemoteReceiveWindow(receivedAck, mReceiveWindowMaxSize, mWoBle.GetNewestUnackedSentSequenceNumber());
+
+        WeaveLogDebugBleEndPoint(Ble, "adjusted remote rx window, new size = %u", mRemoteReceiveWindowSize);
+
+        // Restart message transmission if it was previously paused due to window exhaustion.
+        err = DriveSending();
+        SuccessOrExit(err);
+    }
+
+    // The previous DriveSending() might have generated a piggyback acknowledgement if there was
+    // previously un-acked data.  Otherwise, prepare to send acknowledgement for newly received fragment.
+    //
+    // If local receive window is below immediate ack threshold, AND there is no previous stand-alone ack in
+    // flight, AND there is no pending outbound message fragment on which the ack can and will be piggybacked,
+    // send immediate stand-alone ack to reopen window for sender.
+    //
+    // The "GATT operation in flight" check below covers "pending outbound message fragment" by extension, as when
+    // a message has been passed to the end point via Send(), its next outbound fragment must either be in flight
+    // itself, or awaiting the completion of another in-flight GATT operation.
+    //
+    // If any GATT operation is in flight that is NOT a stand-alone ack, the window size will be checked against
+    // this threshold again when the GATT operation is confirmed.
+    if (mWoBle.HasUnackedData())
+    {
+        if (mLocalReceiveWindowSize <= BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD &&
+            !GetFlag(mConnStateFlags, kConnState_GattOperationInFlight))
+        {
+            WeaveLogDebugBleEndPoint(Ble, "sending immediate ack");
+            err = DriveStandAloneAck();
+            SuccessOrExit(err);
+        }
+        else
+        {
+            WeaveLogDebugBleEndPoint(Ble, "starting send-ack timer");
+
+            // Send ack when timer expires.
+            err = StartSendAckTimer();
+            SuccessOrExit(err);
+        }
+    }
+
+    // If we've reassembled a whole message...
+    if (mWoBle.RxState() == WoBle::kState_Complete)
+    {
+        // Take ownership of message PacketBuffer
+        PacketBuffer * full_packet = mWoBle.RxPacket();
+        mWoBle.ClearRxPacket();
+
+        WeaveLogDebugBleEndPoint(Ble, "reassembled whole msg, len = %d", full_packet->DataLength());
+
+#if WEAVE_ENABLE_WOBLE_TEST
+        // If we have a control message received callback, and end point is not closing...
+        if (mWoBle.RxPacketType() == kType_Control && OnCommandReceived && mState != kState_Closing)
+        {
+            WeaveLogDebugBleEndPoint(Ble, "%s: calling OnCommandReceived, seq# %u, len = %u, type %u", __FUNCTION__, receivedAck,
+                                     full_packet->DataLength(), mWoBle.RxPacketType());
+            // Pass received control message up the stack.
+            mWoBle.SetRxPacketSeq(receivedAck);
+            OnCommandReceived(this, full_packet);
+        }
+        else
+#endif
+            // If we have a message received callback, and end point is not closing...
+            if (OnMessageReceived && mState != kState_Closing)
+        {
+            // Pass received message up the stack.
+            OnMessageReceived(this, full_packet);
+        }
+        else
+        {
+            // Free received message if there's no one to own it.
+            PacketBuffer::Free(full_packet);
+        }
+    }
+
+exit:
+    if (data != NULL)
+    {
+        PacketBuffer::Free(data);
+    }
+
+    if (err != BLE_NO_ERROR)
+    {
+        DoClose(closeFlags, err);
+    }
+
+    return err;
+}
+
+bool BLEEndPoint::SendWrite(PacketBuffer * buf)
+{
+    // Add reference to message fragment for duration of platform's GATT write attempt. Weave retains partial
+    // ownership of message fragment's PacketBuffer, since this is the same buffer as that of the whole message, just
+    // with a fragmenter-modified payload offset and data length. Buffer must be decref'd (i.e. PacketBuffer::Free'd) by
+    // platform when BLE GATT operation completes.
+    buf->AddRef();
+
+    SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+
+    return mBle->mPlatformDelegate->SendWriteRequest(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_1_ID, buf);
+}
+
+bool BLEEndPoint::SendIndication(PacketBuffer * buf)
+{
+    // Add reference to message fragment for duration of platform's GATT indication attempt. Weave retains partial
+    // ownership of message fragment's PacketBuffer, since this is the same buffer as that of the whole message, just
+    // with a fragmenter-modified payload offset and data length. Buffer must be decref'd (i.e. PacketBuffer::Free'd) by
+    // platform when BLE GATT operation completes.
+    buf->AddRef();
+
+    SetFlag(mConnStateFlags, kConnState_GattOperationInFlight, true);
+
+    return mBle->mPlatformDelegate->SendIndication(mConnObj, &WEAVE_BLE_SVC_ID, &mBle->WEAVE_BLE_CHAR_2_ID, buf);
+}
+
+BLE_ERROR BLEEndPoint::StartConnectTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    Weave::System::Error timerErr;
+
+    timerErr = mBle->mSystemLayer->StartTimer(BLE_CONNECT_TIMEOUT_MS, HandleConnectTimeout, this);
+    VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+    SetFlag(mTimerStateFlags, kTimerState_ConnectTimerRunning, true);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::StartReceiveConnectionTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    Weave::System::Error timerErr;
+
+    timerErr = mBle->mSystemLayer->StartTimer(BLE_CONNECT_TIMEOUT_MS, HandleReceiveConnectionTimeout, this);
+    VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+    SetFlag(mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, true);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::StartAckReceivedTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    Weave::System::Error timerErr;
+
+    if (!GetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning))
+    {
+        timerErr = mBle->mSystemLayer->StartTimer(BTP_ACK_RECEIVED_TIMEOUT_MS, HandleAckReceivedTimeout, this);
+        VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+
+        SetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning, true);
+    }
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::RestartAckReceivedTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    VerifyOrExit(GetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning), err = BLE_ERROR_INCORRECT_STATE);
+
+    StopAckReceivedTimer();
+
+    err = StartAckReceivedTimer();
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::StartSendAckTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    Weave::System::Error timerErr;
+
+    WeaveLogDebugBleEndPoint(Ble, "entered StartSendAckTimer");
+
+    if (!GetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning))
+    {
+        WeaveLogDebugBleEndPoint(Ble, "starting new SendAckTimer");
+        timerErr = mBle->mSystemLayer->StartTimer(BTP_ACK_SEND_TIMEOUT_MS, HandleSendAckTimeout, this);
+        VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+
+        SetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning, true);
+    }
+
+exit:
+    return err;
+}
+
+BLE_ERROR BLEEndPoint::StartUnsubscribeTimer()
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    Weave::System::Error timerErr;
+
+    timerErr = mBle->mSystemLayer->StartTimer(BLE_UNSUBSCRIBE_TIMEOUT_MS, HandleUnsubscribeTimeout, this);
+    VerifyOrExit(timerErr == WEAVE_SYSTEM_NO_ERROR, err = BLE_ERROR_START_TIMER_FAILED);
+    SetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, true);
+
+exit:
+    return err;
+}
+
+void BLEEndPoint::StopConnectTimer()
+{
+    // Cancel any existing connect timer.
+    mBle->mSystemLayer->CancelTimer(HandleConnectTimeout, this);
+    SetFlag(mTimerStateFlags, kTimerState_ConnectTimerRunning, false);
+}
+
+void BLEEndPoint::StopReceiveConnectionTimer()
+{
+    // Cancel any existing receive connection timer.
+    mBle->mSystemLayer->CancelTimer(HandleReceiveConnectionTimeout, this);
+    SetFlag(mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, false);
+}
+
+void BLEEndPoint::StopAckReceivedTimer()
+{
+    // Cancel any existing ack-received timer.
+    mBle->mSystemLayer->CancelTimer(HandleAckReceivedTimeout, this);
+    SetFlag(mTimerStateFlags, kTimerState_AckReceivedTimerRunning, false);
+}
+
+void BLEEndPoint::StopSendAckTimer()
+{
+    // Cancel any existing send-ack timer.
+    mBle->mSystemLayer->CancelTimer(HandleSendAckTimeout, this);
+    SetFlag(mTimerStateFlags, kTimerState_SendAckTimerRunning, false);
+}
+
+void BLEEndPoint::StopUnsubscribeTimer()
+{
+    // Cancel any existing unsubscribe timer.
+    mBle->mSystemLayer->CancelTimer(HandleUnsubscribeTimeout, this);
+    SetFlag(mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, false);
+}
+
+void BLEEndPoint::HandleConnectTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+    BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+    // Check for event-based timer race condition.
+    if (GetFlag(ep->mTimerStateFlags, kTimerState_ConnectTimerRunning))
+    {
+        WeaveLogError(Ble, "connect handshake timed out, closing ep %p", ep);
+        SetFlag(ep->mTimerStateFlags, kTimerState_ConnectTimerRunning, false);
+        ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CONNECT_TIMED_OUT);
+    }
+}
+
+void BLEEndPoint::HandleReceiveConnectionTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+    BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+    // Check for event-based timer race condition.
+    if (GetFlag(ep->mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning))
+    {
+        WeaveLogError(Ble, "receive handshake timed out, closing ep %p", ep);
+        SetFlag(ep->mTimerStateFlags, kTimerState_ReceiveConnectionTimerRunning, false);
+        ep->DoClose(kBleCloseFlag_SuppressCallback | kBleCloseFlag_AbortTransmission, BLE_ERROR_RECEIVE_TIMED_OUT);
+    }
+}
+
+void BLEEndPoint::HandleAckReceivedTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+    BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+    // Check for event-based timer race condition.
+    if (GetFlag(ep->mTimerStateFlags, kTimerState_AckReceivedTimerRunning))
+    {
+        WeaveLogError(Ble, "ack recv timeout, closing ep %p", ep);
+        ep->mWoBle.LogStateDebug();
+        SetFlag(ep->mTimerStateFlags, kTimerState_AckReceivedTimerRunning, false);
+        ep->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_FRAGMENT_ACK_TIMED_OUT);
+    }
+}
+
+void BLEEndPoint::HandleSendAckTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+    BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+    // Check for event-based timer race condition.
+    if (GetFlag(ep->mTimerStateFlags, kTimerState_SendAckTimerRunning))
+    {
+        SetFlag(ep->mTimerStateFlags, kTimerState_SendAckTimerRunning, false);
+
+        // If previous stand-alone ack isn't still in flight...
+        if (!GetFlag(ep->mConnStateFlags, kConnState_StandAloneAckInFlight))
+        {
+            BLE_ERROR sendErr = ep->DriveStandAloneAck();
+
+            if (sendErr != BLE_NO_ERROR)
+            {
+                ep->DoClose(kBleCloseFlag_AbortTransmission, sendErr);
+            }
+        }
+    }
+}
+
+void BLEEndPoint::HandleUnsubscribeTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err)
+{
+    BLEEndPoint * ep = static_cast<BLEEndPoint *>(appState);
+
+    // Check for event-based timer race condition.
+    if (GetFlag(ep->mTimerStateFlags, kTimerState_UnsubscribeTimerRunning))
+    {
+        WeaveLogError(Ble, "unsubscribe timed out, ble ep %p", ep);
+        SetFlag(ep->mTimerStateFlags, kTimerState_UnsubscribeTimerRunning, false);
+        ep->HandleUnsubscribeComplete();
+    }
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BLEEndPoint.h b/src/ble/BLEEndPoint.h
new file mode 100644
index 0000000..4894ef5
--- /dev/null
+++ b/src/ble/BLEEndPoint.h
@@ -0,0 +1,235 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file defines a Bluetooth Low Energy (BLE) connection
+ *      endpoint abstraction for the byte-streaming,
+ *      connection-oriented Weave over Bluetooth Low Energy (WoBLE)
+ *      Bluetooth Transport Protocol (BTP).
+ *
+ */
+
+#ifndef BLEENDPOINT_H_
+#define BLEENDPOINT_H_
+
+#include <SystemLayer/SystemMutex.h>
+
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include <BleLayer/WoBleTest.h>
+#endif
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+enum
+{
+    kBleCloseFlag_SuppressCallback  = 0x01,
+    kBleCloseFlag_AbortTransmission = 0x02
+};
+
+// Forward declarations
+class BleLayer;
+class BleEndPointPool;
+#if WEAVE_ENABLE_WOBLE_TEST
+class WoBleTest;
+#endif
+
+class NL_DLL_EXPORT BLEEndPoint : public BleLayerObject
+{
+    friend class BleLayer;
+    friend class BleEndPointPool;
+#if WEAVE_ENABLE_WOBLE_TEST
+    friend class WoBleTest;
+#endif
+
+public:
+    typedef uint64_t AlignT;
+
+    // Public data members:
+    enum
+    {
+        kState_Ready      = 0,
+        kState_Connecting = 1,
+        kState_Aborting   = 2,
+        kState_Connected  = 3,
+        kState_Closing    = 4,
+        kState_Closed     = 5
+    } mState; // [READ-ONLY] End point connection state. Refers to state of Weave over
+              // BLE transport protocol connection, not of underlying BLE connection.
+
+    // Public function pointers:
+    typedef void (*OnConnectCompleteFunct)(BLEEndPoint * endPoint, BLE_ERROR err);
+    OnConnectCompleteFunct OnConnectComplete;
+
+    typedef void (*OnMessageReceivedFunct)(BLEEndPoint * endPoint, PacketBuffer * msg);
+    OnMessageReceivedFunct OnMessageReceived;
+
+    typedef void (*OnConnectionClosedFunct)(BLEEndPoint * endPoint, BLE_ERROR err);
+    OnConnectionClosedFunct OnConnectionClosed;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    typedef void (*OnCommandReceivedFunct)(BLEEndPoint * endPoint, PacketBuffer * msg);
+    OnCommandReceivedFunct OnCommandReceived;
+    inline void SetOnCommandReceivedCB(OnCommandReceivedFunct cb) { OnCommandReceived = cb; };
+    WoBleTest mWoBleTest;
+    inline void SetTxWindowSize(uint8_t size) { mRemoteReceiveWindowSize = size; };
+    inline void SetRxWindowSize(uint8_t size) { mReceiveWindowMaxSize = size; };
+#endif
+
+public:
+    // Public functions:
+    BLE_ERROR Send(PacketBuffer * data);
+    BLE_ERROR Receive(PacketBuffer * data);
+    BLE_ERROR StartConnect(void);
+
+    bool IsUnsubscribePending(void) const;
+    void Close(void);
+    void Abort(void);
+
+private:
+    // Private data members:
+    enum ConnectionStateFlags
+    {
+        kConnState_AutoClose                = 0x01, // End point should close underlying BLE conn on BTP close.
+        kConnState_CapabilitiesConfReceived = 0x02, // GATT confirmation received for sent capabilities req/resp.
+        kConnState_CapabilitiesMsgReceived  = 0x04, // Capabilities request or response message received.
+        kConnState_DidBeginSubscribe        = 0x08, // GATT subscribe request sent; must unsubscribe on close.
+        kConnState_StandAloneAckInFlight    = 0x10, // Stand-alone ack in flight, awaiting GATT confirmation.
+        kConnState_GattOperationInFlight    = 0x20  // GATT write, indication, subscribe, or unsubscribe in flight,
+                                                    // awaiting GATT confirmation.
+    };
+
+    enum TimerStateFlags
+    {
+        kTimerState_ConnectTimerRunning           = 0x01, // BTP connect completion timer running.
+        kTimerState_ReceiveConnectionTimerRunning = 0x02, // BTP receive connection completion timer running.
+        kTimerState_AckReceivedTimerRunning       = 0x04, // Ack received timer running due to unacked sent fragment.
+        kTimerState_SendAckTimerRunning           = 0x08, // Send ack timer running; indicates pending ack to send.
+        kTimerState_UnsubscribeTimerRunning       = 0x10, // Unsubscribe completion timer running.
+#if WEAVE_ENABLE_WOBLE_TEST
+        kTimerState_UnderTestTimerRunnung = 0x80 // running throughput Tx test
+#endif
+    };
+
+    // BLE connection to which an end point is uniquely bound. Type BLE_CONNECTION_OBJECT is defined by the platform or
+    // void* by default. This object is passed back to the platform delegate with each call to send traffic over or
+    // modify the state of the underlying BLE connection.
+    BLE_CONNECTION_OBJECT mConnObj;
+
+    // Queue of outgoing messages to send when current WoBle transmission completes.
+    //
+    // Re-used during connection setup to cache capabilities request and response payloads; payloads are freed when
+    // connection is established.
+    PacketBuffer * mSendQueue;
+
+    // Pending stand-alone BTP acknolwedgement. Pre-empts regular send queue or fragmented message transmission in
+    // progress.
+    PacketBuffer * mAckToSend;
+
+    WoBle mWoBle;
+    BleRole mRole;
+    uint8_t mConnStateFlags;
+    uint8_t mTimerStateFlags;
+    SequenceNumber_t mLocalReceiveWindowSize;
+    SequenceNumber_t mRemoteReceiveWindowSize;
+    SequenceNumber_t mReceiveWindowMaxSize;
+#if WEAVE_ENABLE_WOBLE_TEST
+    nl::Weave::System::Mutex mTxQueueMutex; // For MT-safe Tx queuing
+#endif
+
+private:
+    // Private functions:
+    BLEEndPoint(void);  // not defined
+    ~BLEEndPoint(void); // not defined
+
+    BLE_ERROR Init(BleLayer * bleLayer, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose);
+    bool IsConnected(uint8_t state) const;
+    void DoClose(uint8_t flags, BLE_ERROR err);
+
+    // Transmit path:
+    BLE_ERROR DriveSending(void);
+    BLE_ERROR DriveStandAloneAck(void);
+    bool PrepareNextFragment(PacketBuffer * data, bool & sentAck);
+    BLE_ERROR SendNextMessage(void);
+    BLE_ERROR ContinueMessageSend(void);
+    BLE_ERROR DoSendStandAloneAck(void);
+    BLE_ERROR SendCharacteristic(PacketBuffer * buf);
+    bool SendIndication(PacketBuffer * buf);
+    bool SendWrite(PacketBuffer * buf);
+
+    // Receive path:
+    BLE_ERROR HandleConnectComplete(void);
+    BLE_ERROR HandleReceiveConnectionComplete(void);
+    void HandleSubscribeReceived(void);
+    void HandleSubscribeComplete(void);
+    void HandleUnsubscribeComplete(void);
+    BLE_ERROR HandleGattSendConfirmationReceived(void);
+    BLE_ERROR HandleHandshakeConfirmationReceived(void);
+    BLE_ERROR HandleFragmentConfirmationReceived(void);
+    BLE_ERROR HandleCapabilitiesRequestReceived(PacketBuffer * data);
+    BLE_ERROR HandleCapabilitiesResponseReceived(PacketBuffer * data);
+    SequenceNumber_t AdjustRemoteReceiveWindow(SequenceNumber_t lastReceivedAck, SequenceNumber_t maxRemoteWindowSize,
+                                               SequenceNumber_t newestUnackedSentSeqNum);
+
+    // Timer control functions:
+    BLE_ERROR StartConnectTimer(void);           // Start connect timer.
+    BLE_ERROR StartReceiveConnectionTimer(void); // Start receive connection timer.
+    BLE_ERROR StartAckReceivedTimer(void);       // Start ack-received timer if it's not already running.
+    BLE_ERROR RestartAckReceivedTimer(void);     // Restart ack-received timer.
+    BLE_ERROR StartSendAckTimer(void);           // Start send-ack timer if it's not already running.
+    BLE_ERROR StartUnsubscribeTimer(void);
+    void StopConnectTimer(void);           // Stop connect timer.
+    void StopReceiveConnectionTimer(void); // Stop receive connection timer.
+    void StopAckReceivedTimer(void);       // Stop ack-received timer.
+    void StopSendAckTimer(void);           // Stop send-ack timer.
+    void StopUnsubscribeTimer(void);       // Stop unsubscribe timer.
+
+    // Timer expired callbacks:
+    static void HandleConnectTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+    static void HandleReceiveConnectionTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+    static void HandleAckReceivedTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+    static void HandleSendAckTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+    static void HandleUnsubscribeTimeout(Weave::System::Layer * systemLayer, void * appState, Weave::System::Error err);
+
+    // Close functions:
+    void DoCloseCallback(uint8_t state, uint8_t flags, BLE_ERROR err);
+    void FinalizeClose(uint8_t state, uint8_t flags, BLE_ERROR err);
+    void ReleaseBleConnection(void);
+    void Free(void);
+    void FreeWoBle(void);
+
+    // Mutex lock on Tx queue. Used only in WoBle test build for now.
+#if WEAVE_ENABLE_WOBLE_TEST
+    inline void QueueTxLock() { mTxQueueMutex.Lock(); };
+    inline void QueueTxUnlock() { mTxQueueMutex.Unlock(); };
+#else
+    inline void QueueTxLock() { };
+    inline void QueueTxUnlock() { };
+#endif
+    void QueueTx(PacketBuffer * data, PacketType_t type);
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEENDPOINT_H_ */
diff --git a/src/ble/Ble.h b/src/ble/Ble.h
new file mode 100644
index 0000000..a653bc0
--- /dev/null
+++ b/src/ble/Ble.h
@@ -0,0 +1,48 @@
+/*
+ *
+ *    Copyright (c) 2015-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 is an umbrella header for the Ble library, a
+ *      portable Bluetooth Low Energy (BLE), also known as Bluetooth
+ *      Smart, layer for transporting Weave over a BLE connection.
+ *
+ */
+
+#ifndef NL_BLE_H
+#define NL_BLE_H
+
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BLEEndPoint.h>
+#include <BleLayer/BleError.h>
+#include <BleLayer/Ble.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/WoBle.h>
+
+/**
+ *   @namespace nl::Ble
+ *
+ *   @brief
+ *     This namespace includes all interfaces within Weave for
+ *     Bluetooth Low Energy (BLE), also known as Bluetooth Smart.
+ */
+
+#endif // NL_BLE_H
diff --git a/src/ble/BleApplicationDelegate.h b/src/ble/BleApplicationDelegate.h
new file mode 100644
index 0000000..c2f2111
--- /dev/null
+++ b/src/ble/BleApplicationDelegate.h
@@ -0,0 +1,49 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file defines the interface for upcalls from BleLayer
+ *      to a client application.
+ */
+
+#ifndef BLEAPPLICATIONDELEGATE_H_
+#define BLEAPPLICATIONDELEGATE_H_
+
+#include <Weave/Support/NLDLLUtil.h>
+
+#include "BleConfig.h"
+
+namespace nl {
+namespace Ble {
+
+// Platform-agnostic BLE interface
+class NL_DLL_EXPORT BleApplicationDelegate
+{
+public:
+    // Weave calls this function once it closes the last BLEEndPoint associated with a BLE given connection object.
+    // A call to this function means Weave no longer cares about the state of the given BLE connection.
+    // The application can use this callback to e.g. close the underlying BLE conection if it is no longer needed,
+    // decrement the connection's refcount if it has one, or perform any other sort of cleanup as desired.
+    virtual void NotifyWeaveConnectionClosed(BLE_CONNECTION_OBJECT connObj) = 0;
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEAPPLICATIONDELEGATE_H_ */
diff --git a/src/ble/BleConfig.h b/src/ble/BleConfig.h
new file mode 100644
index 0000000..0a4bf56
--- /dev/null
+++ b/src/ble/BleConfig.h
@@ -0,0 +1,254 @@
+/*
+ *
+ *    Copyright (c) 2014-2018 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 defines default compile-time configuration constants
+ *      for the Nest BleLayer, a Bluetooth Low Energy communications
+ *      abstraction layer.
+ *
+ *      Package integrators that wish to override these values should
+ *      either use preprocessor definitions or create a project-
+ *      specific BleProjectConfig.h header and then assert
+ *      HAVE_BLEPROJECTCONFIG_H via the package configuration tool
+ *      via --with-weave-ble-project-includes=DIR where DIR is the
+ *      directory that contains the header.
+ *
+ *  NOTE WELL: On some platforms, this header is included by C-language programs.
+ *
+ */
+
+#ifndef BLECONFIG_H_
+#define BLECONFIG_H_
+
+#include <SystemLayer/SystemConfig.h>
+
+/* Include a project-specific configuration file, if defined.
+ *
+ * An application or module that incorporates Weave can define a project configuration
+ * file to override standard BLE Layer configuration with application-specific values.
+ * The project config file is typically located outside the OpenWeave source tree,
+ * alongside the source code for the application.
+ */
+#ifdef BLE_PROJECT_CONFIG_INCLUDE
+#include BLE_PROJECT_CONFIG_INCLUDE
+#endif
+
+/* Include a platform-specific configuration file, if defined.
+ *
+ * A platform configuration file contains overrides to standard BLE Layer configuration
+ * that are specific to the platform or OS on which Weave is running.  It is typically
+ * provided as apart of an adaptation layer that adapts OpenWeave to the target
+ * environment.  This adaptation layer may be included in the OpenWeave source tree
+ * itself or implemented externally.
+ */
+#ifdef BLE_PLATFORM_CONFIG_INCLUDE
+#include BLE_PLATFORM_CONFIG_INCLUDE
+#endif
+
+// clang-format off
+
+/**
+ *  @def BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+ *
+ *  @brief
+ *    This boolean configuration option is (1) if the obsolescent interfaces
+ *    of the BLE layer that now reside elsewhere, for example, in the Weave System
+ *    Layer or the INET layer, are aliased for transitional purposes.
+ *
+ */
+#ifndef BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#define BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES          0
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#if !WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#error "REQUIRED: if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES then WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES!"
+#endif // !WEAVE_SYSTEM_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+#include <InetLayer/InetBuffer.h>
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+/**
+ *  @def BLE_LAYER_NUM_BLE_ENDPOINTS
+ *
+ *  @brief
+ *    This defines the number of BLEEndPoint objects allocated for use by the
+ *    BleLayer subsystem. Value should be defined as the minimum of (max number
+ *    of simultaneous BLE connections the system supports, max number of
+ *    simultaneous BLE connections the application will establish).
+ */
+#ifndef BLE_LAYER_NUM_BLE_ENDPOINTS
+#define BLE_LAYER_NUM_BLE_ENDPOINTS 1
+#endif // BLE_LAYER_NUM_BLE_ENDPOINTS
+
+#if (BLE_LAYER_NUM_BLE_ENDPOINTS < 1)
+#error "BLE_LAYER_NUM_BLE_ENDPOINTS must be greater than 0. configure options may be used to disable Weave over BLE."
+#endif
+
+/**
+ *  @def BLE_CONNECTION_OBJECT
+ *
+ *  @brief
+ *    This defines the type of BLE_CONNECTION_OBJECT parameters passed between
+ *    BLE platform code and the BleLayer subsystem.
+ *
+ *    This type must support operator == such that BLE_CONNECTION_OBJECT instances
+ *    which refer to the same BLE connection are considered equivalent.
+ *
+ *    Most platforms should be able to retain this type's default definition as
+ *    (void *), and pass [pointers to] connection handles generated by their
+ *    platform interface where BLE_CONNECTION_OBJECT arguments are required by
+ *    BleLayer input functions.
+ *
+ */
+#ifndef BLE_CONNECTION_OBJECT
+#define BLE_CONNECTION_OBJECT void*
+#endif // BLE_CONNECTION_OBJECT
+
+/**
+ *  @def BLE_CONFIG_BLUEZ_MTU_FEATURE
+ *
+ *  @brief
+ *    This define if BLUEZ MTU FEATURE is enabled or not
+ */
+#ifndef BLE_CONFIG_BLUEZ_MTU_FEATURE
+#define BLE_CONFIG_BLUEZ_MTU_FEATURE 0
+#endif // BLE_CONFIG_BLUEZ_MTU_FEATURE
+
+/**
+ *  @def BLE_CONNECTION_UNINITIALIZED
+ *
+ *  @brief
+ *    This defines the value of an uninitialized BLE_CONNECTION_OBJECT.
+ *
+ */
+#ifndef BLE_CONNECTION_UNINITIALIZED
+#define BLE_CONNECTION_UNINITIALIZED NULL
+#endif // BLE_CONNECTION_UNINITIALIZED
+
+/**
+ *  @def BLE_READ_REQUEST_CONTEXT
+ *
+ *  @brief
+ *    This defines the type of BLE_READ_REQUEST_CONTEXT parameters passed between
+ *    BLE platform code and the BleLayer subsystem.
+ *
+ *    BLE_READ_REQUEST_CONTEXT objects are handed to BleLayer when a read request
+ *    is received by the BLE platform. BleLayer hands these objects back to the
+ *    appropriate platform delegate function when sending the read response.
+ *
+ */
+#ifndef BLE_READ_REQUEST_CONTEXT
+#define BLE_READ_REQUEST_CONTEXT void*
+#endif // BLE_READ_REQUEST_CONTEXT
+
+/**
+ *  @def BLE_MAX_RECEIVE_WINDOW_SIZE
+ *
+ *  @brief
+ *    This is the maximum allowed size of a BLE end point's receive window, defined as the number of fragments the
+ *    end point may reliably receive without BTP-layer acknowledgement. This value should be no larger than the floor
+ *    of ONE-HALF the total number of slots or buffers reserved for GATT operations at any point along a platform's
+ *    BLE pipeline. The BLE layer reserves all of these buffers for its own use - one half for incoming GATT writes or
+ *    indications, and the other half for incoming GATT confirmations.
+ *
+ *    This value must be greater than 1, or race condition avoidance logic will prevent send the on remote device. This
+ *    logic prevents any send with no piggybacked ack when the receiver's window has only 1 slot open. Without this
+ *    logic, simultaneous data transmissions could fill both receiver's windows, leaving no room for the acks required
+ *    to re-open them. Both senders would wedge, and the BTP connection would stall.
+ *
+ *    This value must also exceed (BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD + 1), or ***immediate*** stand-alone
+ *    acks will forever be sent without delay in response to one another as each peer's window size dips below
+ *    BLE_CONFIG_IMMEDIATE_ACK_WINDOW_THRESHOLD with receipt of any single message fragment.
+ *
+ *    Default value of 3 is absolute minimum for stable performance, and an attempt to ensure safe window sizes on new
+ *    platforms.
+ *
+ */
+#ifndef BLE_MAX_RECEIVE_WINDOW_SIZE
+#define BLE_MAX_RECEIVE_WINDOW_SIZE                            3
+#endif
+
+#if (BLE_MAX_RECEIVE_WINDOW_SIZE < 3)
+#error "BLE_MAX_RECEIVE_WINDOW_SIZE must be greater than 2 for BLE transport protocol stability."
+#endif
+
+/**
+ *  @def BLE_CONFIG_ERROR_TYPE
+ *
+ *  @brief
+ *    This defines the data type used to represent errors for the
+ *    BleLayer subsystem.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_TYPE
+#include <stdint.h>
+#define BLE_CONFIG_ERROR_TYPE                              int32_t
+#endif // BLE_CONFIG_ERROR_TYPE
+
+/**
+ *  @def BLE_CONFIG_NO_ERROR
+ *
+ *  @brief
+ *    This defines the BleLayer error code for no error or success.
+ *
+ */
+#ifndef BLE_CONFIG_NO_ERROR
+#define BLE_CONFIG_NO_ERROR                                0
+#endif // BLE_CONFIG_NO_ERROR
+
+/**
+ *  @def BLE_CONFIG_ERROR_MIN
+ *
+ *  @brief
+ *    This defines the base or minimum BleLayer error number range.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_MIN
+#define BLE_CONFIG_ERROR_MIN                               6000
+#endif // BLE_CONFIG_ERROR_MIN
+
+/**
+ *  @def BLE_CONFIG_ERROR_MAX
+ *
+ *  @brief
+ *    This defines the top or maximum BleLayer error number range.
+ *
+ */
+#ifndef BLE_CONFIG_ERROR_MAX
+#define BLE_CONFIG_ERROR_MAX                               6999
+#endif // BLE_CONFIG_ERROR_MAX
+
+/**
+ *  @def _BLE_CONFIG_ERROR
+ *
+ *  @brief
+ *    This defines a mapping function for BleLayer errors that allows
+ *    mapping such errors into a platform- or system-specific range.
+ *
+ */
+#ifndef _BLE_CONFIG_ERROR
+#define _BLE_CONFIG_ERROR(e)                               (BLE_ERROR_MIN + (e))
+#endif // _BLE_CONFIG_ERROR
+
+// clang-format on
+
+#include <Weave/Core/WeaveConfig.h>
+
+#endif /* BLECONFIG_H_ */
diff --git a/src/ble/BleError.cpp b/src/ble/BleError.cpp
new file mode 100644
index 0000000..9d86de6
--- /dev/null
+++ b/src/ble/BleError.cpp
@@ -0,0 +1,107 @@
+/*
+ *
+ *    Copyright (c) 2019 Google LLC.
+ *    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 contains functions for working with BLE Layer errors.
+ */
+
+#include <stddef.h>
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BleError.h>
+
+#include <Weave/Support/ErrorStr.h>
+
+namespace nl {
+namespace Ble {
+
+/**
+ * Register a text error formatter for BLE Layer errors.
+ */
+void RegisterBleLayerErrorFormatter(void)
+{
+    static ErrorFormatter sBleLayerErrorFormatter =
+    {
+        FormatBleLayerError,
+        NULL
+    };
+
+    RegisterErrorFormatter(&sBleLayerErrorFormatter);
+}
+
+bool FormatBleLayerError(char * buf, uint16_t bufSize, int32_t err)
+{
+    const char * desc = NULL;
+
+    if (err < BLE_ERROR_MIN || err > BLE_ERROR_MAX)
+    {
+        return false;
+    }
+
+#if !WEAVE_CONFIG_SHORT_ERROR_STR
+    switch (err)
+    {
+    case BLE_ERROR_BAD_ARGS                                     : desc = "Bad arguments"; break;
+    case BLE_ERROR_INCORRECT_STATE                              : desc = "Incorrect state"; break;
+    case BLE_ERROR_NO_ENDPOINTS                                 : desc = "No more BLE endpoints"; break;
+    case BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK              : desc = "No Weave over BLE connection received callback set"; break;
+    case BLE_ERROR_CENTRAL_UNSUBSCRIBED                         : desc = "BLE central unsubscribed"; break;
+    case BLE_ERROR_GATT_SUBSCRIBE_FAILED                        : desc = "GATT subscribe operation failed"; break;
+    case BLE_ERROR_GATT_UNSUBSCRIBE_FAILED                      : desc = "GATT unsubscribe operation failed"; break;
+    case BLE_ERROR_GATT_WRITE_FAILED                            : desc = "GATT write characteristic operation failed"; break;
+    case BLE_ERROR_GATT_INDICATE_FAILED                         : desc = "GATT indicate characteristic operation failed"; break;
+    case BLE_ERROR_NOT_IMPLEMENTED                              : desc = "Not implemented"; break;
+    case BLE_ERROR_WOBLE_PROTOCOL_ABORT                         : desc = "BLE transport protocol fired abort"; break;
+    case BLE_ERROR_REMOTE_DEVICE_DISCONNECTED                   : desc = "Remote device closed BLE connection"; break;
+    case BLE_ERROR_APP_CLOSED_CONNECTION                        : desc = "Application closed BLE connection"; break;
+    case BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG                     : desc = "Outbound message too big"; break;
+    case BLE_ERROR_NOT_WEAVE_DEVICE                             : desc = "BLE device doesn't seem to support Weave"; break;
+    case BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS               : desc = "Incompatible BLE transport protocol versions"; break;
+    case BLE_ERROR_NO_MEMORY                                    : desc = "No memory"; break;
+    case BLE_ERROR_MESSAGE_INCOMPLETE                           : desc = "Message incomplete"; break;
+    case BLE_ERROR_INVALID_FRAGMENT_SIZE                        : desc = "Invalid fragment size"; break;
+    case BLE_ERROR_START_TIMER_FAILED                           : desc = "Start timer failed"; break;
+    case BLE_ERROR_CONNECT_TIMED_OUT                            : desc = "Connect handshake timed out"; break;
+    case BLE_ERROR_RECEIVE_TIMED_OUT                            : desc = "Receive handshake timed out"; break;
+    case BLE_ERROR_INVALID_MESSAGE                              : desc = "Invalid message"; break;
+    case BLE_ERROR_FRAGMENT_ACK_TIMED_OUT                       : desc = "Message fragment acknowledgement timed out"; break;
+    case BLE_ERROR_KEEP_ALIVE_TIMED_OUT                         : desc = "Keep-alive receipt timed out"; break;
+    case BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK                : desc = "Missing required callback"; break;
+    case BLE_ERROR_INVALID_ACK                                  : desc = "Received invalid BLE transport protocol fragment acknowledgement"; break;
+    case BLE_ERROR_REASSEMBLER_MISSING_DATA                     : desc = "BLE message reassembler did not receive enough data"; break;
+    case BLE_ERROR_INVALID_BTP_HEADER_FLAGS                     : desc = "Received invalid BLE transport protocol header flags"; break;
+    case BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER                  : desc = "Received invalid BLE transport protocol sequence number"; break;
+    case BLE_ERROR_REASSEMBLER_INCORRECT_STATE                  : desc = "BLE message reassembler received packet in incorrect state"; break;
+    case BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG                     : desc = "Message received by BLE message reassembler was too large"; break;
+    }
+#endif // !WEAVE_CONFIG_SHORT_ERROR_STR
+
+    FormatError(buf, bufSize, "Ble", err, desc);
+
+    return true;
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif // CONFIG_NETWORK_LAYER_BLE
diff --git a/src/ble/BleError.h b/src/ble/BleError.h
new file mode 100644
index 0000000..df00d3d
--- /dev/null
+++ b/src/ble/BleError.h
@@ -0,0 +1,430 @@
+/*
+ *
+ *    Copyright (c) 2015-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 defines constants for the Nest BleLayer subsystem.
+ *
+ *      Error types, ranges, and mappings overrides may be made by
+ *      defining the appropriate BLE_CONFIG_* or _BLE_CONFIG_*
+ *      macros.
+ *
+ *  NOTE WELL: On some platforms, this header is included by C-language programs.
+ *
+ */
+
+#ifndef BLEERROR_H_
+#define BLEERROR_H_
+
+#include "BleConfig.h"
+
+// clang-format off
+
+/**
+ *  @def BLE_NO_ERROR
+ *
+ *  @brief
+ *    This defines the BleLayer error code for success or no
+ *    error. This value may be configured via #BLE_CONFIG_NO_ERROR.
+ *
+ */
+#define BLE_NO_ERROR                   BLE_CONFIG_NO_ERROR
+
+/**
+ *  @def BLE_ERROR_MIN
+ *
+ *  @brief
+ *    This defines the base or minimum value of the BleLayer error number
+ *    range. This value may be configured via #BLE_CONFIG_ERROR_MIN.
+ *
+ */
+#define BLE_ERROR_MIN                  BLE_CONFIG_ERROR_MIN
+
+/**
+ *  @def BLE_ERROR_MAX
+ *
+ *  @brief
+ *    This defines the top or maximum value of the BleLayer error number
+ *    range. This value may be configured via #BLE_CONFIG_ERROR_MAX.
+ *
+ */
+#define BLE_ERROR_MAX                  BLE_CONFIG_ERROR_MAX
+
+/**
+ *  @def _BLE_ERROR(e)
+ *
+ *  @brief
+ *    This defines a mapping function for BleLayer errors that allows
+ *    mapping such errors into a platform- or system-specific
+ *    range. This function may be configured via
+ *    #_BLE_CONFIG_ERROR.
+ *
+ *  @param[in]  e  The BleLayer error to map.
+ *
+ *  @return The mapped BleLayer error.
+ *
+ */
+#define _BLE_ERROR(e)                  _BLE_CONFIG_ERROR(e)
+
+/**
+ *  @typedef BLE_ERROR
+ *
+ *  The basic type for all BleLayer errors.
+ *
+ *  This is defined to a platform- or system-specific type.
+ *
+ */
+typedef BLE_CONFIG_ERROR_TYPE BLE_ERROR;
+
+/**
+ *  @name Error Definitions
+ *
+ *  @{
+ */
+
+/**
+ *  @def BLE_ERROR_BAD_ARGS
+ *
+ *  @brief
+ *    An invalid argument or arguments were supplied.
+ *
+ */
+#define BLE_ERROR_BAD_ARGS                                 _BLE_ERROR(0)
+
+/**
+ *  @def BLE_ERROR_INCORRECT_STATE
+ *
+ *  @brief
+ *    An unexpected state was encountered.
+ *
+ */
+#define BLE_ERROR_INCORRECT_STATE                          _BLE_ERROR(1)
+
+/**
+ *  @def BLE_ERROR_NO_ENDPOINTS
+ *
+ *  @brief
+ *    No BLE endpoint is available.
+ *
+ */
+#define BLE_ERROR_NO_ENDPOINTS                             _BLE_ERROR(2)
+
+/**
+ *  @def BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK
+ *
+ *  @brief
+ *    No callback was registered to receive a BLE Transport Protocol (BTP)
+ *    connection.
+ *
+ */
+#define BLE_ERROR_NO_CONNECTION_RECEIVED_CALLBACK          _BLE_ERROR(3)
+
+/**
+ *  @def BLE_ERROR_CENTRAL_UNSUBSCRIBED
+ *
+ *  @brief
+ *    A BLE central device unsubscribed from a peripheral device's BLE
+ *    Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_CENTRAL_UNSUBSCRIBED                     _BLE_ERROR(4)
+
+/**
+ *  @def BLE_ERROR_GATT_SUBSCRIBE_FAILED
+ *
+ *  @brief
+ *    A BLE central device failed to subscribe to a peripheral device's BLE
+ *    Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_GATT_SUBSCRIBE_FAILED                    _BLE_ERROR(5)
+
+/**
+ *  @def BLE_ERROR_GATT_UNSUBSCRIBE_FAILED
+ *
+ *  @brief
+ *    A BLE central device failed to unsubscribe from a peripheral device's
+ *    BLE Transport Protocol (BTP) transmit characteristic.
+ *
+ */
+#define BLE_ERROR_GATT_UNSUBSCRIBE_FAILED                  _BLE_ERROR(6)
+
+/**
+ *  @def BLE_ERROR_GATT_WRITE_FAILED
+ *
+ *  @brief
+ *    A General Attribute Profile (GATT) write operation failed.
+ *
+ */
+#define BLE_ERROR_GATT_WRITE_FAILED                        _BLE_ERROR(7)
+
+/**
+ *  @def BLE_ERROR_GATT_INDICATE_FAILED
+ *
+ *  @brief
+ *    A General Attribute Profile (GATT) indicate operation failed.
+ *
+ */
+#define BLE_ERROR_GATT_INDICATE_FAILED                     _BLE_ERROR(8)
+
+/**
+ *  @def BLE_ERROR_NOT_IMPLEMENTED
+ *
+ *  @brief
+ *    A requested function or feature is not implemented.
+ *
+ */
+#define BLE_ERROR_NOT_IMPLEMENTED                          _BLE_ERROR(9)
+
+/*
+ * Unused                                                  _BLE_ERROR(10)
+ */
+
+/**
+ *  @def BLE_ERROR_WOBLE_PROTOCOL_ABORT
+ *
+ *  @brief
+ *    A BLE Transport Protocol (BTP) error was encountered.
+ *
+ */
+#define BLE_ERROR_WOBLE_PROTOCOL_ABORT                     _BLE_ERROR(11)
+
+/**
+ *  @def BLE_ERROR_REMOTE_DEVICE_DISCONNECTED
+ *
+ *  @brief
+ *    A remote BLE connection peer disconnected, either actively or due to the
+ *    expiration of a BLE connection supervision timeout.
+ *
+ */
+#define BLE_ERROR_REMOTE_DEVICE_DISCONNECTED               _BLE_ERROR(12)
+
+/**
+ *  @def BLE_ERROR_APP_CLOSED_CONNECTION
+ *
+ *  @brief
+ *    The local application closed a BLE connection, and has informed BleLayer.
+ *
+ */
+#define BLE_ERROR_APP_CLOSED_CONNECTION                    _BLE_ERROR(13)
+
+/**
+ *  @def BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG
+ *
+ *  @brief
+ *    More outbound message data is pending than available buffer space
+ *    available to copy it.
+ *
+ */
+#define BLE_ERROR_OUTBOUND_MESSAGE_TOO_BIG                 _BLE_ERROR(14)
+
+/**
+ *  @def BLE_ERROR_NOT_WEAVE_DEVICE
+ *
+ *  @brief
+ *    A BLE peripheral device did not expose the General Attribute Profile
+ *    (GATT) service required by the Bluetooth Transport Protocol (BTP).
+ *
+ */
+#define BLE_ERROR_NOT_WEAVE_DEVICE                         _BLE_ERROR(15)
+
+/**
+ *  @def BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS
+ *
+ *  @brief
+ *    A remote device does not offer a compatible version of the Bluetooth
+ *    Transport Protocol (BTP).
+ *
+ */
+#define BLE_ERROR_INCOMPATIBLE_PROTOCOL_VERSIONS           _BLE_ERROR(16)
+
+/**
+ *  @def BLE_ERROR_NO_MEMORY
+ *
+ *  @brief
+ *    A request for memory could not be fulfilled.
+ *
+ */
+#define BLE_ERROR_NO_MEMORY                                _BLE_ERROR(17)
+
+/**
+ *  @def BLE_ERROR_MESSAGE_INCOMPLETE
+ *
+ *  @brief
+ *    A received Bluetooth Transport Protocol (BTP) message was incomplete.
+ *
+ */
+#define BLE_ERROR_MESSAGE_INCOMPLETE                       _BLE_ERROR(18)
+
+/**
+ *  @def BLE_ERROR_INVALID_FRAGMENT_SIZE
+ *
+ *  @brief
+ *    A remote device selected in invalid Bluetooth Transport Protocol (BTP)
+ *    fragment size.
+ *
+ */
+#define BLE_ERROR_INVALID_FRAGMENT_SIZE                    _BLE_ERROR(19)
+
+/**
+ *  @def BLE_ERROR_START_TIMER_FAILED
+ *
+ *  @brief
+ *    A timer failed to start within BleLayer.
+ *
+ */
+#define BLE_ERROR_START_TIMER_FAILED                       _BLE_ERROR(20)
+
+/**
+ *  @def BLE_ERROR_CONNECT_TIMED_OUT
+ *
+ *  @brief
+ *    A remote BLE peripheral device's Bluetooth Transport Protocol (BTP)
+ *    connect handshake response timed out.
+ *
+ */
+#define BLE_ERROR_CONNECT_TIMED_OUT                        _BLE_ERROR(21)
+
+/**
+ *  @def BLE_ERROR_RECEIVE_TIMED_OUT
+ *
+ *  @brief
+ *    A remote BLE central device's Bluetooth Transport Protocol (BTP) connect
+ *    handshake timed out.
+ *
+ */
+#define BLE_ERROR_RECEIVE_TIMED_OUT                        _BLE_ERROR(22)
+
+/**
+ *  @def BLE_ERROR_INVALID_MESSAGE
+ *
+ *  @brief
+ *    An invalid Bluetooth Transport Protocol (BTP) message was received.
+ *
+ */
+#define BLE_ERROR_INVALID_MESSAGE                          _BLE_ERROR(23)
+
+/**
+ *  @def BLE_ERROR_FRAGMENT_ACK_TIMED_OUT
+ *
+ *  @brief
+ *    Receipt of an expected Bluetooth Transport Protocol (BTP) fragment
+ *    acknowledgement timed out.
+ *
+ */
+#define BLE_ERROR_FRAGMENT_ACK_TIMED_OUT                   _BLE_ERROR(24)
+
+/**
+ *  @def BLE_ERROR_KEEP_ALIVE_TIMED_OUT
+ *
+ *  @brief
+ *    Receipt of an expected Bluetooth Transport Protocol (BTP) keep-alive
+ *    fragment timed out.
+ *
+ */
+#define BLE_ERROR_KEEP_ALIVE_TIMED_OUT                     _BLE_ERROR(25)
+
+/**
+ *  @def BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK
+ *
+ *  @brief
+ *    No callback was registered to handle Bluetooth Transport Protocol (BTP)
+ *    connect completion.
+ *
+ */
+#define BLE_ERRROR_NO_CONNECT_COMPLETE_CALLBACK            _BLE_ERROR(26)
+
+/**
+ *  @def BLE_ERROR_INVALID_ACK
+ *
+ *  @brief
+ *    A Bluetooth Transport Protcol (BTP) fragment acknowledgement was invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_ACK                              _BLE_ERROR(27)
+
+/**
+ *  @def BLE_ERROR_REASSEMBLER_MISSING_DATA
+ *
+ *  @brief
+ *    A Bluetooth Transport Protocol (BTP) end-of-message fragment was
+ *    received, but the total size of the received fragments is less than
+ *    the indicated size of the original fragmented message.
+ *
+ */
+#define BLE_ERROR_REASSEMBLER_MISSING_DATA                 _BLE_ERROR(28)
+
+/**
+ *  @def BLE_ERROR_INVALID_BTP_HEADER_FLAGS
+ *
+ *  @brief
+ *    A set of Bluetooth Transport Protocol (BTP) header flags is invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_BTP_HEADER_FLAGS                 _BLE_ERROR(29)
+
+/**
+ *  @def BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER
+ *
+ *  @brief
+ *    A Bluetooth Transport Protocol (BTP) fragment sequence number is invalid.
+ *
+ */
+#define BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER              _BLE_ERROR(30)
+
+/**
+ *  @def BLE_ERROR_REASSEMBLER_INCORRECT_STATE
+ *
+ *  @brief
+ *    The Bluetooth Transport Protocol (BTP) message reassembly engine
+ *    encountered an unexpected state.
+ *
+ */
+#define BLE_ERROR_REASSEMBLER_INCORRECT_STATE              _BLE_ERROR(31)
+
+/**
+ *  @def BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG
+ *
+ *  @brief
+ *    More inbound message data is pending than available buffer space
+ *    available to copy it.
+ *
+ */
+#define BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG                 _BLE_ERROR(32)
+
+// !!!!! IMPORTANT !!!!!  If you add new Ble errors, please update the translation
+// of error codes to strings in BleError.cpp, and add them to unittest
+// in test-apps/TestErrorStr.cpp
+
+/**
+ *  @}
+ */
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+extern void RegisterBleLayerErrorFormatter(void);
+extern bool FormatBleLayerError(char * buf, uint16_t bufSize, int32_t err);
+
+} /* namespace Ble */
+} /* namespace nl */
+
+
+#endif /* BLEERROR_H_ */
diff --git a/src/ble/BleLayer.am b/src/ble/BleLayer.am
new file mode 100644
index 0000000..21bb8b3
--- /dev/null
+++ b/src/ble/BleLayer.am
@@ -0,0 +1,39 @@
+#
+#    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 Nest BleLayer
+#      library sources.
+#
+#      These sources are shared by other SDK makefiles and consequently
+#      must be anchored relative to the top build directory.
+#
+
+nl_BleLayer_sources                                       = \
+    @top_builddir@/src/ble/BleLayer.cpp                     \
+    @top_builddir@/src/ble/BLEEndPoint.cpp                  \
+    @top_builddir@/src/ble/WoBle.cpp                        \
+    @top_builddir@/src/ble/BleUUID.cpp                      \
+    @top_builddir@/src/ble/BleError.cpp                     \
+    $(NULL)
+
+if WEAVE_ENABLE_WOBLE_TEST
+nl_BleLayer_sources                                      += \
+    @top_builddir@/src/device-manager/WoBleTest.cpp         \
+    $(NULL)
+endif # WEAVE_ENABLE_WOBLE_TEST
diff --git a/src/ble/BleLayer.cpp b/src/ble/BleLayer.cpp
new file mode 100644
index 0000000..83b25f0
--- /dev/null
+++ b/src/ble/BleLayer.cpp
@@ -0,0 +1,738 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file implements objects which provide an abstraction layer between
+ *      a platform's Bluetooth Low Energy (BLE) implementation and the Weave
+ *      stack.
+ *
+ *      The BleLayer obect accepts BLE data and control input from the
+ *      application via a functional interface. It performs the fragmentation
+ *      and reassembly required to transmit Weave message via a BLE GATT
+ *      characteristic interface, and drives incoming messages up the Weave
+ *      stack.
+ *
+ *      During initialization, the BleLayer object requires a pointer to the
+ *      platform's implementation of the BlePlatformDelegate and
+ *      BleApplicationDelegate objects.
+ *
+ *      The BlePlatformDelegate provides the Weave stack with an interface
+ *      by which to form and cancel GATT subscriptions, read and write
+ *      GATT characteristic values, send GATT characteristic notifications,
+ *      respond to GATT read requests, and close BLE connections.
+ *
+ *      The BleApplicationDelegate provides a mechanism for Weave to inform
+ *      the application when it has finished using a given BLE connection,
+ *      i.e when the WeaveConnection object wrapping this connection has
+ *      closed. This allows the application to either close the BLE connection
+ *      or continue to keep it open for non-Weave purposes.
+ *
+ *      To enable Weave over BLE for a new platform, the application developer
+ *      must provide an implementation for both delegates, provides points to
+ *      instances of these delegates on startup, and ensure that the
+ *      application calls the necessary BleLayer functions when appropriate to
+ *      drive BLE data and control input up the stack.
+ */
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <string.h>
+
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleLayer.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/BLEEndPoint.h>
+
+#include <Weave/Core/WeaveEncoding.h>
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+
+// clang-format off
+
+#define CAPABILITIES_REQUEST_MAGICNUM_LEN 2
+#define CAPABILITIES_REQUEST_L2CAP_MTU_LEN 2
+#define CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN 4
+#define CAPABILITIES_REQUEST_WINDOW_SIZE_LEN 1
+#define CAPABILITIES_REQUEST_LEN (CAPABILITIES_REQUEST_MAGICNUM_LEN + \
+                                  CAPABILITIES_REQUEST_L2CAP_MTU_LEN + \
+                                  CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN + \
+                                  CAPABILITIES_REQUEST_WINDOW_SIZE_LEN)
+
+#define CAPABILITIES_RESPONSE_MAGICNUM_LEN 2
+#define CAPABILITIES_RESPONSE_L2CAP_MTU_LEN 2
+#define CAPABILITIES_RESPONSE_SELECTED_PROTOCOL_VERSION_LEN 1
+#define CAPABILITIES_RESPONSE_WINDOW_SIZE_LEN 1
+#define CAPABILITIES_RESPONSE_LEN (CAPABILITIES_RESPONSE_MAGICNUM_LEN + \
+                                   CAPABILITIES_RESPONSE_L2CAP_MTU_LEN + \
+                                   CAPABILITIES_RESPONSE_SELECTED_PROTOCOL_VERSION_LEN + \
+                                   CAPABILITIES_RESPONSE_WINDOW_SIZE_LEN)
+
+// Magic values expected in first 2 bytes of valid BLE transport capabilities request or response:
+#define CAPABILITIES_MSG_CHECK_BYTE_1 'n'
+#define CAPABILITIES_MSG_CHECK_BYTE_2 'l'
+
+// clang-format on
+
+namespace nl {
+namespace Ble {
+
+class BleEndPointPool
+{
+public:
+    int Size() const { return BLE_LAYER_NUM_BLE_ENDPOINTS; }
+
+    BLEEndPoint * Get(int i) const
+    {
+        static union
+        {
+            uint8_t Pool[sizeof(BLEEndPoint) * BLE_LAYER_NUM_BLE_ENDPOINTS];
+            BLEEndPoint::AlignT ForceAlignment;
+        } sEndPointPool;
+
+        if (i < BLE_LAYER_NUM_BLE_ENDPOINTS)
+        {
+            return (BLEEndPoint *) (sEndPointPool.Pool + (sizeof(BLEEndPoint) * i));
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+
+    BLEEndPoint * Find(BLE_CONNECTION_OBJECT c)
+    {
+        if (c == BLE_CONNECTION_UNINITIALIZED)
+        {
+            return NULL;
+        }
+
+        for (int i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
+        {
+            BLEEndPoint * elem = Get(i);
+            if (elem->mBle != NULL && elem->mConnObj == c)
+            {
+                return elem;
+            }
+        }
+
+        return NULL;
+    }
+
+    BLEEndPoint * GetFree() const
+    {
+        for (int i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
+        {
+            BLEEndPoint * elem = Get(i);
+            if (elem->mBle == NULL)
+            {
+                return elem;
+            }
+        }
+        return NULL;
+    }
+};
+
+// EndPoint Pools
+//
+static BleEndPointPool sBLEEndPointPool;
+
+// UUIDs used internally by BleLayer:
+
+const WeaveBleUUID BleLayer::WEAVE_BLE_CHAR_1_ID = { { // 18EE2EF5-263D-4559-959F-4F9C429F9D11
+                                                       0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42,
+                                                       0x9F, 0x9D, 0x11 } };
+
+const WeaveBleUUID BleLayer::WEAVE_BLE_CHAR_2_ID = { { // 18EE2EF5-263D-4559-959F-4F9C429F9D12
+                                                       0x18, 0xEE, 0x2E, 0xF5, 0x26, 0x3D, 0x45, 0x59, 0x95, 0x9F, 0x4F, 0x9C, 0x42,
+                                                       0x9F, 0x9D, 0x12 } };
+
+void BleLayerObject::Release()
+{
+    // Decrement the ref count.  When it reaches zero, NULL out the pointer to the Weave::System::Layer
+    // object. This effectively declared the object free and ready for re-allocation.
+    mRefCount--;
+    if (mRefCount == 0)
+    {
+        mBle = NULL;
+    }
+}
+
+// BleTransportCapabilitiesRequestMessage implementation:
+
+void BleTransportCapabilitiesRequestMessage::SetSupportedProtocolVersion(uint8_t index, uint8_t version)
+{
+    uint8_t mask;
+
+    // If even-index, store version in lower 4 bits; else, higher 4 bits.
+    if (index % 2 == 0)
+    {
+        mask = 0x0F;
+    }
+    else
+    {
+        mask    = 0xF0;
+        version = version << 4;
+    }
+
+    version &= mask;
+
+    mSupportedProtocolVersions[(index / 2)] &= ~mask; // Clear version at index; leave other version in same byte alone
+    mSupportedProtocolVersions[(index / 2)] |= version;
+}
+
+BLE_ERROR BleTransportCapabilitiesRequestMessage::Encode(PacketBuffer * msgBuf) const
+{
+    uint8_t * p   = msgBuf->Start();
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    // Verify we can write the fixed-length request without running into the end of the buffer.
+    VerifyOrExit(msgBuf->MaxDataLength() >= CAPABILITIES_REQUEST_LEN, err = BLE_ERROR_NO_MEMORY);
+
+    nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
+    nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
+
+    for (int i = 0; i < CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN; i++)
+    {
+        nl::Weave::Encoding::Write8(p, mSupportedProtocolVersions[i]);
+    }
+
+    nl::Weave::Encoding::LittleEndian::Write16(p, mMtu);
+    nl::Weave::Encoding::Write8(p, mWindowSize);
+
+    msgBuf->SetDataLength(CAPABILITIES_REQUEST_LEN);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BleTransportCapabilitiesRequestMessage::Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesRequestMessage & msg)
+{
+    const uint8_t * p = msgBuf.Start();
+    BLE_ERROR err     = BLE_NO_ERROR;
+
+    // Verify we can read the fixed-length request without running into the end of the buffer.
+    VerifyOrExit(msgBuf.DataLength() >= CAPABILITIES_REQUEST_LEN, err = BLE_ERROR_MESSAGE_INCOMPLETE);
+
+    VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_1 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+    VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_2 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+
+    for (int i = 0; i < CAPABILITIES_REQUEST_SUPPORTED_VERSIONS_LEN; i++)
+    {
+        msg.mSupportedProtocolVersions[i] = nl::Weave::Encoding::Read8(p);
+    }
+
+    msg.mMtu        = nl::Weave::Encoding::LittleEndian::Read16(p);
+    msg.mWindowSize = nl::Weave::Encoding::Read8(p);
+
+exit:
+    return err;
+}
+
+// BleTransportCapabilitiesResponseMessage implementation:
+
+BLE_ERROR BleTransportCapabilitiesResponseMessage::Encode(PacketBuffer * msgBuf) const
+{
+    uint8_t * p   = msgBuf->Start();
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    // Verify we can write the fixed-length request without running into the end of the buffer.
+    VerifyOrExit(msgBuf->MaxDataLength() >= CAPABILITIES_RESPONSE_LEN, err = BLE_ERROR_NO_MEMORY);
+
+    nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_1);
+    nl::Weave::Encoding::Write8(p, CAPABILITIES_MSG_CHECK_BYTE_2);
+
+    nl::Weave::Encoding::Write8(p, mSelectedProtocolVersion);
+    nl::Weave::Encoding::LittleEndian::Write16(p, mFragmentSize);
+    nl::Weave::Encoding::Write8(p, mWindowSize);
+
+    msgBuf->SetDataLength(CAPABILITIES_RESPONSE_LEN);
+
+exit:
+    return err;
+}
+
+BLE_ERROR BleTransportCapabilitiesResponseMessage::Decode(const PacketBuffer & msgBuf,
+                                                          BleTransportCapabilitiesResponseMessage & msg)
+{
+    const uint8_t * p = msgBuf.Start();
+    BLE_ERROR err     = BLE_NO_ERROR;
+
+    // Verify we can read the fixed-length response without running into the end of the buffer.
+    VerifyOrExit(msgBuf.DataLength() >= CAPABILITIES_RESPONSE_LEN, err = BLE_ERROR_MESSAGE_INCOMPLETE);
+
+    VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_1 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+    VerifyOrExit(CAPABILITIES_MSG_CHECK_BYTE_2 == nl::Weave::Encoding::Read8(p), err = BLE_ERROR_INVALID_MESSAGE);
+
+    msg.mSelectedProtocolVersion = nl::Weave::Encoding::Read8(p);
+    msg.mFragmentSize            = nl::Weave::Encoding::LittleEndian::Read16(p);
+    msg.mWindowSize              = nl::Weave::Encoding::Read8(p);
+
+exit:
+    return err;
+}
+
+// BleLayer implementation:
+
+BleLayer::BleLayer()
+{
+    mState = kState_NotInitialized;
+}
+
+BLE_ERROR BleLayer::Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate,
+                         Weave::System::Layer * systemLayer)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    RegisterBleLayerErrorFormatter();
+
+    VerifyOrExit(platformDelegate != NULL, err = BLE_ERROR_BAD_ARGS);
+    VerifyOrExit(appDelegate != NULL, err = BLE_ERROR_BAD_ARGS);
+    VerifyOrExit(systemLayer != NULL, err = BLE_ERROR_BAD_ARGS);
+
+    if (mState != kState_NotInitialized)
+    {
+        return BLE_ERROR_INCORRECT_STATE;
+    }
+
+    mPlatformDelegate    = platformDelegate;
+    mApplicationDelegate = appDelegate;
+    mSystemLayer         = systemLayer;
+
+    memset(&sBLEEndPointPool, 0, sizeof(sBLEEndPointPool));
+
+    mState = kState_Initialized;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    mTestBleEndPoint = NULL;
+#endif
+
+exit:
+    return err;
+}
+
+BLE_ERROR BleLayer::Shutdown()
+{
+    mState = kState_NotInitialized;
+
+    // Close and free all BLE end points.
+    for (int i = 0; i < BLE_LAYER_NUM_BLE_ENDPOINTS; i++)
+    {
+        BLEEndPoint * elem = sBLEEndPointPool.Get(i);
+
+        // If end point was initialized, and has not since been freed...
+        if (elem->mBle != NULL)
+        {
+            // If end point hasn't already been closed...
+            if (elem->mState != BLEEndPoint::kState_Closed)
+            {
+                // Close end point such that callbacks are suppressed and pending transmissions aborted.
+                elem->Abort();
+            }
+
+            // If end point was closed, but is still waiting for GATT unsubscribe to complete, free it anyway.
+            // This cancels the unsubscribe timer (plus all the end point's other timers).
+            if (elem->IsUnsubscribePending())
+            {
+                elem->Free();
+            }
+        }
+    }
+
+    return BLE_NO_ERROR;
+}
+
+BLE_ERROR BleLayer::NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose)
+{
+    *retEndPoint = NULL;
+
+    if (mState != kState_Initialized)
+    {
+        return BLE_ERROR_INCORRECT_STATE;
+    }
+
+    if (connObj == BLE_CONNECTION_UNINITIALIZED)
+    {
+        return BLE_ERROR_BAD_ARGS;
+    }
+
+    *retEndPoint = sBLEEndPointPool.GetFree();
+    if (*retEndPoint == NULL)
+    {
+        WeaveLogError(Ble, "%s endpoint pool FULL", "Ble");
+        return BLE_ERROR_NO_ENDPOINTS;
+    }
+
+    (*retEndPoint)->Init(this, connObj, role, autoClose);
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    mTestBleEndPoint = *retEndPoint;
+#endif
+
+    return BLE_NO_ERROR;
+}
+
+// Handle remote central's initiation of Weave over BLE protocol handshake.
+BLE_ERROR BleLayer::HandleBleTransportConnectionInitiated(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf)
+{
+    BLE_ERROR err             = BLE_NO_ERROR;
+    BLEEndPoint * newEndPoint = NULL;
+
+    // Only BLE peripherals can receive GATT writes, so specify this role in our creation of the BLEEndPoint.
+    // Set autoClose = false. Peripherals only notify the application when an end point releases a BLE connection.
+    err = NewBleEndPoint(&newEndPoint, connObj, kBleRole_Peripheral, false);
+    SuccessOrExit(err);
+
+    newEndPoint->mAppState = mAppState;
+
+    err  = newEndPoint->Receive(pBuf);
+    pBuf = NULL;
+    SuccessOrExit(err); // If we fail here, end point will have already released connection and freed itself.
+
+exit:
+    if (pBuf != NULL)
+    {
+        PacketBuffer::Free(pBuf);
+    }
+
+    // If we failed to allocate a new end point, release underlying BLE connection. Central's handshake will time out
+    // if the application decides to keep the BLE connection open.
+    if (newEndPoint == NULL)
+    {
+        mApplicationDelegate->NotifyWeaveConnectionClosed(connObj);
+    }
+
+    if (err != BLE_NO_ERROR)
+    {
+        WeaveLogError(Ble, "HandleWeaveConnectionReceived failed, err = %d", err);
+    }
+
+    return err;
+}
+
+bool BleLayer::HandleWriteReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                   PacketBuffer * pBuf)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        WeaveLogError(Ble, "ble write rcvd on unknown svc id");
+        ExitNow();
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_1_ID, charId))
+    {
+        if (pBuf == NULL)
+        {
+            WeaveLogError(Ble, "rcvd null ble write");
+            ExitNow();
+        }
+
+        // Find matching connection end point.
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            BLE_ERROR status = endPoint->Receive(pBuf);
+            pBuf             = NULL;
+            if (status != BLE_NO_ERROR)
+            {
+                WeaveLogError(Ble, "BLEEndPoint rcv failed, err = %d", status);
+            }
+        }
+        else
+        {
+            BLE_ERROR status = HandleBleTransportConnectionInitiated(connObj, pBuf);
+            pBuf             = NULL;
+            if (status != BLE_NO_ERROR)
+            {
+                WeaveLogError(Ble, "failed handle new Weave BLE connection, status = %d", status);
+            }
+        }
+    }
+    else
+    {
+        WeaveLogError(Ble, "ble write rcvd on unknown char");
+    }
+
+exit:
+    if (pBuf != NULL)
+    {
+        PacketBuffer::Free(pBuf);
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleIndicationReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                        PacketBuffer * pBuf)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        if (pBuf == NULL)
+        {
+            WeaveLogError(Ble, "rcvd null ble indication");
+            ExitNow();
+        }
+
+        // find matching connection end point.
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            BLE_ERROR status = endPoint->Receive(pBuf);
+            pBuf             = NULL;
+            if (status != BLE_NO_ERROR)
+            {
+                WeaveLogError(Ble, "BLEEndPoint rcv failed, err = %d", status);
+            }
+        }
+        else
+        {
+            WeaveLogDetail(Ble, "no endpoint for rcvd indication");
+        }
+    }
+    else
+    {
+        WeaveLogError(Ble, "ble ind rcvd on unknown char");
+    }
+
+exit:
+    if (pBuf != NULL)
+    {
+        PacketBuffer::Free(pBuf);
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleWriteConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_1_ID, charId))
+    {
+        HandleAckReceived(connObj);
+    }
+    else
+    {
+        WeaveLogError(Ble, "ble write con rcvd on unknown char");
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleIndicationConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        HandleAckReceived(connObj);
+    }
+    else
+    {
+        WeaveLogError(Ble, "ble ind con rcvd on unknown char");
+    }
+
+    return true;
+}
+
+void BleLayer::HandleAckReceived(BLE_CONNECTION_OBJECT connObj)
+{
+    // find matching connection end point.
+    BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+    if (endPoint != NULL)
+    {
+        BLE_ERROR status = endPoint->HandleGattSendConfirmationReceived();
+
+        if (status != BLE_NO_ERROR)
+        {
+            WeaveLogError(Ble, "endpoint conf recvd failed, err = %d", status);
+        }
+    }
+    else
+    {
+        WeaveLogError(Ble, "no endpoint for BLE sent data ack");
+    }
+}
+
+bool BleLayer::HandleSubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        // Find end point already associated with BLE connection, if any.
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            endPoint->HandleSubscribeReceived();
+        }
+        else
+        {
+            WeaveLogError(Ble, "no endpoint for sub recvd");
+        }
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleSubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            endPoint->HandleSubscribeComplete();
+        }
+        else
+        {
+            WeaveLogError(Ble, "no endpoint for sub complete");
+        }
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleUnsubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        // Find end point already associated with BLE connection, if any.
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            endPoint->DoClose(kBleCloseFlag_AbortTransmission, BLE_ERROR_CENTRAL_UNSUBSCRIBED);
+        }
+        else
+        {
+            WeaveLogError(Ble, "no endpoint for unsub recvd");
+        }
+    }
+
+    return true;
+}
+
+bool BleLayer::HandleUnsubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId)
+{
+    if (!UUIDsMatch(&WEAVE_BLE_SVC_ID, svcId))
+    {
+        return false;
+    }
+
+    if (UUIDsMatch(&WEAVE_BLE_CHAR_2_ID, charId))
+    {
+        // Find end point already associated with BLE connection, if any.
+        BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+        if (endPoint != NULL)
+        {
+            endPoint->HandleUnsubscribeComplete();
+        }
+        else
+        {
+            WeaveLogError(Ble, "no endpoint for unsub complete");
+        }
+    }
+
+    return true;
+}
+
+void BleLayer::HandleConnectionError(BLE_CONNECTION_OBJECT connObj, BLE_ERROR err)
+{
+    // BLE connection has failed somehow, we must find and abort matching connection end point.
+    BLEEndPoint * endPoint = sBLEEndPointPool.Find(connObj);
+
+    if (endPoint != NULL)
+    {
+        if (err == BLE_ERROR_GATT_UNSUBSCRIBE_FAILED && endPoint->IsUnsubscribePending())
+        {
+            // If end point was already closed and just waiting for unsubscribe to complete, free it. Call to Free()
+            // stops unsubscribe timer.
+            endPoint->Free();
+        }
+        else
+        {
+            endPoint->DoClose(kBleCloseFlag_AbortTransmission, err);
+        }
+    }
+}
+
+BleTransportProtocolVersion BleLayer::GetHighestSupportedProtocolVersion(const BleTransportCapabilitiesRequestMessage & reqMsg)
+{
+    BleTransportProtocolVersion retVersion = kBleTransportProtocolVersion_None;
+
+    uint8_t shift_width = 4;
+
+    for (int i = 0; i < NUM_SUPPORTED_PROTOCOL_VERSIONS; i++)
+    {
+        shift_width ^= 4;
+
+        uint8_t version = reqMsg.mSupportedProtocolVersions[(i / 2)];
+        version         = (version >> shift_width) & 0x0F; // Grab just the nibble we want.
+
+        if ((version >= NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION) &&
+            (version <= NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION) && (version > retVersion))
+        {
+            retVersion = static_cast<BleTransportProtocolVersion>(version);
+        }
+        else if (version == kBleTransportProtocolVersion_None) // Signifies end of supported versions list
+        {
+            break;
+        }
+    }
+
+    return retVersion;
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BleLayer.h b/src/ble/BleLayer.h
new file mode 100644
index 0000000..56c1afe
--- /dev/null
+++ b/src/ble/BleLayer.h
@@ -0,0 +1,373 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file defines objects that provide an abstraction layer between a
+ *      platform's Bluetooth Low Energy (BLE) implementation and the Weave
+ *      stack.
+ *
+ *      The BleLayer obect accepts BLE data and control input from the
+ *      application via a functional interface. It performs the fragmentation
+ *      and reassembly required to transmit Weave message via a BLE GATT
+ *      characteristic interface, and drives incoming messages up the Weave
+ *      stack.
+ *
+ *      During initialization, the BleLayer object requires a pointer to the
+ *      platform's implementation of the BleAdapter object. This object is
+ *      defined but not implemented by the Weave stack, and provides the
+ *      BleLayer with a functional interface to drive outgoing GATT
+ *      characteristic writes and indications. It also provides a mechanism
+ *      for Weave to inform the application when it has finished using a given
+ *      BLE connection, i.e., when the WeaveConnection object wrapping this
+ *      connection has closed.
+ *
+ *      To enable Weave over BLE for a new platform, the application developer
+ *      must implement the BleAdapter class for their platform, pass it to the
+ *      BleLayer on startup, pass a pointer to this BleLayer to their instance
+ *      of WeaveMessageLayer, and ensure that the application calls the
+ *      necessary BleLayer functions to drive BLE data and control input up the
+ *      stack.
+ */
+
+#ifndef BLELAYER_H_
+#define BLELAYER_H_
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+
+#include <BleLayer/BleConfig.h>
+
+#include <SystemLayer/SystemLayer.h>
+#include <SystemLayer/SystemPacketBuffer.h>
+
+#include <BleLayer/BlePlatformDelegate.h>
+#include <BleLayer/BleApplicationDelegate.h>
+#include <BleLayer/BleUUID.h>
+#include <BleLayer/BleError.h>
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+#include <InetLayer/InetLayer.h>
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+/**
+ *  @def NUM_SUPPORTED_PROTOCOL_VERSIONS
+ *
+ *  Number of unsigned 4-bit representations of supported transport protocol
+ *  versions encapsulated in a BleTransportCapabilitiesRequest. Defined by Weave
+ *  over BLE protocol specification.
+ */
+#define NUM_SUPPORTED_PROTOCOL_VERSIONS     8
+/// Version(s) of the Nest BLE Transport Protocol that this stack supports.
+#define NL_BLE_TRANSPORT_PROTOCOL_MIN_SUPPORTED_VERSION kBleTransportProtocolVersion_V2
+#define NL_BLE_TRANSPORT_PROTOCOL_MAX_SUPPORTED_VERSION kBleTransportProtocolVersion_V3
+
+/// Forward declarations.
+class BleLayer;
+class BLEEndPoint;
+
+/// Role of end points' associated BLE connections. Determines means used by end points to send and receive data.
+typedef enum
+{
+    kBleRole_Central    = 0,
+    kBleRole_Peripheral = 1
+} BleRole;
+
+/// Enum defining versions of Weave over BLE transport protocol.
+typedef enum
+{
+    kBleTransportProtocolVersion_None = 0,
+    kBleTransportProtocolVersion_V1   = 1, // Prototype WoBLe version without ACKs or flow-control.
+    kBleTransportProtocolVersion_V2   = 2, // First WoBLE version with ACKs and flow-control.
+    kBleTransportProtocolVersion_V3   = 3  // First WoBLE version with asymetric fragement sizes.
+} BleTransportProtocolVersion;
+
+class BleLayerObject
+{
+    friend class BleLayer;
+
+public:
+    // Public data members:
+    BleLayer * mBle;  ///< [READ-ONLY] Pointer to the BleLayer object that owns this object.
+    void * mAppState; ///< Generic pointer to app-specific data associated with the object.
+
+protected:
+    uint32_t mRefCount;
+
+    void AddRef(void) { mRefCount++; }
+    void Release(void);
+};
+
+class BleTransportCapabilitiesRequestMessage
+{
+public:
+    /**
+     * An array of size NUM_SUPPORTED_PROTOCOL_VERSIONS listing versions of the
+     * BLE transport protocol that this node supports. Each protocol version is
+     * specified as a 4-bit unsigned integer. A zero-value represents unused
+     * array elements. Counting up from the zero-index, the first zero-value
+     * specifies the end of the list of supported protocol versions.
+     */
+    uint8_t mSupportedProtocolVersions[(NUM_SUPPORTED_PROTOCOL_VERSIONS / 2) + (NUM_SUPPORTED_PROTOCOL_VERSIONS % 2)];
+
+    /**
+     *  The MTU that has been negotiated for this BLE connection. Specified in
+     *  the BleTransportCapabilitiesRequestMessage because the remote node may
+     *  be unable to glean this info from its own BLE hardware/software stack,
+     *  such as on older Android platforms.
+     *
+     *  A value of 0 means that the central could not determine the negotiated
+     *  BLE connection MTU.
+     */
+    uint16_t mMtu;
+
+    /**
+     *  The initial and maximum receive window size offered by the central,
+     *  defined in terms of GATT indication payloads.
+     */
+    uint8_t mWindowSize;
+
+    /**
+     *  Set supported version value at given index in
+     *  SupportedProtocolVersions. uint8_t version argument is truncated to 4
+     *  least-significant bits. Index shall be 0 through number of
+     *  SupportedProtocolVersions elements - 1.
+     */
+    void SetSupportedProtocolVersion(uint8_t index, uint8_t version);
+
+    /// Must be able to reserve 20 byte data length in msgBuf.
+    BLE_ERROR Encode(PacketBuffer * msgBuf) const;
+
+    static BLE_ERROR Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesRequestMessage & msg);
+};
+
+class BleTransportCapabilitiesResponseMessage
+{
+public:
+    /**
+     *  The lower 4 bits specify the BLE transport protocol version that the BLE
+     *  peripheral has selected for this connection.
+     *
+     *  A value of kBleTransportProtocolVersion_None means that no supported
+     *  protocol version was found in the central's capabilities request. The
+     *  central should unsubscribe after such a response has been sent to free
+     *  up the peripheral for connections from devices with supported protocol
+     *  versions.
+     */
+    uint8_t mSelectedProtocolVersion;
+
+    /**
+     *  BLE transport fragment size selected by peripheral in response to MTU
+     *  value in BleTransportCapabilitiesRequestMessage and its local
+     *  observation of the BLE connection MTU.
+     */
+    uint16_t mFragmentSize;
+
+    /**
+     *  The initial and maximum receive window size offered by the peripheral,
+     *  defined in terms of GATT write payloads.
+     */
+    uint8_t mWindowSize;
+
+    /// Must be able to reserve 20 byte data length in msgBuf.
+    BLE_ERROR Encode(PacketBuffer * msgBuf) const;
+
+    static BLE_ERROR Decode(const PacketBuffer & msgBuf, BleTransportCapabilitiesResponseMessage & msg);
+};
+
+/**
+ *  @class BleLayer
+ *
+ *  @brief
+ *    This class provides an interface for a single thread to drive data
+ *    either up the stack via the BleLayer platform interface functions,
+ *    or down the stack via a WeaveConnection object associated with a
+ *    BLEEndPoint.
+ *
+ *    There are two ways to associate a WeaveConnection (defined by the
+ *    WeaveMessageLayer) with a BLE connection:
+ *
+ *    First, the application can passively receive an incoming BLE connection
+ *    and hand the platform-specific BLE_CONNECTION_OBJECT that this receipt
+ *    generates to BleLayer via the corresponding platform interface function.
+ *    This causes BleLayer to wrap the BLE_CONNECTION_OBJECT in a BLEEndPoint,
+ *    and notify WeaveMessageLayer that a new BLE conneciotn has been received.
+ *    The message layer then wraps the new BLEEndPoint object in a
+ *    WeaveConnection, and hands this object to the application via the message
+ *    layer's OnConnectionReceived callback.
+ *
+ *    Second, the application can actively form an outgoing BLE connection, e.g.,
+ *    by connecting to a BLE peripheral. It then creates a new WeaveConnection
+ *    via the WeaveMessageLayer, assigns an authentication type to this
+ *    connection, and binds it to the BLE_CONNECTION_OBJECT for the new BLE
+ *    connection via WeaveConnection::ConnectBle. This function then
+ *    establishes the secure session type specified by the WeaveConnection's
+ *    authentication type member variable.
+ *
+ */
+class NL_DLL_EXPORT BleLayer
+{
+    friend class BLEEndPoint;
+#if WEAVE_ENABLE_WOBLE_TEST
+    friend class WoBleTest;
+#endif
+
+public:
+    // Public data members:
+    enum
+    {
+        kState_NotInitialized = 0,
+        kState_Initialized    = 1
+    } mState; ///< [READ-ONLY] Current state
+
+    void * mAppState;
+
+    typedef void (*BleConnectionReceivedFunct)(BLEEndPoint * newEndPoint);
+    BleConnectionReceivedFunct OnWeaveBleConnectReceived;
+
+public:
+    // Public functions:
+    BleLayer(void);
+
+    BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate,
+                   Weave::System::Layer * systemLayer);
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+    BLE_ERROR Init(BlePlatformDelegate * platformDelegate, BleApplicationDelegate * appDelegate, Inet::InetLayer * inetLayer);
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+    BLE_ERROR Shutdown(void);
+
+    BLE_ERROR NewBleEndPoint(BLEEndPoint ** retEndPoint, BLE_CONNECTION_OBJECT connObj, BleRole role, bool autoClose);
+
+    nl::Weave::System::Error ScheduleWork(nl::Weave::System::Layer::TimerCompleteFunct aComplete, void* aAppState)
+    {
+        return mSystemLayer->ScheduleWork(aComplete, aAppState);
+    }
+
+    /**< Platform interface functions:
+
+     *   Calling conventions:
+     *     Weave takes ownership of PacketBuffers received through these functions,
+     *     and will free them or pass ownership up the stack.
+     *
+     *     Beyond each call, no guarantees are provided as to the lifetime of UUID arguments.
+     *
+     *     A 'true' return value means the Weave stack successfully handled the
+     *     corresponding message or state indication. 'false' means the Weave stack either
+     *     failed or chose not to handle this. In case of 'false,' the Weave stack will not
+     *     have freed or taken ownership of any PacketBuffer arguments. This contract allows the
+     *     platform to pass BLE events to Weave without needing to know which characteristics
+     *     Weave cares about.
+
+     *     Platform must call this function when a GATT subscription has been established to any Weave service
+     *     charateristic.
+     *
+     *     If this function returns true, Weave has accepted the BLE connection and wrapped it
+     *     in a WeaveConnection object. If Weave accepts a BLE connection, the platform MUST
+     *     notify Weave if the subscription is canceled or the underlying BLE connection is
+     *     closed, or the associated WeaveConnection will never be closed or freed. */
+    bool HandleSubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /// Call when a GATT subscribe request succeeds.
+    bool HandleSubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /**< Platform must call this function when a GATT unsubscribe is requested on any Weave
+     *   service charateristic, that is, when an existing GATT subscription on a Weave service
+     *   characteristic is canceled. */
+    bool HandleUnsubscribeReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /// Call when a GATT unsubscribe request succeeds.
+    bool HandleUnsubscribeComplete(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /// Call when a GATT write request is received.
+    bool HandleWriteReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                             PacketBuffer * pBuf);
+
+    /// Call when a GATT indication is received.
+    bool HandleIndicationReceived(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                  PacketBuffer * pBuf);
+
+    /// Call when an outstanding GATT write request receives a positive receipt confirmation.
+    bool HandleWriteConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /// Call when an oustanding GATT indication receives a positive receipt confirmation.
+    bool HandleIndicationConfirmation(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId);
+
+    /// Call when a GATT read request is received.
+    bool HandleReadReceived(BLE_CONNECTION_OBJECT connObj, BLE_READ_REQUEST_CONTEXT requestContext, const WeaveBleUUID * svcId,
+                            const WeaveBleUUID * charId);
+
+    /**< Platform must call this function when any previous operation undertaken by the BleLayer via BleAdapter
+     *   fails, such as a characteristic write request or subscribe attempt, or when a BLE connection is closed.
+     *
+     *   In most cases, this will prompt Weave to close the associated WeaveConnection and notify that platform that
+     *   it has abandoned the underlying BLE connection.
+     *
+     *   NOTE: if the application explicitly closes a BLE connection with an associated WeaveConnection such that
+     *   the BLE connection close will not generate an upcall to Weave, HandleConnectionError must be called with
+     *   err = BLE_ERROR_APP_CLOSED_CONNECTION to prevent the leak of this WeaveConnection and its end point object. */
+    void HandleConnectionError(BLE_CONNECTION_OBJECT connObj, BLE_ERROR err);
+
+#if WEAVE_ENABLE_WOBLE_TEST
+    BLEEndPoint * mTestBleEndPoint;
+#endif
+
+private:
+    // Private data members:
+
+    // UUID of Weave service characteristic used for central writes.
+    static const WeaveBleUUID WEAVE_BLE_CHAR_1_ID;
+    // UUID of Weave service characteristic used for peripheral indications.
+    static const WeaveBleUUID WEAVE_BLE_CHAR_2_ID;
+
+    BlePlatformDelegate * mPlatformDelegate;
+    BleApplicationDelegate * mApplicationDelegate;
+    Weave::System::Layer * mSystemLayer;
+
+private:
+    // Private functions:
+    void HandleDataReceived(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf);
+    void HandleAckReceived(BLE_CONNECTION_OBJECT connObj);
+    void DriveSending(void);
+    BLE_ERROR HandleBleTransportConnectionInitiated(BLE_CONNECTION_OBJECT connObj, PacketBuffer * pBuf);
+
+    static BleTransportProtocolVersion GetHighestSupportedProtocolVersion(const BleTransportCapabilitiesRequestMessage & reqMsg);
+};
+
+#if BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+inline BLE_ERROR BleLayer::Init(BlePlatformDelegate * aPlatformDelegate, BleApplicationDelegate * aAppDelegate,
+                                Inet::InetLayer * aInetLayer)
+{
+    return Init(aPlatformDelegate, aAppDelegate, aInetLayer->SystemLayer());
+}
+#endif // BLE_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#include "BLEEndPoint.h"
+
+#endif /* BLELAYER_H_ */
diff --git a/src/ble/BlePlatformDelegate.h b/src/ble/BlePlatformDelegate.h
new file mode 100644
index 0000000..4425cec
--- /dev/null
+++ b/src/ble/BlePlatformDelegate.h
@@ -0,0 +1,97 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file defines the interface for downcalls from BleLayer
+ *      to a platform's BLE framework.
+ */
+
+#ifndef BLEPLATFORMDELEGATE_H_
+#define BLEPLATFORMDELEGATE_H_
+
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BleUUID.h>
+
+#include <SystemLayer/SystemPacketBuffer.h>
+#include <Weave/Support/NLDLLUtil.h>
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+// Platform-agnostic BLE interface
+class NL_DLL_EXPORT BlePlatformDelegate
+{
+public:
+    // Following APIs must be implemented by platform:
+
+    // Subscribe to updates and indications on the specfied characteristic
+    virtual bool SubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId,
+                                         const WeaveBleUUID * charId) = 0;
+
+    // Unsubscribe from updates and indications on the specified characteristic
+    virtual bool UnsubscribeCharacteristic(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId,
+                                           const WeaveBleUUID * charId) = 0;
+
+    // Close the underlying BLE connection.
+    virtual bool CloseConnection(BLE_CONNECTION_OBJECT connObj) = 0;
+
+    // Get MTU size negotiated for specified BLE connection. Return value of 0 means MTU size could not be determined.
+    virtual uint16_t GetMTU(BLE_CONNECTION_OBJECT connObj) const = 0;
+
+    // Data path calling convention:
+    //   The Weave stack retains partial ownership of pBufs sent via the below functions. These buffers are freed by
+    //   Weave after either they're acknowledged by the peer's BLE controller, or Weave shuts down the pBuf's
+    //   associated BLEEndPoint.
+    //
+    //   For its part, the platform MUST call PacketBuffer::Free on each pBuf it receives via a Send* function once it no
+    //   longer requires a reference to this buffer, e.g. when a NL_CLIENT_EVENT_BLE_PBUF_CLEAR event is received on
+    //   platforms with the Nest BLE SDK.
+    //
+    //   On platforms such as iOS or Android where the contents of the pBuf PacketBuffer are copied into a separate
+    //   buffer for transmission, pBuf may be freed on the downcall to the platform delegate once the copy completes.
+    //
+    //   A 'true' return value from a Send* function indicates that the characteristic was written or updated
+    //   successfully. A 'false' value indicates failure, and is used to report this failure to the user via the return
+    //   value of WeaveConnection::SendMessage.
+    //
+    //   If a Send* function returns false, it must release its reference to pBuf prior to return.
+
+    // Send GATT characteristic indication request
+    virtual bool SendIndication(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                PacketBuffer * pBuf) = 0;
+
+    // Send GATT characteristic write request
+    virtual bool SendWriteRequest(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                  PacketBuffer * pBuf) = 0;
+
+    // Send GATT characteristic read request
+    virtual bool SendReadRequest(BLE_CONNECTION_OBJECT connObj, const WeaveBleUUID * svcId, const WeaveBleUUID * charId,
+                                 PacketBuffer * pBuf) = 0;
+
+    // Send response to remote host's GATT chacteristic read response
+    virtual bool SendReadResponse(BLE_CONNECTION_OBJECT connObj, BLE_READ_REQUEST_CONTEXT requestContext,
+                                  const WeaveBleUUID * svcId, const WeaveBleUUID * charId) = 0;
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEPLATFORMDELEGATE_H_ */
diff --git a/src/ble/BleUUID.cpp b/src/ble/BleUUID.cpp
new file mode 100644
index 0000000..9831ed0
--- /dev/null
+++ b/src/ble/BleUUID.cpp
@@ -0,0 +1,46 @@
+/*
+ *
+ *    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.
+ */
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <stdint.h>
+#include <string.h>
+
+#include "BleUUID.h"
+
+namespace nl {
+namespace Ble {
+
+const WeaveBleUUID WEAVE_BLE_SVC_ID = { { // 0000FEAF-0000-1000-8000-00805F9B34FB
+                                          0x00, 0x00, 0xFE, 0xAF, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34,
+                                          0xFB } };
+
+bool UUIDsMatch(const WeaveBleUUID * idOne, const WeaveBleUUID * idTwo)
+{
+    if ((idOne == NULL) || (idTwo == NULL))
+    {
+        return false;
+    }
+    return (memcmp(idOne->bytes, idTwo->bytes, 16) == 0);
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/BleUUID.h b/src/ble/BleUUID.h
new file mode 100644
index 0000000..b0102af
--- /dev/null
+++ b/src/ble/BleUUID.h
@@ -0,0 +1,43 @@
+/*
+ *
+ *    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.
+ */
+#ifndef BLEUUID_H_
+#define BLEUUID_H_
+
+#include <stdint.h>
+
+namespace nl {
+namespace Ble {
+
+// Type to represent 128-bit BLE UUIDs. 16-bit short UUIDs may be combined with
+// the Bluetooth Base UUID to form full 128-bit UUIDs as described in the
+// Service Discovery Protocol (SDP) definition, part of the Bluetooth Core
+// Specification.
+typedef struct
+{
+    uint8_t bytes[16];
+} WeaveBleUUID;
+
+// UUID of Nest Weave BLE service. Exposed for use in scan filter.
+extern const WeaveBleUUID WEAVE_BLE_SVC_ID;
+
+bool UUIDsMatch(const WeaveBleUUID * idOne, const WeaveBleUUID * idTwo);
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* BLEUUID_H_ */
diff --git a/src/ble/Makefile.am b/src/ble/Makefile.am
new file mode 100644
index 0000000..40e2cd7
--- /dev/null
+++ b/src/ble/Makefile.am
@@ -0,0 +1,44 @@
+#
+#
+#    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 template for the Nest BleLayer
+#      library.
+#
+
+include $(abs_top_nlbuild_autotools_dir)/automake/pre.am
+
+include BleLayer.am
+
+lib_LIBRARIES                       = libBleLayer.a
+
+libBleLayer_a_CPPFLAGS              = \
+    -I$(top_srcdir)/src/include       \
+    $(LWIP_CPPFLAGS)                  \
+    $(NULL)
+
+if WEAVE_ENABLE_WOBLE_TEST
+libBleLayer_a_CPPFLAGS             += \
+    -I$(top_srcdir)/src/device-manager\
+    $(NULL)
+endif # WEAVE_ENABLE_WOBLE_TEST
+
+libBleLayer_a_SOURCES               = $(nl_BleLayer_sources)
+
+include $(abs_top_nlbuild_autotools_dir)/automake/post.am
diff --git a/src/ble/WeaveBleServiceData.h b/src/ble/WeaveBleServiceData.h
new file mode 100644
index 0000000..4b65485
--- /dev/null
+++ b/src/ble/WeaveBleServiceData.h
@@ -0,0 +1,111 @@
+/*
+ *
+ *    Copyright (c) 2019 Google LLC.
+ *    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
+ *          Definitions for Weave BLE service advertisement data.
+ */
+
+#ifndef WEAVE_BLE_SERVICE_DATA_H
+#define WEAVE_BLE_SERVICE_DATA_H
+
+namespace nl {
+namespace Ble {
+
+/**
+ * Weave data block types that may appear with Weave BLE service advertisement data.
+ */
+enum WeaveBLEServiceDataType
+{
+    kWeaveBLEServiceDataType_DeviceIdentificationInfo       = 0x01,
+    kWeaveBLEServiceDataType_TokenIdentificationInfo        = 0x02,
+};
+
+/**
+ * Weave BLE Device Identification Information Block
+ *
+ * Defines the over-the-air encoded format of the device identification information block that appears
+ * within Weave BLE service advertisement data.
+ */
+struct WeaveBLEDeviceIdentificationInfo
+{
+    enum
+    {
+        kMajorVersion           = 0,
+        kMinorVersion           = 1,
+    };
+
+    enum
+    {
+        kPairingStatus_Unpaired = 0,
+        kPairingStatus_Paired   = 1,
+    };
+
+    uint8_t BlockLen;
+    uint8_t BlockType;
+    uint8_t MajorVersion;
+    uint8_t MinorVersion;
+    uint8_t DeviceVendorId[2];
+    uint8_t DeviceProductId[2];
+    uint8_t DeviceId[8];
+    uint8_t PairingStatus;
+
+    void Init()
+    {
+        memset(this, 0, sizeof(*this));
+        BlockLen = sizeof(*this) - sizeof(BlockLen); // size of all fields EXCEPT BlockLen
+        BlockType = kWeaveBLEServiceDataType_DeviceIdentificationInfo;
+        MajorVersion = kMajorVersion;
+        MinorVersion = kMinorVersion;
+    }
+
+    uint16_t GetVendorId(void)
+    {
+        return nl::Weave::Encoding::LittleEndian::Get16(DeviceVendorId);
+    }
+
+    void SetVendorId(uint16_t vendorId)
+    {
+        nl::Weave::Encoding::LittleEndian::Put16(DeviceVendorId, vendorId);
+    }
+
+    uint16_t GetProductId(void)
+    {
+        return nl::Weave::Encoding::LittleEndian::Get16(DeviceProductId);
+    }
+
+    void SetProductId(uint16_t productId)
+    {
+        nl::Weave::Encoding::LittleEndian::Put16(DeviceProductId, productId);
+    }
+
+    uint64_t GetDeviceId(void)
+    {
+        return nl::Weave::Encoding::LittleEndian::Get64(DeviceId);
+    }
+
+    void SetDeviceId(uint64_t deviceId)
+    {
+        nl::Weave::Encoding::LittleEndian::Put64(DeviceId, deviceId);
+    }
+} __attribute__((packed));
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif // WEAVE_BLE_SERVICE_DATA_H
diff --git a/src/ble/WoBle.cpp b/src/ble/WoBle.cpp
new file mode 100644
index 0000000..f044220
--- /dev/null
+++ b/src/ble/WoBle.cpp
@@ -0,0 +1,630 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file implements types and an object for the Weave over
+ *      Bluetooth Low Energy (WoBLE) byte-stream, connection-oriented
+ *      adaptation of Weave for point-to-point Bluetooth Low Energy
+ *      (BLE) links.
+ *
+ */
+
+#include <BleLayer/BleConfig.h>
+
+#if CONFIG_NETWORK_LAYER_BLE
+
+#include <BleLayer/WoBle.h>
+#if WEAVE_ENABLE_WOBLE_TEST
+#include <BleLayer/WoBleTest.h>
+#endif
+
+#include <Weave/Support/logging/WeaveLogging.h>
+#include <Weave/Support/CodeUtils.h>
+
+// Define below to enable extremely verbose BLE-specific debug logging.
+#undef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+#define WeaveLogDebugBtpEngine(MOD, MSG, ...) WeaveLogError(MOD, MSG, ##__VA_ARGS__)
+#else
+#define WeaveLogDebugBtpEngine(MOD, MSG, ...)
+#endif
+
+#define NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE 1 // Size in bytes of enocded BTP fragment header flag bits
+#define NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE 1 // Size in bytes of encoded BTP sequence number
+#define NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE 1          // Size in bytes of encoded BTP fragment acknowledgement number
+#define NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE 2      // Size in byte of encoded BTP total fragmented message length
+
+#define NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE                                                                                   \
+    (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE + \
+     NL_BLE_TRANSFER_PROTOCOL_MSG_LEN_SIZE)
+
+#define NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE                                                                      \
+    (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
+
+#define NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE                                                                        \
+    (NL_BLE_TRANSFER_PROTOCOL_HEADER_FLAGS_SIZE + NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE + NL_BLE_TRANSFER_PROTOCOL_SEQUENCE_NUM_SIZE)
+
+namespace nl {
+namespace Ble {
+
+static inline void IncSeqNum(SequenceNumber_t & a_seq_num)
+{
+    a_seq_num = 0xff & ((a_seq_num) + 1);
+}
+
+static inline bool DidReceiveData(uint8_t rx_flags)
+{
+    return (GetFlag(rx_flags, WoBle::kHeaderFlag_StartMessage) ||
+            GetFlag(rx_flags, WoBle::kHeaderFlag_ContinueMessage) ||
+            GetFlag(rx_flags, WoBle::kHeaderFlag_EndMessage));
+}
+
+static void PrintBufDebug(PacketBuffer * buf)
+{
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+    uint8_t * b = buf->Start();
+
+    for (int i = 0; i < buf->DataLength(); i++)
+    {
+        WeaveLogError(Ble, "\t%02x", b[i]);
+    }
+#endif
+}
+
+const uint16_t WoBle::sDefaultFragmentSize = 20;  // 23-byte minimum ATT_MTU - 3 bytes for ATT operation header
+const uint16_t WoBle::sMaxFragmentSize     = 128; // Size of write and indication characteristics
+
+BLE_ERROR WoBle::Init(void * an_app_state, bool expect_first_ack)
+{
+    mAppState              = an_app_state;
+    mRxState               = kState_Idle;
+    mRxBuf                 = NULL;
+    mRxNewestUnackedSeqNum = 0;
+    mRxOldestUnackedSeqNum = 0;
+    mRxFragmentSize        = sDefaultFragmentSize;
+    mTxState               = kState_Idle;
+    mTxBuf                 = NULL;
+    mTxFragmentSize        = sDefaultFragmentSize;
+    mRxCharCount           = 0;
+    mRxPacketCount         = 0;
+    mTxCharCount           = 0;
+    mTxPacketCount         = 0;
+    mTxNewestUnackedSeqNum = 0;
+    mTxOldestUnackedSeqNum = 0;
+#if WEAVE_ENABLE_WOBLE_TEST
+    mTxPacketType = kType_Data; // Default WoBle Data packet
+    mRxPacketType = kType_Data; // Default WoBle Data packet
+#endif
+
+    if (expect_first_ack)
+    {
+        mTxNextSeqNum = 1;
+        mExpectingAck = true;
+        mRxNextSeqNum = 0;
+    }
+    else
+    {
+        mTxNextSeqNum = 0;
+        mExpectingAck = false;
+        mRxNextSeqNum = 1;
+    }
+
+    return BLE_NO_ERROR;
+}
+
+SequenceNumber_t WoBle::GetAndIncrementNextTxSeqNum()
+{
+    SequenceNumber_t ret = mTxNextSeqNum;
+
+    // If not already expecting ack...
+    if (!mExpectingAck)
+    {
+        mExpectingAck          = true;
+        mTxOldestUnackedSeqNum = mTxNextSeqNum;
+    }
+
+    // Update newest unacknowledged sequence number.
+    mTxNewestUnackedSeqNum = mTxNextSeqNum;
+
+    // Increment mTxNextSeqNum.
+    IncSeqNum(mTxNextSeqNum);
+
+    return ret;
+}
+
+SequenceNumber_t WoBle::GetAndRecordRxAckSeqNum()
+{
+    SequenceNumber_t ret = mRxNewestUnackedSeqNum;
+
+    mRxNewestUnackedSeqNum = mRxNextSeqNum;
+    mRxOldestUnackedSeqNum = mRxNextSeqNum;
+
+    return ret;
+}
+
+bool WoBle::HasUnackedData() const
+{
+    return (mRxOldestUnackedSeqNum != mRxNextSeqNum);
+}
+
+bool WoBle::IsValidAck(SequenceNumber_t ack_num) const
+{
+    WeaveLogDebugBtpEngine(Ble, "entered IsValidAck, ack = %u, oldest = %u, newest = %u", ack_num, mTxOldestUnackedSeqNum,
+                           mTxNewestUnackedSeqNum);
+
+    // Return false if not awaiting any ack.
+    if (!mExpectingAck)
+    {
+        WeaveLogDebugBtpEngine(Ble, "unexpected ack is invalid");
+        return false;
+    }
+
+    // Assumption: maximum valid sequence number equals maximum value of SequenceNumber_t.
+
+    if (mTxNewestUnackedSeqNum >= mTxOldestUnackedSeqNum) // If current unacked interval does NOT wrap...
+    {
+        return (ack_num <= mTxNewestUnackedSeqNum && ack_num >= mTxOldestUnackedSeqNum);
+    }
+    else // Else, if current unacked interval DOES wrap...
+    {
+        return (ack_num <= mTxNewestUnackedSeqNum || ack_num >= mTxOldestUnackedSeqNum);
+    }
+}
+
+BLE_ERROR WoBle::HandleAckReceived(SequenceNumber_t ack_num)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+
+    WeaveLogDebugBtpEngine(Ble, "entered HandleAckReceived, ack_num = %u", ack_num);
+
+    // Ensure ack_num falls within range of ack values we're expecting.
+    VerifyOrExit(IsValidAck(ack_num), err = BLE_ERROR_INVALID_ACK);
+
+    if (mTxNewestUnackedSeqNum == ack_num) // If ack is for newest outstanding unacknowledged fragment...
+    {
+        mTxOldestUnackedSeqNum = ack_num;
+
+        // All oustanding fragments have been acknowledged.
+        mExpectingAck = false;
+    }
+    else // If ack is valid, but not for newest oustanding unacknowledged fragment...
+    {
+        // Update newest unacknowledged fragment to one past that which was just acknowledged.
+        mTxOldestUnackedSeqNum = ack_num;
+        IncSeqNum(mTxOldestUnackedSeqNum);
+    }
+
+exit:
+    return err;
+}
+
+// Calling convention:
+//   EncodeStandAloneAck may only be called if data arg is commited for immediate, synchronous subsequent transmission.
+BLE_ERROR WoBle::EncodeStandAloneAck(PacketBuffer * data)
+{
+    BLE_ERROR err = BLE_NO_ERROR;
+    uint8_t * characteristic;
+
+    // Ensure enough headroom exists for the lower BLE layers.
+    VerifyOrExit(data->EnsureReservedSize(WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE), err = BLE_ERROR_NO_MEMORY);
+
+    // Ensure enough space for standalone ack payload.
+    VerifyOrExit(data->MaxDataLength() >= NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE, err = BLE_ERROR_NO_MEMORY);
+    characteristic = data->Start();
+
+    // Since there's no preexisting message payload, we can write BTP header without adjusting data start pointer.
+    characteristic[0] = kHeaderFlag_FragmentAck;
+
+    // Acknowledge most recently received sequence number.
+    characteristic[1] = GetAndRecordRxAckSeqNum();
+    WeaveLogDebugBtpEngine(Ble, "===> encoded stand-alone ack = %u", characteristic[1]);
+
+    // Include sequence number for stand-alone ack itself.
+    characteristic[2] = GetAndIncrementNextTxSeqNum();
+
+    // Set ack payload data length.
+    data->SetDataLength(NL_BLE_TRANSFER_PROTOCOL_STANDALONE_ACK_HEADER_SIZE);
+
+exit:
+    return err;
+}
+
+// Calling convention:
+//   WoBle does not retain ownership of reassembled messages, layer above needs to free when done.
+//
+//   WoBle does not reset itself on error. Upper layer should free outbound message and inbound reassembly buffers
+//   if there is a problem.
+
+// HandleCharacteristicReceived():
+//
+//   Non-NULL characteristic data arg is always either designated as or appended to the message reassembly buffer,
+//   or freed if it holds a stand-alone ack. In all cases, caller must clear its reference to data arg when this
+//   function returns.
+//
+//   Upper layer must immediately clean up and reinitialize protocol engine if returned err != BLE_NO_ERROR.
+BLE_ERROR WoBle::HandleCharacteristicReceived(PacketBuffer * data, SequenceNumber_t & receivedAck, bool & didReceiveAck)
+{
+    BLE_ERROR err            = BLE_NO_ERROR;
+    uint8_t rx_flags         = 0;
+    uint8_t cursor           = 0;
+    uint8_t * characteristic = data->Start();
+
+    VerifyOrExit(data != NULL, err = BLE_ERROR_BAD_ARGS);
+
+    mRxCharCount++;
+
+    // Get header flags, always in first byte.
+    rx_flags = characteristic[cursor++];
+#if WEAVE_ENABLE_WOBLE_TEST
+    if (GetFlag(rx_flags, kHeaderFlag_CommandMessage))
+        SetRxPacketType(kType_Control);
+    else
+        SetRxPacketType(kType_Data);
+#endif
+
+    didReceiveAck = GetFlag(rx_flags, kHeaderFlag_FragmentAck);
+
+    // Get ack number, if any.
+    if (didReceiveAck)
+    {
+        receivedAck = characteristic[cursor++];
+
+        err = HandleAckReceived(receivedAck);
+        SuccessOrExit(err);
+    }
+
+    // Get sequence number.
+    mRxNewestUnackedSeqNum = characteristic[cursor++];
+
+    // Verify that received sequence number is the next one we'd expect.
+    VerifyOrExit(mRxNewestUnackedSeqNum == mRxNextSeqNum, err = BLE_ERROR_INVALID_BTP_SEQUENCE_NUMBER);
+
+    // Increment next expected rx sequence number.
+    IncSeqNum(mRxNextSeqNum);
+
+    // If fragment was stand-alone ack, we're done here; no payload for message reassembler.
+    if (!DidReceiveData(rx_flags))
+    {
+        // Free stand-alone ack buffer.
+        PacketBuffer::Free(data);
+        data = NULL;
+
+        ExitNow();
+    }
+
+    // Truncate the incoming fragment length by the mRxFragmentSize as the negotiated
+    // mRxFragnentSize may be smaller than the characteristic size.
+    data->SetDataLength(nl::Weave::min(data->DataLength(), mRxFragmentSize));
+
+    WeaveLogDebugBtpEngine(Ble, ">>> BTP reassembler received data:");
+    PrintBufDebug(data);
+
+    if (mRxState == kState_Idle)
+    {
+        // Verify StartMessage header flag set.
+        VerifyOrExit(rx_flags & kHeaderFlag_StartMessage, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+        mRxLength = (characteristic[(cursor + 1)] << 8) | characteristic[cursor];
+        cursor += 2;
+
+        mRxState = kState_InProgress;
+
+        data->SetStart(&(characteristic[cursor]));
+
+        // Create a new buffer for use as the Rx re-assembly area.
+        mRxBuf = PacketBuffer::New();
+
+        VerifyOrExit(mRxBuf != NULL, err = BLE_ERROR_NO_MEMORY);
+
+        mRxBuf->AddToEnd(data);
+        mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
+        data = NULL;
+    }
+    else if (mRxState == kState_InProgress)
+    {
+        // Verify StartMessage header flag NOT set, since we're in the middle of receiving a message.
+        VerifyOrExit((rx_flags & kHeaderFlag_StartMessage) == 0, err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+        // Verify ContinueMessage or EndMessage header flag set.
+        VerifyOrExit((rx_flags & kHeaderFlag_ContinueMessage) || (rx_flags & kHeaderFlag_EndMessage),
+                     err = BLE_ERROR_INVALID_BTP_HEADER_FLAGS);
+
+        // Add received fragment to reassembled message buffer.
+        data->SetStart(&(characteristic[cursor]));
+        mRxBuf->AddToEnd(data);
+        mRxBuf->CompactHead(); // will free 'data' and adjust rx buf's end/length
+        data = NULL;
+
+        // For now, limit WoBle message size to max length of 1 pbuf, as we do for Weave messages sent via IP.
+        // TODO add support for WoBle messages longer than 1 pbuf
+        VerifyOrExit(mRxBuf->Next() == NULL, err = BLE_ERROR_RECEIVED_MESSAGE_TOO_BIG);
+    }
+    else
+    {
+        err = BLE_ERROR_REASSEMBLER_INCORRECT_STATE;
+        ExitNow();
+    }
+
+    if (rx_flags & kHeaderFlag_EndMessage)
+    {
+        // Trim remainder, if any, of received PacketBuffer based on sender-specified length of reassembled message.
+        int padding = mRxBuf->DataLength() - mRxLength;
+
+        if (padding > 0)
+        {
+            mRxBuf->SetDataLength(mRxLength);
+        }
+
+        // Ensure all received fragments add up to sender-specified total message size.
+        VerifyOrExit(mRxBuf->DataLength() == mRxLength, err = BLE_ERROR_REASSEMBLER_MISSING_DATA);
+
+        // We've reassembled the entire message.
+        mRxState = kState_Complete;
+        mRxPacketCount++;
+    }
+
+exit:
+    if (err != BLE_NO_ERROR)
+    {
+        mRxState = kState_Error;
+
+        // Dump protocol engine state, plus header flags and received data length.
+        WeaveLogError(Ble, "HandleCharacteristicReceived failed, err = %d, rx_flags = %u", err, rx_flags);
+        if (didReceiveAck)
+        {
+            WeaveLogError(Ble, "With rx'd ack = %u", receivedAck);
+        }
+        if (mRxBuf != NULL)
+        {
+            WeaveLogError(Ble, "With rx buf data length = %u", mRxBuf->DataLength());
+        }
+        LogState();
+
+        if (data != NULL)
+        {
+            // Tack received data onto rx buffer, to be freed when end point resets protocol engine on close.
+            if (mRxBuf != NULL)
+            {
+                mRxBuf->AddToEnd(data);
+            }
+            else
+            {
+                mRxBuf = data;
+            }
+        }
+    }
+
+    return err;
+}
+
+PacketBuffer * WoBle::RxPacket()
+{
+    return mRxBuf;
+}
+
+bool WoBle::ClearRxPacket()
+{
+    if (mRxState == kState_Complete)
+    {
+        mRxState = kState_Idle;
+        mRxBuf   = NULL;
+        // do not reset mRxNextSeqNum
+        return true;
+    }
+
+    return false;
+}
+
+// Calling convention:
+//   May only be called if data arg is commited for immediate, synchronous subsequent transmission.
+//   Returns false on error. Caller must free data arg on error.
+bool WoBle::HandleCharacteristicSend(PacketBuffer * data, bool send_ack)
+{
+    uint8_t * characteristic;
+    mTxCharCount++;
+
+    if (send_ack && !HasUnackedData())
+    {
+        WeaveLogError(Ble, "HandleCharacteristicSend: send_ack true, but nothing to acknowledge.");
+        return false;
+    }
+
+    if (mTxState == kState_Idle)
+    {
+        if (data == NULL)
+        {
+            return false;
+        }
+
+        mTxBuf    = data;
+        mTxState  = kState_InProgress;
+        mTxLength = mTxBuf->DataLength();
+
+        WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send whole message:");
+        PrintBufDebug(data);
+
+        // Determine fragment header size.
+        uint8_t header_size = send_ack ? NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE
+                                       : (NL_BLE_TRANSFER_PROTOCOL_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
+
+        // Ensure enough headroom exists for the BTP header, and any headroom needed by the lower BLE layers.
+        if (!mTxBuf->EnsureReservedSize(header_size + WEAVE_CONFIG_BLE_PKT_RESERVED_SIZE))
+        {
+            // handle error
+            WeaveLogError(Ble, "HandleCharacteristicSend: not enough headroom");
+            mTxState = kState_Error;
+            mTxBuf   = NULL; // Avoid double-free after assignment above, as caller frees data on error.
+
+            return false;
+        }
+
+        // prepend header.
+        characteristic = mTxBuf->Start();
+        characteristic -= header_size;
+        mTxBuf->SetStart(characteristic);
+        uint8_t cursor = 1; // first position past header flags byte
+
+        characteristic[0] = kHeaderFlag_StartMessage;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+        if (TxPacketType() == kType_Control)
+            SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
+#endif
+
+        if (send_ack)
+        {
+            SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
+            characteristic[cursor++] = GetAndRecordRxAckSeqNum();
+            WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
+        }
+
+        characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
+        characteristic[cursor++] = mTxLength & 0xff;
+        characteristic[cursor++] = mTxLength >> 8;
+
+        if ((mTxLength + cursor) <= mTxFragmentSize)
+        {
+            mTxBuf->SetDataLength(mTxLength + cursor);
+            mTxLength = 0;
+            SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
+            mTxState = kState_Complete;
+            mTxPacketCount++;
+        }
+        else
+        {
+            mTxBuf->SetDataLength(mTxFragmentSize);
+            mTxLength -= mTxFragmentSize - cursor;
+        }
+
+        WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send first fragment:");
+        PrintBufDebug(data);
+    }
+    else if (mTxState == kState_InProgress)
+    {
+        if (data != NULL)
+        {
+            return false;
+        }
+
+        // advance past the previous fragment
+        characteristic = mTxBuf->Start();
+        characteristic += mTxFragmentSize;
+
+        // prepend header
+        characteristic -= send_ack
+            ? NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE
+            : (NL_BLE_TRANSFER_PROTOCOL_MID_FRAGMENT_MAX_HEADER_SIZE - NL_BLE_TRANSFER_PROTOCOL_ACK_SIZE);
+        mTxBuf->SetStart(characteristic);
+        uint8_t cursor = 1; // first position past header flags byte
+
+        characteristic[0] = kHeaderFlag_ContinueMessage;
+
+#if WEAVE_ENABLE_WOBLE_TEST
+        if (TxPacketType() == kType_Control)
+            SetFlag(characteristic[0], kHeaderFlag_CommandMessage, true);
+#endif
+
+        if (send_ack)
+        {
+            SetFlag(characteristic[0], kHeaderFlag_FragmentAck, true);
+            characteristic[cursor++] = GetAndRecordRxAckSeqNum();
+            WeaveLogDebugBtpEngine(Ble, "===> encoded piggybacked ack, ack_num = %u", characteristic[cursor - 1]);
+        }
+
+        characteristic[cursor++] = GetAndIncrementNextTxSeqNum();
+
+        if ((mTxLength + cursor) <= mTxFragmentSize)
+        {
+            mTxBuf->SetDataLength(mTxLength + cursor);
+            mTxLength = 0;
+            SetFlag(characteristic[0], kHeaderFlag_EndMessage, true);
+            mTxState = kState_Complete;
+            mTxPacketCount++;
+        }
+        else
+        {
+            mTxBuf->SetDataLength(mTxFragmentSize);
+            mTxLength -= mTxFragmentSize - cursor;
+        }
+
+        WeaveLogDebugBtpEngine(Ble, ">>> WoBle preparing to send additional fragment:");
+        PrintBufDebug(mTxBuf);
+    }
+    else
+    {
+        // Invalid tx state.
+        return false;
+    }
+
+    return true;
+}
+
+PacketBuffer * WoBle::TxPacket()
+{
+    return mTxBuf;
+}
+
+bool WoBle::ClearTxPacket()
+{
+    if (mTxState == kState_Complete)
+    {
+        mTxState = kState_Idle;
+        mTxBuf   = NULL;
+        // do not reset mTxNextSeqNum
+        return true;
+    }
+
+    return false;
+}
+
+void WoBle::LogState() const
+{
+    WeaveLogError(Ble, "mAppState: %p", mAppState);
+
+    WeaveLogError(Ble, "mRxFragmentSize: %d", mRxFragmentSize);
+    WeaveLogError(Ble, "mRxState: %d", mRxState);
+    WeaveLogError(Ble, "mRxBuf: %p", mRxBuf);
+    WeaveLogError(Ble, "mRxNextSeqNum: %d", mRxNextSeqNum);
+    WeaveLogError(Ble, "mRxNewestUnackedSeqNum: %d", mRxNewestUnackedSeqNum);
+    WeaveLogError(Ble, "mRxOldestUnackedSeqNum: %d", mRxOldestUnackedSeqNum);
+    WeaveLogError(Ble, "mRxCharCount: %d", mRxCharCount);
+    WeaveLogError(Ble, "mRxPacketCount: %d", mRxPacketCount);
+
+    WeaveLogError(Ble, "mTxFragmentSize: %d", mTxFragmentSize);
+    WeaveLogError(Ble, "mTxState: %d", mTxState);
+    WeaveLogError(Ble, "mTxBuf: %p", mTxBuf);
+    WeaveLogError(Ble, "mTxNextSeqNum: %d", mTxNextSeqNum);
+    WeaveLogError(Ble, "mTxNewestUnackedSeqNum: %d", mTxNewestUnackedSeqNum);
+    WeaveLogError(Ble, "mTxOldestUnackedSeqNum: %d", mTxOldestUnackedSeqNum);
+    WeaveLogError(Ble, "mTxCharCount: %d", mTxCharCount);
+    WeaveLogError(Ble, "mTxPacketCount: %d", mTxPacketCount);
+}
+
+void WoBle::LogStateDebug() const
+{
+#ifdef NL_BTP_PROTOCOL_ENGINE_DEBUG_LOGGING_ENABLED
+    LogState();
+#endif
+}
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* CONFIG_NETWORK_LAYER_BLE */
diff --git a/src/ble/WoBle.h b/src/ble/WoBle.h
new file mode 100644
index 0000000..c8a7d04
--- /dev/null
+++ b/src/ble/WoBle.h
@@ -0,0 +1,194 @@
+/*
+ *
+ *    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.
+ */
+
+/**
+ *    @file
+ *      This file defines types and an object for the Weave over
+ *      Bluetooth Low Energy (WoBLE) byte-stream, connection-oriented
+ *      adaptation of Weave for point-to-point Bluetooth Low Energy
+ *      (BLE) links.
+ *
+ */
+
+#ifndef WOBLE_H_
+#define WOBLE_H_
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#include <BleLayer/BleConfig.h>
+#include <BleLayer/BleError.h>
+#include <SystemLayer/SystemPacketBuffer.h>
+#include <Weave/Support/FlagUtils.hpp>
+
+namespace nl {
+namespace Ble {
+
+using ::nl::Weave::System::PacketBuffer;
+
+typedef uint8_t SequenceNumber_t; // If type changed from uint8_t, adjust assumptions in WoBle::IsValidAck and
+                                  // BLEEndPoint::AdjustReceiveWindow.
+
+#if WEAVE_ENABLE_WOBLE_TEST
+class BLEEndPoint;
+#endif
+
+// Public data members:
+typedef enum
+{
+    kType_Data    = 0, // Default 0 for data
+    kType_Control = 1,
+} PacketType_t; // WoBle packet types
+
+class WoBle
+{
+#if WEAVE_ENABLE_WOBLE_TEST
+    friend class BLEEndPoint;
+#endif
+
+public:
+    // Public data members:
+    typedef enum
+    {
+        kState_Idle       = 0,
+        kState_InProgress = 1,
+        kState_Complete   = 2,
+        kState_Error      = 3
+    } State_t; // [READ-ONLY] Current state
+
+    enum
+    {
+        kHeaderFlag_StartMessage    = 0x01,
+        kHeaderFlag_ContinueMessage = 0x02,
+        kHeaderFlag_EndMessage      = 0x04,
+        kHeaderFlag_FragmentAck     = 0x08,
+#if WEAVE_ENABLE_WOBLE_TEST
+        kHeaderFlag_CommandMessage = 0x10,
+#endif
+    }; // Masks for BTP fragment header flag bits.
+
+    static const uint16_t sDefaultFragmentSize;
+    static const uint16_t sMaxFragmentSize;
+
+public:
+    // Public functions:
+    WoBle(void) { };
+    ~WoBle(void) { };
+
+    BLE_ERROR Init(void * an_app_state, bool expect_first_ack);
+
+    inline void SetTxFragmentSize(uint8_t size) { mTxFragmentSize = size; };
+    inline void SetRxFragmentSize(uint8_t size) { mRxFragmentSize = size; };
+
+    uint16_t GetRxFragmentSize(void) { return mRxFragmentSize; };
+    uint16_t GetTxFragmentSize(void) { return mTxFragmentSize; };
+
+    SequenceNumber_t GetAndIncrementNextTxSeqNum(void);
+    SequenceNumber_t GetAndRecordRxAckSeqNum(void);
+
+    inline SequenceNumber_t GetLastReceivedSequenceNumber(void) { return mRxNewestUnackedSeqNum; };
+    inline SequenceNumber_t GetNewestUnackedSentSequenceNumber(void) { return mTxNewestUnackedSeqNum; };
+
+    inline bool ExpectingAck(void) const { return mExpectingAck; };
+
+    inline State_t RxState(void) { return mRxState; }
+    inline State_t TxState(void) { return mTxState; }
+#if WEAVE_ENABLE_WOBLE_TEST
+    inline PacketType_t SetTxPacketType(PacketType_t type) { return (mTxPacketType = type); };
+    inline PacketType_t SetRxPacketType(PacketType_t type) { return (mRxPacketType = type); };
+    inline PacketType_t TxPacketType() { return mTxPacketType; };
+    inline PacketType_t RxPacketType() { return mRxPacketType; };
+    inline SequenceNumber_t SetTxPacketSeq(SequenceNumber_t seq) { return (mTxPacketSeq = seq); };
+    inline SequenceNumber_t SetRxPacketSeq(SequenceNumber_t seq) { return (mRxPacketSeq = seq); };
+    inline SequenceNumber_t TxPacketSeq() { return mTxPacketSeq; };
+    inline SequenceNumber_t RxPacketSeq() { return mRxPacketSeq; };
+    inline bool IsCommandPacket(PacketBuffer * p) { return GetFlag(*(p->Start()), kHeaderFlag_CommandMessage); }
+    inline void PushPacketTag(PacketBuffer * p, PacketType_t type)
+    {
+        p->SetStart(p->Start() - sizeof(type));
+        memcpy(p->Start(), &type, sizeof(type));
+    };
+    inline PacketType_t PopPacketTag(PacketBuffer * p)
+    {
+        PacketType_t type;
+        memcpy(&type, p->Start(), sizeof(type));
+        p->SetStart(p->Start() + sizeof(type));
+        return type;
+    };
+#endif // WEAVE_ENABLE_WOBLE_TEST
+
+    bool HasUnackedData(void) const;
+
+    BLE_ERROR HandleCharacteristicReceived(PacketBuffer * data, SequenceNumber_t & receivedAck, bool & didReceiveAck);
+    bool HandleCharacteristicSend(PacketBuffer * data, bool send_ack);
+    BLE_ERROR EncodeStandAloneAck(PacketBuffer * data);
+
+    PacketBuffer * RxPacket(void);
+    PacketBuffer * TxPacket(void);
+
+    bool ClearRxPacket(void);
+    bool ClearTxPacket(void);
+
+    void LogState(void) const;
+    void LogStateDebug(void) const;
+
+private:
+    // Private data members:
+#if WEAVE_ENABLE_WOBLE_TEST
+    PacketType_t mTxPacketType;
+    PacketType_t mRxPacketType;
+    SequenceNumber_t mTxPacketSeq;
+    SequenceNumber_t mRxPacketSeq;
+#endif
+    State_t mRxState;
+    uint16_t mRxLength;
+    void * mAppState;
+    PacketBuffer * mRxBuf;
+    SequenceNumber_t mRxNextSeqNum;
+    SequenceNumber_t mRxNewestUnackedSeqNum;
+    SequenceNumber_t mRxOldestUnackedSeqNum;
+    uint16_t mRxFragmentSize;
+
+    State_t mTxState;
+    uint16_t mTxLength;
+    PacketBuffer * mTxBuf;
+    SequenceNumber_t mTxNextSeqNum;
+    SequenceNumber_t mTxNewestUnackedSeqNum;
+    SequenceNumber_t mTxOldestUnackedSeqNum;
+    bool mExpectingAck;
+    uint16_t mTxFragmentSize;
+
+    uint16_t mRxCharCount;
+    uint16_t mRxPacketCount;
+    uint16_t mTxCharCount;
+    uint16_t mTxPacketCount;
+
+private:
+    // Private functions:
+    bool IsValidAck(SequenceNumber_t ack_num) const;
+    BLE_ERROR HandleAckReceived(SequenceNumber_t ack_num);
+};
+
+} /* namespace Ble */
+} /* namespace nl */
+
+#endif /* WOBLE_H_ */