Azure IoT / iothub_amqp_transport

Dependents:   sht15_remote_monitoring RobotArmDemo iothub_client_sample_amqp iothub_client_sample_amqp ... more

Files at this revision

API Documentation at this revision

Comitter:
AzureIoTClient
Date:
Fri Mar 10 11:46:55 2017 -0800
Parent:
29:7e8852b14e3b
Child:
31:adadaef857c1
Commit message:
1.1.9

Changed in this revision

iothubtransport_amqp_cbs_auth.c Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_cbs_auth.h Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_common.c Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_common.h Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_connection.c Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_connection.h Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_device.c Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_device.h Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_messenger.c Show annotated file Show diff for this revision Revisions of this file
iothubtransport_amqp_messenger.h Show annotated file Show diff for this revision Revisions of this file
iothubtransportamqp.c Show annotated file Show diff for this revision Revisions of this file
iothubtransportamqp_auth.c Show diff for this revision Revisions of this file
iothubtransportamqp_auth.h Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_cbs_auth.c	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,982 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdlib.h>
+#include "iothubtransport_amqp_cbs_auth.h"
+#include "azure_c_shared_utility/optimize_size.h"
+#include "azure_c_shared_utility/gballoc.h"
+#include "azure_c_shared_utility/agenttime.h" 
+#include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/sastoken.h"
+
+#define RESULT_OK                                 0
+#define INDEFINITE_TIME                           ((time_t)(-1))
+#define SAS_TOKEN_TYPE                            "servicebus.windows.net:sastoken"
+#define IOTHUB_DEVICES_PATH_FMT                   "%s/devices/%s"
+#define DEFAULT_CBS_REQUEST_TIMEOUT_SECS          UINT32_MAX
+#define DEFAULT_SAS_TOKEN_LIFETIME_SECS           3600
+#define DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS       1800
+
+typedef enum CREDENTIAL_TYPE_TAG
+{
+	CREDENTIAL_TYPE_NONE,
+	DEVICE_PRIMARY_KEY,
+	DEVICE_SECONDARY_KEY,
+	USER_PROVIDED_SAS_TOKEN
+} CREDENTIAL_TYPE;
+
+typedef struct AUTHENTICATION_INSTANCE_TAG 
+{
+	STRING_HANDLE device_id;
+	STRING_HANDLE iothub_host_fqdn;
+	
+	STRING_HANDLE device_sas_token;
+	STRING_HANDLE device_primary_key;
+	STRING_HANDLE device_secondary_key;
+
+	ON_AUTHENTICATION_STATE_CHANGED_CALLBACK on_state_changed_callback;
+	void* on_state_changed_callback_context;
+
+	ON_AUTHENTICATION_ERROR_CALLBACK on_error_callback;
+	void* on_error_callback_context;
+	
+	size_t cbs_request_timeout_secs;
+	size_t sas_token_lifetime_secs;
+	size_t sas_token_refresh_time_secs;
+
+	AUTHENTICATION_STATE state;
+	CBS_HANDLE cbs_handle;
+
+	bool is_cbs_put_token_in_progress;
+	bool is_sas_token_refresh_in_progress;
+
+	time_t current_sas_token_put_time;
+
+	CREDENTIAL_TYPE current_credential_in_use;
+} AUTHENTICATION_INSTANCE;
+
+
+// Helper functions:
+
+static int get_seconds_since_epoch(double *seconds)
+{
+	int result;
+	time_t current_time;
+
+	if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+	{
+		LogError("Failed getting the current local time (get_time() failed)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		*seconds = get_difftime(current_time, (time_t)0);
+
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+static void update_state(AUTHENTICATION_INSTANCE* instance, AUTHENTICATION_STATE new_state)
+{
+	if (new_state != instance->state)
+	{
+		AUTHENTICATION_STATE previous_state = instance->state;
+		instance->state = new_state;
+
+		if (instance->on_state_changed_callback != NULL)
+		{
+			instance->on_state_changed_callback(instance->on_state_changed_callback_context, previous_state, new_state);
+		}
+	}
+}
+
+static void notify_error(AUTHENTICATION_INSTANCE* instance, AUTHENTICATION_ERROR_CODE error_code)
+{
+	if (instance->on_error_callback != NULL)
+	{
+		instance->on_error_callback(instance->on_error_callback_context, error_code);
+	}
+}
+
+static int verify_cbs_put_token_timeout(AUTHENTICATION_INSTANCE* instance, bool* is_timed_out)
+{
+	int result;
+
+	if (instance->current_sas_token_put_time == INDEFINITE_TIME)
+	{
+		result = __FAILURE__;
+		LogError("Failed verifying if cbs_put_token has timed out (current_sas_token_put_time is not set)");
+	}
+	else
+	{
+		time_t current_time;
+
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			result = __FAILURE__;
+			LogError("Failed verifying if cbs_put_token has timed out (get_time failed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_083: [authentication_do_work() shall check for authentication timeout comparing the current time since `instance->current_sas_token_put_time` to `instance->cbs_request_timeout_secs`]
+		else if ((uint32_t)get_difftime(current_time, instance->current_sas_token_put_time) >= instance->cbs_request_timeout_secs)
+		{
+			*is_timed_out = true;
+			result = RESULT_OK;
+		}
+		else
+		{
+			*is_timed_out = false;
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+static int verify_sas_token_refresh_timeout(AUTHENTICATION_INSTANCE* instance, bool* is_timed_out)
+{
+	int result;
+
+	if (instance->current_sas_token_put_time == INDEFINITE_TIME)
+	{
+		result = __FAILURE__;
+		LogError("Failed verifying if SAS token refresh timed out (current_sas_token_put_time is not set)");
+	}
+	else
+	{
+		time_t current_time;
+
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			result = __FAILURE__;
+			LogError("Failed verifying if SAS token refresh timed out (get_time failed)");
+		}
+		else if ((uint32_t)get_difftime(current_time, instance->current_sas_token_put_time) >= instance->sas_token_refresh_time_secs)
+		{
+			*is_timed_out = true;
+			result = RESULT_OK;
+		}
+		else
+		{
+			*is_timed_out = false;
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+static STRING_HANDLE create_devices_path(STRING_HANDLE iothub_host_fqdn, STRING_HANDLE device_id)
+{
+	STRING_HANDLE devices_path;
+
+	if ((devices_path = STRING_new()) == NULL)
+	{
+		LogError("Failed creating devices_path (STRING_new failed)");
+	}
+    else
+    {
+        const char* device_id_c_str = STRING_c_str(device_id);
+        const char* iothub_host_fqdn_c_str = STRING_c_str(iothub_host_fqdn);
+        
+        if (STRING_sprintf(devices_path, IOTHUB_DEVICES_PATH_FMT, iothub_host_fqdn_c_str, device_id_c_str) != RESULT_OK)
+        {
+            STRING_delete(devices_path);
+            devices_path = NULL;
+            LogError("Failed creating devices_path (STRING_sprintf failed)");
+        }
+    }
+
+	return devices_path;
+}
+
+
+static bool are_device_keys_used_for_authentication(AUTHENTICATION_INSTANCE* instance)
+{
+	return (instance->current_credential_in_use == DEVICE_PRIMARY_KEY || instance->current_credential_in_use == DEVICE_SECONDARY_KEY);
+}
+
+// @returns  0 there is one more device key to be attempted, !=0 otherwise.
+static int mark_current_device_key_as_invalid(AUTHENTICATION_INSTANCE* instance)
+{
+	int result;
+
+	if (instance->current_credential_in_use == DEVICE_PRIMARY_KEY)
+	{
+		if (instance->device_secondary_key != NULL)
+		{
+			instance->current_credential_in_use = DEVICE_SECONDARY_KEY;
+			LogError("Primary key of device '%s' was marked as invalid. Using secondary key now", STRING_c_str(instance->device_id));
+			result = 0;
+		}
+		else
+		{
+			instance->current_credential_in_use = CREDENTIAL_TYPE_NONE;
+			LogError("Primary key of device '%s' was marked as invalid. No other device keys available", STRING_c_str(instance->device_id));
+			result = __FAILURE__;
+		}
+	}
+	else if (instance->current_credential_in_use == DEVICE_SECONDARY_KEY)
+	{
+		instance->current_credential_in_use = CREDENTIAL_TYPE_NONE;
+		LogError("Secondary key of device '%s' was marked as invalid. No other device keys available", STRING_c_str(instance->device_id));
+		result = __FAILURE__;
+	}
+	else
+	{
+		result = __FAILURE__;
+	}
+
+	return result;
+}
+
+static STRING_HANDLE get_current_valid_device_key(AUTHENTICATION_INSTANCE* instance)
+{
+	STRING_HANDLE device_key;
+
+	switch (instance->current_credential_in_use)
+	{
+	case DEVICE_PRIMARY_KEY:
+		device_key = instance->device_primary_key;
+		break;
+	case DEVICE_SECONDARY_KEY:
+		device_key = instance->device_secondary_key;
+		break;
+	default:
+		device_key = NULL;
+		break;
+	}
+
+	return device_key;
+}
+
+static void on_cbs_put_token_complete_callback(void* context, CBS_OPERATION_RESULT operation_result, unsigned int status_code, const char* status_description)
+{
+#ifdef NO_LOGGING
+	UNUSED(status_code);
+	UNUSED(status_description);
+#endif
+	AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)context;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_095: [`instance->is_sas_token_refresh_in_progress` and `instance->is_cbs_put_token_in_progress` shall be set to FALSE]
+	instance->is_cbs_put_token_in_progress = false;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_091: [If `result` is CBS_OPERATION_RESULT_OK `instance->state` shall be set to AUTHENTICATION_STATE_STARTED and `instance->on_state_changed_callback` invoked]
+	if (operation_result == CBS_OPERATION_RESULT_OK)
+	{
+		update_state(instance, AUTHENTICATION_STATE_STARTED);
+	}
+	else
+	{
+		LogError("CBS reported status code %u, error: '%s' for put-token operation for device '%s'", status_code, status_description, STRING_c_str(instance->device_id));
+
+		if (mark_current_device_key_as_invalid(instance) != 0)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_092: [If `result` is not CBS_OPERATION_RESULT_OK `instance->state` shall be set to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked]
+			update_state(instance, AUTHENTICATION_STATE_ERROR);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_094: [If `result` is not CBS_OPERATION_RESULT_OK and `instance->is_sas_token_refresh_in_progress` is TRUE, `instance->on_error_callback`shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_FAILED]
+			if (instance->is_sas_token_refresh_in_progress)
+			{
+				notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_FAILED);
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_093: [If `result` is not CBS_OPERATION_RESULT_OK and `instance->is_sas_token_refresh_in_progress` is FALSE, `instance->on_error_callback`shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED]
+			else
+			{
+				notify_error(instance, AUTHENTICATION_ERROR_AUTH_FAILED);
+			}
+		}
+	}
+
+	instance->is_sas_token_refresh_in_progress = false;
+}
+
+static int put_SAS_token_to_cbs(AUTHENTICATION_INSTANCE* instance, STRING_HANDLE cbs_audience, STRING_HANDLE sas_token)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_043: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_057: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_075: [authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to TRUE]
+	instance->is_cbs_put_token_in_progress = true;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_046: [The SAS token provided shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_path` as audience and passing on_cbs_put_token_complete_callback]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_058: [The SAS token shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_path` as audience and passing on_cbs_put_token_complete_callback]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_076: [The SAS token shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type, `devices_path` as audience and passing on_cbs_put_token_complete_callback]
+    const char* sas_token_c_str = STRING_c_str(sas_token);
+    const char* cbs_audience_c_str = STRING_c_str(cbs_audience);
+	if (cbs_put_token(instance->cbs_handle, SAS_TOKEN_TYPE, cbs_audience_c_str, sas_token_c_str, on_cbs_put_token_complete_callback, instance) != RESULT_OK)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_048: [If cbs_put_token() failed, authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to FALSE, destroy `devices_path` and return]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_060: [If cbs_put_token() fails, `instance->is_cbs_put_token_in_progress` shall be set to FALSE]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_078: [If cbs_put_token() fails, `instance->is_cbs_put_token_in_progress` shall be set to FALSE]
+		instance->is_cbs_put_token_in_progress = false;
+		result = __FAILURE__;
+		LogError("Failed putting SAS token to CBS for device '%s' (cbs_put_token failed)", STRING_c_str(instance->device_id));
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_047: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with current time]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_059: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with current time]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_077: [If cbs_put_token() succeeds, authentication_do_work() shall set `instance->current_sas_token_put_time` with the current time]
+		time_t current_time;
+
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Failed setting current_sas_token_put_time for device '%s' (get_time() failed)", STRING_c_str(instance->device_id));
+		}
+
+		instance->current_sas_token_put_time = current_time; // If it failed, fear not. `current_sas_token_put_time` shall be checked for INDEFINITE_TIME wherever it is used.
+
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+static int create_and_put_SAS_token_to_cbs(AUTHENTICATION_INSTANCE* instance, STRING_HANDLE device_key)
+{
+	int result;
+	double seconds_since_epoch;
+
+	if (get_seconds_since_epoch(&seconds_since_epoch) != RESULT_OK)
+	{
+		result = __FAILURE__;
+		LogError("Failed creating a SAS token (get_seconds_since_epoch() failed)");
+	}
+	else
+	{
+		STRING_HANDLE devices_path = NULL;
+		STRING_HANDLE sasTokenKeyName = NULL;
+		STRING_HANDLE sas_token = NULL;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_052: [The SAS token expiration time shall be calculated adding `instance->sas_token_lifetime_secs` to the current number of seconds since epoch time UTC]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_070: [The SAS token expiration time shall be calculated adding `instance->sas_token_lifetime_secs` to the current number of seconds since epoch time UTC]
+		size_t sas_token_expiration_time_secs = (size_t)seconds_since_epoch + instance->sas_token_lifetime_secs;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_053: [A STRING_HANDLE, referred to as `devices_path`, shall be created from the following parts: iothub_host_fqdn + "/devices/" + device_id]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_071: [A STRING_HANDLE, referred to as `devices_path`, shall be created from the following parts: iothub_host_fqdn + "/devices/" + device_id]
+		if ((devices_path = create_devices_path(instance->iothub_host_fqdn, instance->device_id)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_054: [If `devices_path` failed to be created, authentication_do_work() shall fail and return]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_072: [If `devices_path` failed to be created, authentication_do_work() shall fail and return]		
+			result = __FAILURE__;
+			LogError("Failed creating a SAS token (create_devices_path() failed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_115: [An empty STRING_HANDLE, referred to as `sasTokenKeyName`, shall be created using STRING_new()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_117: [An empty STRING_HANDLE, referred to as `sasTokenKeyName`, shall be created using STRING_new()]
+		else if ((sasTokenKeyName = STRING_new()) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_116: [If `sasTokenKeyName` failed to be created, authentication_do_work() shall fail and return]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_118: [If `sasTokenKeyName` failed to be created, authentication_do_work() shall fail and return]
+			result = __FAILURE__;
+			LogError("Failed creating a SAS token (STRING_new() failed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_055: [The SAS token shall be created using SASToken_Create(), passing the selected device key, `device_path`, `sasTokenKeyName` and expiration time as arguments]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_073: [The SAS token shall be created using SASToken_Create(), passing the selected device key, device_path, sasTokenKeyName and expiration time as arguments]
+		else if ((sas_token = SASToken_Create(device_key, devices_path, sasTokenKeyName, (size_t)sas_token_expiration_time_secs)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_056: [If SASToken_Create() fails, authentication_do_work() shall fail and return]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_074: [If SASToken_Create() fails, authentication_do_work() shall fail and return]
+			result = __FAILURE__;
+			LogError("Failed creating a SAS token (SASToken_Create() failed)");
+		}
+		else if (put_SAS_token_to_cbs(instance, devices_path, sas_token) != RESULT_OK)
+		{
+			result = __FAILURE__;
+			LogError("Failed putting SAS token to CBS");
+		}
+		else
+		{
+			result = RESULT_OK;
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_081: [authentication_do_work() shall free the memory it allocated for `devices_path`, `sasTokenKeyName` and SAS token]
+		if (devices_path != NULL)
+			STRING_delete(devices_path);
+		if (sasTokenKeyName != NULL)
+			STRING_delete(sasTokenKeyName);
+		if (sas_token != NULL)
+			STRING_delete(sas_token);
+	}
+
+	return result;
+}
+
+
+// ---------- Set/Retrieve Options Helpers ----------//
+
+static void* authentication_clone_option(const char* name, const void* value)
+{
+	void* result;
+
+	if (name == NULL)
+	{
+		LogError("Failed to clone authentication option (name is NULL)"); 
+		result = NULL;
+	}
+	else if (value == NULL)
+	{
+		LogError("Failed to clone authentication option (value is NULL)");
+		result = NULL;
+	}
+	else
+	{
+		if (strcmp(AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0 ||
+			strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0 ||
+			strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0 ||
+			strcmp(AUTHENTICATION_OPTION_SAVED_OPTIONS, name) == 0)
+		{
+			result = (void*)value;
+		}
+		else
+		{
+			LogError("Failed to clone authentication option (option with name '%s' is not suppported)", name);
+			result = NULL;
+		}
+	}
+
+	return result;
+}
+
+static void authentication_destroy_option(const char* name, const void* value)
+{
+	if (name == NULL)
+	{
+		LogError("Failed to destroy authentication option (name is NULL)");
+	}
+	else if (value == NULL)
+	{
+		LogError("Failed to destroy authentication option (value is NULL)");
+	}
+	else
+	{
+		if (strcmp(name, AUTHENTICATION_OPTION_SAVED_OPTIONS) == 0)
+		{
+			OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value);
+		}
+	}
+}
+
+
+// Public APIs:
+
+int authentication_start(AUTHENTICATION_HANDLE authentication_handle, const CBS_HANDLE cbs_handle)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_025: [If authentication_handle is NULL, authentication_start() shall fail and return __FAILURE__ as error code]
+	if (authentication_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("authentication_start failed (authentication_handle is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_026: [If `cbs_handle` is NULL, authentication_start() shall fail and return __FAILURE__ as error code]
+	else if (cbs_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("authentication_start failed (cbs_handle is NULL)");
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_027: [If authenticate state has been started already, authentication_start() shall fail and return __FAILURE__ as error code]
+		if (instance->state != AUTHENTICATION_STATE_STOPPED)
+		{
+			result = __FAILURE__;
+			LogError("authentication_start failed (messenger has already been started; current state: %d)", instance->state);
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_028: [authentication_start() shall save `cbs_handle` on `instance->cbs_handle`]
+			instance->cbs_handle = cbs_handle;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_029: [If no failures occur, `instance->state` shall be set to AUTHENTICATION_STATE_STARTING and `instance->on_state_changed_callback` invoked]
+			update_state(instance, AUTHENTICATION_STATE_STARTING);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_030: [If no failures occur, authentication_start() shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int authentication_stop(AUTHENTICATION_HANDLE authentication_handle)
+{
+	int result;
+	
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_031: [If `authentication_handle` is NULL, authentication_stop() shall fail and return __FAILURE__]
+	if (authentication_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("authentication_stop failed (authentication_handle is NULL)");
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_032: [If `instance->state` is AUTHENTICATION_STATE_STOPPED, authentication_stop() shall fail and return __FAILURE__]
+		if (instance->state == AUTHENTICATION_STATE_STOPPED)
+		{
+			result = __FAILURE__;
+			LogError("authentication_stop failed (messenger is already stopped)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_033: [`instance->cbs_handle` shall be set to NULL]
+			instance->cbs_handle = NULL;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_034: [`instance->state` shall be set to AUTHENTICATION_STATE_STOPPED and `instance->on_state_changed_callback` invoked]
+			update_state(instance, AUTHENTICATION_STATE_STOPPED);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_035: [authentication_stop() shall return success code 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+void authentication_destroy(AUTHENTICATION_HANDLE authentication_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_106: [If authentication_handle is NULL, authentication_destroy() shall return]
+	if (authentication_handle == NULL)
+	{
+		LogError("authentication_destroy failed (authentication_handle is NULL)");
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_107: [If `instance->state` is AUTHENTICATION_STATE_STARTING or AUTHENTICATION_STATE_STARTED, authentication_stop() shall be invoked and its result ignored]
+		if (instance->state != AUTHENTICATION_STATE_STOPPED)
+		{
+			(void)authentication_stop(authentication_handle);
+		}
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_108: [authentication_destroy() shall destroy `instance->device_id` using STRING_delete()]
+		if (instance->device_id != NULL)
+			STRING_delete(instance->device_id);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_109: [authentication_destroy() shall destroy `instance->device_sas_token` using STRING_delete()]
+		if (instance->device_sas_token != NULL)
+			STRING_delete(instance->device_sas_token);
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_110: [authentication_destroy() shall destroy `instance->device_primary_key` using STRING_delete()]
+		if (instance->device_primary_key != NULL)
+			STRING_delete(instance->device_primary_key);
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_111: [authentication_destroy() shall destroy `instance->device_secondary_key` using STRING_delete()]
+		if (instance->device_secondary_key != NULL)
+			STRING_delete(instance->device_secondary_key);
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_112: [authentication_destroy() shall destroy `instance->iothub_host_fqdn` using STRING_delete()]
+		if (instance->iothub_host_fqdn != NULL)
+			STRING_delete(instance->iothub_host_fqdn);
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_113: [authentication_destroy() shall destroy `instance` using free()]
+		free(instance);
+	}
+}
+
+AUTHENTICATION_HANDLE authentication_create(const AUTHENTICATION_CONFIG* config)
+{
+	AUTHENTICATION_HANDLE result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_001: [If parameter `config` or `config->device_id` are NULL, authentication_create() shall fail and return NULL.]
+	if (config == NULL)
+	{
+		result = NULL;
+		LogError("authentication_create failed (config is NULL)");
+	}
+	else if (config->device_id == NULL)
+	{
+		result = NULL;
+		LogError("authentication_create failed (config->device_id is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_002: [If device keys and SAS token are NULL, authentication_create() shall fail and return NULL.]
+	else if (config->device_sas_token == NULL && config->device_primary_key == NULL)
+	{
+		result = NULL;
+		LogError("authentication_create failed (either config->device_sas_token or config->device_primary_key must be provided; config->device_secondary_key is optional)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_003: [If device keys and SAS token are both provided, authentication_create() shall fail and return NULL.]
+	else if (config->device_sas_token != NULL && (config->device_primary_key != NULL || config->device_secondary_key != NULL))
+	{
+		result = NULL;
+		LogError("authentication_create failed (both config->device_sas_token and device device keys were provided; must provide only one)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_004: [If `config->iothub_host_fqdn` is NULL, authentication_create() shall fail and return NULL.]
+	else if (config->iothub_host_fqdn == NULL)
+	{
+		result = NULL;
+		LogError("authentication_create failed (config->iothub_host_fqdn is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_005: [If `config->on_state_changed_callback` is NULL, authentication_create() shall fail and return NULL]
+	else if (config->on_state_changed_callback == NULL)
+	{
+		result = NULL;
+		LogError("authentication_create failed (config->on_state_changed_callback is NULL)");
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_006: [authentication_create() shall allocate memory for a new authenticate state structure AUTHENTICATION_INSTANCE.]
+		if ((instance = (AUTHENTICATION_INSTANCE*)malloc(sizeof(AUTHENTICATION_INSTANCE))) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_007: [If malloc() fails, authentication_create() shall fail and return NULL.]
+			result = NULL;
+			LogError("authentication_create failed (malloc failed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_123: [authentication_create() shall initialize all fields of `instance` with 0 using memset().]
+			memset(instance, 0, sizeof(AUTHENTICATION_INSTANCE));
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_008: [authentication_create() shall save a copy of `config->device_id` into the `instance->device_id`]
+			if ((instance->device_id = STRING_construct(config->device_id)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_009: [If STRING_construct() fails, authentication_create() shall fail and return NULL]
+				result = NULL;
+				LogError("authentication_create failed (config->device_id could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_010: [If `device_config->device_sas_token` is not NULL, authentication_create() shall save a copy into the `instance->device_sas_token`]
+			else if (config->device_sas_token != NULL && (instance->device_sas_token = STRING_construct(config->device_sas_token)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_011: [If STRING_construct() fails, authentication_create() shall fail and return NULL]
+				result = NULL;
+				LogError("authentication_create failed (config->device_sas_token could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_012: [If provided, authentication_create() shall save a copy of `config->device_primary_key` into the `instance->device_primary_key`]
+			else if (config->device_primary_key != NULL && (instance->device_primary_key = STRING_construct(config->device_primary_key)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_013: [If STRING_construct() fails to copy `config->device_primary_key`, authentication_create() shall fail and return NULL]
+				result = NULL;
+				LogError("authentication_create failed (config->device_primary_key could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_014: [If provided, authentication_create() shall save a copy of `config->device_secondary_key` into `instance->device_secondary_key`]
+			else if (config->device_secondary_key != NULL && (instance->device_secondary_key = STRING_construct(config->device_secondary_key)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_015: [If STRING_construct() fails to copy `config->device_secondary_key`, authentication_create() shall fail and return NULL]
+				result = NULL;
+				LogError("authentication_create failed (config->device_secondary_key could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_016: [If provided, authentication_create() shall save a copy of `config->iothub_host_fqdn` into `instance->iothub_host_fqdn`]
+			else if ((instance->iothub_host_fqdn = STRING_construct(config->iothub_host_fqdn)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_017: [If STRING_clone() fails to copy `config->iothub_host_fqdn`, authentication_create() shall fail and return NULL]
+				result = NULL;
+				LogError("authentication_create failed (config->iothub_host_fqdn could not be copied; STRING_construct failed)");
+			}
+			else
+			{
+				instance->state = AUTHENTICATION_STATE_STOPPED;
+
+				if (instance->device_sas_token != NULL)
+				{
+					instance->current_credential_in_use = USER_PROVIDED_SAS_TOKEN;
+				}
+				else
+				{
+					instance->current_credential_in_use = DEVICE_PRIMARY_KEY;
+				}
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_018: [authentication_create() shall save `config->on_state_changed_callback` and `config->on_state_changed_callback_context` into `instance->on_state_changed_callback` and `instance->on_state_changed_callback_context`.]
+				instance->on_state_changed_callback = config->on_state_changed_callback;
+				instance->on_state_changed_callback_context = config->on_state_changed_callback_context;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_019: [authentication_create() shall save `config->on_error_callback` and `config->on_error_callback_context` into `instance->on_error_callback` and `instance->on_error_callback_context`.]
+				instance->on_error_callback = config->on_error_callback;
+				instance->on_error_callback_context = config->on_error_callback_context;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_021: [authentication_create() shall set `instance->cbs_request_timeout_secs` with the default value of UINT32_MAX]
+				instance->cbs_request_timeout_secs = DEFAULT_CBS_REQUEST_TIMEOUT_SECS;
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_022: [authentication_create() shall set `instance->sas_token_lifetime_secs` with the default value of one hour]
+				instance->sas_token_lifetime_secs = DEFAULT_SAS_TOKEN_LIFETIME_SECS;
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_023: [authentication_create() shall set `instance->sas_token_refresh_time_secs` with the default value of 30 minutes]
+				instance->sas_token_refresh_time_secs = DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_024: [If no failure occurs, authentication_create() shall return a reference to the AUTHENTICATION_INSTANCE handle]
+				result = (AUTHENTICATION_HANDLE)instance;
+			}
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_020: [If any failure occurs, authentication_create() shall free any memory it allocated previously]
+			if (result == NULL)
+			{
+				authentication_destroy((AUTHENTICATION_HANDLE)instance);
+			}
+		}
+	}
+	
+    return result;
+}
+
+void authentication_do_work(AUTHENTICATION_HANDLE authentication_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_036: [If authentication_handle is NULL, authentication_do_work() shall fail and return]
+	if (authentication_handle == NULL)
+	{
+		LogError("authentication_do_work failed (authentication_handle is NULL)");
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_038: [If `instance->is_cbs_put_token_in_progress` is TRUE, authentication_do_work() shall only verify the authentication timeout]
+		if (instance->is_cbs_put_token_in_progress)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_084: [If no timeout has occurred, authentication_do_work() shall return]
+
+			bool is_timed_out;
+			if (verify_cbs_put_token_timeout(instance, &is_timed_out) == RESULT_OK && is_timed_out)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_085: [`instance->is_cbs_put_token_in_progress` shall be set to FALSE]
+				instance->is_cbs_put_token_in_progress = false;
+			
+				if (mark_current_device_key_as_invalid(instance))
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_086: [`instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked]
+					update_state(instance, AUTHENTICATION_STATE_ERROR);
+
+					if (instance->is_sas_token_refresh_in_progress)
+					{
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_087: [If `instance->is_sas_token_refresh_in_progress` is TRUE, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT]
+						notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT);
+					}
+					else
+					{
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_088: [If `instance->is_sas_token_refresh_in_progress` is FALSE, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_TIMEOUT]
+						notify_error(instance, AUTHENTICATION_ERROR_AUTH_TIMEOUT);
+					}
+				}
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_089: [`instance->is_sas_token_refresh_in_progress` shall be set to FALSE]
+				instance->is_sas_token_refresh_in_progress = false;
+			}
+		}
+		else if (instance->state == AUTHENTICATION_STATE_STARTED)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_040: [If `instance->state` is AUTHENTICATION_STATE_STARTED and user-provided SAS token was used, authentication_do_work() shall return]
+			if (are_device_keys_used_for_authentication(instance))
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_039: [If `instance->state` is AUTHENTICATION_STATE_STARTED and device keys were used, authentication_do_work() shall only verify the SAS token refresh time]
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_065: [The SAS token shall be refreshed if the current time minus `instance->current_sas_token_put_time` equals or exceeds `instance->sas_token_refresh_time_secs`]
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_066: [If SAS token does not need to be refreshed, authentication_do_work() shall return]
+				bool is_timed_out;
+				if (verify_sas_token_refresh_timeout(instance, &is_timed_out) == RESULT_OK && is_timed_out)
+				{
+					STRING_HANDLE device_key;
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_119: [authentication_do_work() shall set `instance->is_sas_token_refresh_in_progress` to TRUE]
+					instance->is_sas_token_refresh_in_progress = true;
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_067: [authentication_do_work() shall create a SAS token using `instance->device_primary_key`, unless it has failed previously]
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_069: [If using `instance->device_primary_key` has failed previously, a SAS token shall be created using `instance->device_secondary_key`]
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_068: [If using `instance->device_primary_key` has failed previously and `instance->device_secondary_key` is not provided,  authentication_do_work() shall fail and return]
+					while ((device_key = get_current_valid_device_key(instance)) != NULL)
+					{
+						if (create_and_put_SAS_token_to_cbs(instance, device_key) == RESULT_OK)
+						{
+							break;
+						}
+						else
+						{
+							LogError("Failed refreshing SAS token (%d)", instance->current_credential_in_use);
+							(void)mark_current_device_key_as_invalid(instance);
+						}
+					}
+
+					if (!instance->is_cbs_put_token_in_progress)
+					{
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_120: [If cbs_put_token() fails, `instance->is_sas_token_refresh_in_progress` shall be set to FALSE]
+						instance->is_sas_token_refresh_in_progress = false;
+
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_079: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked]
+						update_state(instance, AUTHENTICATION_STATE_ERROR);
+
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_080: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_SAS_REFRESH_FAILED]
+						notify_error(instance, AUTHENTICATION_ERROR_SAS_REFRESH_FAILED);
+					}
+				}
+			}
+		}
+		else if (instance->state == AUTHENTICATION_STATE_STARTING)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_041: [If `instance->device_sas_token` is provided, authentication_do_work() shall put it to CBS]
+			if (instance->device_sas_token != NULL)
+			{
+				STRING_HANDLE devices_path;
+				
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_044: [A STRING_HANDLE, referred to as `devices_path`, shall be created from the following parts: iothub_host_fqdn + "/devices/" + device_id]
+				if ((devices_path = create_devices_path(instance->iothub_host_fqdn, instance->device_id)) == NULL)
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_045: [If `devices_path` failed to be created, authentication_do_work() shall set `instance->is_cbs_put_token_in_progress` to FALSE and return]
+					LogError("Failed authenticating using SAS token (create_devices_path() failed)");
+				}
+				else if (put_SAS_token_to_cbs(instance, devices_path, instance->device_sas_token) != RESULT_OK)
+				{
+					LogError("Failed authenticating using SAS token (put_SAS_token_to_cbs() failed)");
+				}
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_063: [authentication_do_work() shall free the memory it allocated for `devices_path`, `sasTokenKeyName` and SAS token]
+				STRING_delete(devices_path);
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_042: [Otherwise, authentication_do_work() shall use device keys for CBS authentication]
+			else
+			{
+				STRING_HANDLE device_key;
+
+				while ((device_key = get_current_valid_device_key(instance)) != NULL)
+				{
+					if (create_and_put_SAS_token_to_cbs(instance, device_key) == RESULT_OK)
+					{
+						break;
+					}
+					else
+					{
+						LogError("Failed authenticating device '%s' using device keys (%d)", STRING_c_str(instance->device_id), instance->current_credential_in_use);
+						(void)mark_current_device_key_as_invalid(instance);
+					}
+				}
+			}
+
+			if (!instance->is_cbs_put_token_in_progress)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_061: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked]
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_121: [If cbs_put_token() fails, `instance->state` shall be updated to AUTHENTICATION_STATE_ERROR and `instance->on_state_changed_callback` invoked]
+				update_state(instance, AUTHENTICATION_STATE_ERROR);
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_062: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED]
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_122: [If cbs_put_token() fails, `instance->on_error_callback` shall be invoked with AUTHENTICATION_ERROR_AUTH_FAILED]
+				notify_error(instance, AUTHENTICATION_ERROR_AUTH_FAILED);
+			}
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_037: [If `instance->state` is not AUTHENTICATION_STATE_STARTING or AUTHENTICATION_STATE_STARTED, authentication_do_work() shall fail and return]
+			// Nothing to be done.
+		}
+	}
+}
+
+int authentication_set_option(AUTHENTICATION_HANDLE authentication_handle, const char* name, void* value)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_097: [If `authentication_handle` or `name` or `value` is NULL, authentication_set_option shall fail and return a non-zero value]
+	if (authentication_handle == NULL || name == NULL || value == NULL)
+	{
+		LogError("authentication_set_option failed (one of the followin are NULL: authentication_handle=%p, name=%p, value=%p)", 
+			authentication_handle, name, value);
+		result = __FAILURE__;
+	}
+	else
+	{
+		AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_098: [If name matches AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, `value` shall be saved on `instance->cbs_request_timeout_secs`]
+		if (strcmp(AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0)
+		{
+			instance->cbs_request_timeout_secs = *((size_t*)value);
+			result = RESULT_OK;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_124: [If name matches AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, `value` shall be saved on `instance->sas_token_refresh_time_secs`]
+		else if (strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0)
+		{
+			instance->sas_token_refresh_time_secs = *((size_t*)value);
+			result = RESULT_OK;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_125: [If name matches AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, `value` shall be saved on `instance->sas_token_lifetime_secs`]
+		else if (strcmp(AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0)
+		{
+			instance->sas_token_lifetime_secs = *((size_t*)value);
+			result = RESULT_OK;
+		}
+		else if (strcmp(AUTHENTICATION_OPTION_SAVED_OPTIONS, name) == 0)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_098: [If name matches AUTHENTICATION_OPTION_SAVED_OPTIONS, `value` shall be applied using OptionHandler_FeedOptions]
+			if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, authentication_handle) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_126: [If OptionHandler_FeedOptions fails, authentication_set_option shall fail and return a non-zero value]
+				LogError("authentication_set_option failed (OptionHandler_FeedOptions failed)");
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_128: [If name does not match any supported option, authentication_set_option shall fail and return a non-zero value]
+			LogError("authentication_set_option failed (option with name '%s' is not suppported)", name);
+			result = __FAILURE__;
+		}
+	}
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_099: [If no errors occur, authentication_set_option shall return 0]
+	return result;
+}
+
+OPTIONHANDLER_HANDLE authentication_retrieve_options(AUTHENTICATION_HANDLE authentication_handle)
+{
+	OPTIONHANDLER_HANDLE result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_100: [If `authentication_handle` is NULL, authentication_retrieve_options shall fail and return NULL]
+	if (authentication_handle == NULL)
+	{
+		LogError("Failed to retrieve options from authentication instance (authentication_handle is NULL)");
+		result = NULL;
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_101: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create]
+		OPTIONHANDLER_HANDLE options = OptionHandler_Create(authentication_clone_option, authentication_destroy_option, (pfSetOption)authentication_set_option);
+
+		if (options == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_102: [If an OPTIONHANDLER_HANDLE instance fails to be created, authentication_retrieve_options shall fail and return NULL]
+			LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed)");
+			result = NULL;
+		}
+		else
+		{
+			AUTHENTICATION_INSTANCE* instance = (AUTHENTICATION_INSTANCE*)authentication_handle;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_103: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_104: [If OptionHandler_AddOption fails, authentication_retrieve_options shall fail and return NULL]
+			if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS, (void*)&instance->cbs_request_timeout_secs) != OPTIONHANDLER_OK)
+			{
+				LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS);
+				result = NULL;
+			}
+			else if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, (void*)&instance->sas_token_refresh_time_secs) != OPTIONHANDLER_OK)
+			{
+				LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS);
+				result = NULL;
+			}
+			else if (OptionHandler_AddOption(options, AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS, (void*)&instance->sas_token_lifetime_secs) != OPTIONHANDLER_OK)
+			{
+				LogError("Failed to retrieve options from authentication instance (OptionHandler_Create failed for option '%s')", AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS);
+				result = NULL;
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_127: [If no failures occur, authentication_retrieve_options shall return the OPTIONHANDLER_HANDLE instance]
+				result = options;
+			}
+
+			if (result == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_AUTH_09_105: [If authentication_retrieve_options fails, any allocated memory shall be freed]
+				OptionHandler_Destroy(options);
+			}
+		}
+	}
+
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_cbs_auth.h	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,71 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef IOTHUBTRANSPORT_AMQP_CBS_AUTH_H
+#define IOTHUBTRANSPORT_AMQP_CBS_AUTH_H
+
+#include <stdint.h>
+#include "iothub_transport_ll.h"
+#include "azure_uamqp_c/cbs.h"
+#include "azure_c_shared_utility/umock_c_prod.h"
+#include "azure_c_shared_utility/optionhandler.h"
+
+static const char* AUTHENTICATION_OPTION_SAVED_OPTIONS = "saved_authentication_options";
+static const char* AUTHENTICATION_OPTION_CBS_REQUEST_TIMEOUT_SECS = "cbs_request_timeout_secs";
+static const char* AUTHENTICATION_OPTION_SAS_TOKEN_REFRESH_TIME_SECS = "sas_token_refresh_time_secs";
+static const char* AUTHENTICATION_OPTION_SAS_TOKEN_LIFETIME_SECS = "sas_token_lifetime_secs";
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+	typedef enum AUTHENTICATION_STATE_TAG
+	{
+		AUTHENTICATION_STATE_STOPPED,
+		AUTHENTICATION_STATE_STARTING,
+		AUTHENTICATION_STATE_STARTED,
+		AUTHENTICATION_STATE_ERROR
+	} AUTHENTICATION_STATE;
+
+	typedef enum AUTHENTICATION_ERROR_TAG
+	{
+		AUTHENTICATION_ERROR_AUTH_TIMEOUT,
+		AUTHENTICATION_ERROR_AUTH_FAILED,
+		AUTHENTICATION_ERROR_SAS_REFRESH_TIMEOUT,
+		AUTHENTICATION_ERROR_SAS_REFRESH_FAILED
+	} AUTHENTICATION_ERROR_CODE;
+
+	typedef void(*ON_AUTHENTICATION_STATE_CHANGED_CALLBACK)(void* context, AUTHENTICATION_STATE previous_state, AUTHENTICATION_STATE new_state);
+	typedef void(*ON_AUTHENTICATION_ERROR_CALLBACK)(void* context, AUTHENTICATION_ERROR_CODE error_code);
+
+	typedef struct AUTHENTICATION_CONFIG_TAG
+	{
+		char* device_id;
+		char* device_primary_key;
+		char* device_secondary_key;
+		char* device_sas_token;
+		char* iothub_host_fqdn;
+
+		ON_AUTHENTICATION_STATE_CHANGED_CALLBACK on_state_changed_callback;
+		void* on_state_changed_callback_context;
+
+		ON_AUTHENTICATION_ERROR_CALLBACK on_error_callback;
+		void* on_error_callback_context;
+	} AUTHENTICATION_CONFIG;
+
+	typedef struct AUTHENTICATION_INSTANCE* AUTHENTICATION_HANDLE;
+
+	MOCKABLE_FUNCTION(, AUTHENTICATION_HANDLE, authentication_create, const AUTHENTICATION_CONFIG*, config);
+	MOCKABLE_FUNCTION(, int, authentication_start, AUTHENTICATION_HANDLE, authentication_handle, const CBS_HANDLE, cbs_handle);
+	MOCKABLE_FUNCTION(, int, authentication_stop, AUTHENTICATION_HANDLE, authentication_handle);
+	MOCKABLE_FUNCTION(, void, authentication_do_work, AUTHENTICATION_HANDLE, authentication_handle);
+	MOCKABLE_FUNCTION(, void, authentication_destroy, AUTHENTICATION_HANDLE, authentication_handle);
+	MOCKABLE_FUNCTION(, int, authentication_set_option, AUTHENTICATION_HANDLE, authentication_handle, const char*, name, void*, value);
+	MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, authentication_retrieve_options, AUTHENTICATION_HANDLE, authentication_handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*IOTHUBTRANSPORT_AMQP_CBS_AUTH_H*/
--- a/iothubtransport_amqp_common.c	Fri Feb 24 14:00:00 2017 -0800
+++ b/iothubtransport_amqp_common.c	Fri Mar 10 11:46:55 2017 -0800
@@ -9,438 +9,353 @@
 #include "azure_c_shared_utility/agenttime.h"
 #include "azure_c_shared_utility/gballoc.h"
 #include "azure_c_shared_utility/crt_abstractions.h"
+#include "azure_c_shared_utility/singlylinkedlist.h"
 #include "azure_c_shared_utility/doublylinkedlist.h"
 #include "azure_c_shared_utility/xlogging.h"
 #include "azure_c_shared_utility/platform.h"
 #include "azure_c_shared_utility/strings.h"
 #include "azure_c_shared_utility/urlencode.h"
 #include "azure_c_shared_utility/tlsio.h"
-#include "azure_c_shared_utility/vector.h"
+#include "azure_c_shared_utility/optionhandler.h"
 
 #include "azure_uamqp_c/cbs.h"
-#include "azure_uamqp_c/link.h"
+#include "azure_uamqp_c/session.h"
 #include "azure_uamqp_c/message.h"
-#include "azure_uamqp_c/amqpvalue.h"
-#include "azure_uamqp_c/message_receiver.h"
-#include "azure_uamqp_c/message_sender.h"
 #include "azure_uamqp_c/messaging.h"
-#include "azure_uamqp_c/sasl_mssbcbs.h"
-#include "azure_uamqp_c/saslclientio.h"
 
-#include "uamqp_messaging.h"
 #include "iothub_client_ll.h"
 #include "iothub_client_options.h"
 #include "iothub_client_private.h"
-#include "iothubtransportamqp_auth.h"
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
 #include "iothubtransportamqp_methods.h"
 #endif
 #include "iothubtransport_amqp_common.h"
+#include "iothubtransport_amqp_connection.h"
+#include "iothubtransport_amqp_device.h"
 #include "iothub_client_version.h"
 
-#define RESULT_OK 0
+#define RESULT_OK                                 0
+#define INDEFINITE_TIME                           ((time_t)(-1))
+#define DEFAULT_CBS_REQUEST_TIMEOUT_SECS          30
+#define DEFAULT_DEVICE_STATE_CHANGE_TIMEOUT_SECS  60
+#define DEFAULT_EVENT_SEND_TIMEOUT_SECS           300
+#define DEFAULT_SAS_TOKEN_LIFETIME_SECS           3600
+#define DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS       1800
+#define MAX_NUMBER_OF_DEVICE_FAILURES             5
 
-#define INDEFINITE_TIME ((time_t)(-1))
-#define RFC1035_MAX_FQDN_LENGTH 255
-#define DEFAULT_SAS_TOKEN_LIFETIME_MS 3600000
-#define DEFAULT_CBS_REQUEST_TIMEOUT_MS 30000
-#define DEFAULT_CONTAINER_ID "default_container_id"
-#define DEFAULT_INCOMING_WINDOW_SIZE UINT_MAX
-#define DEFAULT_OUTGOING_WINDOW_SIZE 100
-#define MESSAGE_RECEIVER_LINK_NAME_TAG "receiver"
-#define MESSAGE_RECEIVER_TARGET_ADDRESS "target"
-#define MESSAGE_RECEIVER_MAX_LINK_SIZE 65536
-#define MESSAGE_SENDER_LINK_NAME_TAG "sender"
-#define MESSAGE_SENDER_SOURCE_NAME_TAG "source"
-#define MESSAGE_SENDER_MAX_LINK_SIZE UINT64_MAX
 
-typedef enum RESULT_TAG
-{
-    RESULT_SUCCESS,
-    RESULT_INVALID_ARGUMENT,
-    RESULT_TIME_OUT,
-    RESULT_RETRYABLE_ERROR,
-    RESULT_CRITICAL_ERROR
-} RESULT;
+// ---------- Data Definitions ---------- //
 
-typedef struct AMQP_TRANSPORT_STATE_TAG
+typedef enum AMQP_TRANSPORT_AUTHENTICATION_MODE_TAG
 {
-    // FQDN of the IoT Hub.
-    STRING_HANDLE iotHubHostFqdn;
-
-    // TSL I/O transport.
-    XIO_HANDLE tls_io;
-    // Pointer to the function that creates the TLS I/O (internal use only).
-    AMQP_GET_IO_TRANSPORT underlying_io_transport_provider;
-    // AMQP connection.
-    CONNECTION_HANDLE connection;
-    // AMQP session.
-    SESSION_HANDLE session;
-    // All things CBS (and only CBS)
-    AMQP_TRANSPORT_CBS_CONNECTION cbs_connection;
+	AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET,
+	AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS,
+	AMQP_TRANSPORT_AUTHENTICATION_MODE_X509
+} AMQP_TRANSPORT_AUTHENTICATION_MODE;
 
-    // Current AMQP connection state;
-    AMQP_MANAGEMENT_STATE connection_state;
-
-    AMQP_TRANSPORT_CREDENTIAL_TYPE preferred_credential_type;
-    // List of registered devices.
-    VECTOR_HANDLE registered_devices;
-    // Turns logging on and off
-    bool is_trace_on;
-    // Used to generate unique AMQP link names
-    int link_count;
-
-    /*here are the options from the xio layer if any is saved*/
-    OPTIONHANDLER_HANDLE xioOptions;
+typedef struct AMQP_TRANSPORT_INSTANCE_TAG
+{
+    STRING_HANDLE iothub_host_fqdn;                                     // FQDN of the IoT Hub.
+    XIO_HANDLE tls_io;                                                  // TSL I/O transport.
+    AMQP_GET_IO_TRANSPORT underlying_io_transport_provider;             // Pointer to the function that creates the TLS I/O (internal use only).
+	AMQP_CONNECTION_HANDLE amqp_connection;                             // Base amqp connection with service.
+	AMQP_CONNECTION_STATE amqp_connection_state;                        // Current state of the amqp_connection.
+	AMQP_TRANSPORT_AUTHENTICATION_MODE preferred_authentication_mode;   // Used to avoid registered devices using different authentication modes.
+	SINGLYLINKEDLIST_HANDLE registered_devices;                         // List of devices currently registered in this transport.
+    bool is_trace_on;                                                   // Turns logging on and off.
+    OPTIONHANDLER_HANDLE saved_tls_options;                             // Here are the options from the xio layer if any is saved.
+	bool is_connection_retry_required;                                  // Flag that controls whether the connection should be restablished or not.
+	
+	size_t option_sas_token_lifetime_secs;                              // Device-specific option.
+	size_t option_sas_token_refresh_time_secs;                          // Device-specific option.
+	size_t option_cbs_request_timeout_secs;                             // Device-specific option.
+	size_t option_send_event_timeout_secs;                              // Device-specific option.
 } AMQP_TRANSPORT_INSTANCE;
 
-typedef struct AMQP_TRANSPORT_DEVICE_STATE_TAG
+typedef struct AMQP_TRANSPORT_DEVICE_INSTANCE_TAG
 {
-    // Identity of the device.
-    STRING_HANDLE deviceId;
-    // contains the credentials to be used
-    AUTHENTICATION_STATE_HANDLE authentication;
-
-    // Address to which the transport will connect to and send events.
-    STRING_HANDLE targetAddress;
-    // Address to which the transport will connect to and receive messages from.
-    STRING_HANDLE messageReceiveAddress;
-    // Internal parameter that identifies the current logical device within the service.
-    STRING_HANDLE devicesPath;
-    // Saved reference to the IoTHub LL Client.
-    IOTHUB_CLIENT_LL_HANDLE iothub_client_handle;
-    // Saved reference to the transport the device is registered on.
-    AMQP_TRANSPORT_INSTANCE* transport_state;
-    // AMQP link used by the event sender.
-    LINK_HANDLE sender_link;
-    // uAMQP event sender.
-    MESSAGE_SENDER_HANDLE message_sender;
-    // State of the message sender.
-    MESSAGE_SENDER_STATE message_sender_state;
-    // Internal flag that controls if messages should be received or not.
-    bool receive_messages;
-    // AMQP link used by the message receiver.
-    LINK_HANDLE receiver_link;
-    // uAMQP message receiver.
-    MESSAGE_RECEIVER_HANDLE message_receiver;
-    // Message receiver state.
-    MESSAGE_RECEIVER_STATE message_receiver_state;
-    // List with events still pending to be sent. It is provided by the upper layer.
-    PDLIST_ENTRY waitingToSend;
-    // Internal list with the items currently being processed/sent through uAMQP.
-    DLIST_ENTRY inProgress;
+    STRING_HANDLE device_id;                                            // Identity of the device.
+	DEVICE_HANDLE device_handle;                                        // Logic unit that performs authentication, messaging, etc.
+    IOTHUB_CLIENT_LL_HANDLE iothub_client_handle;                       // Saved reference to the IoTHub LL Client.
+    AMQP_TRANSPORT_INSTANCE* transport_instance;                        // Saved reference to the transport the device is registered on.
+	PDLIST_ENTRY waiting_to_send;                                       // List of events waiting to be sent to the iot hub (i.e., haven't been processed by the transport yet).
+	DEVICE_STATE device_state;                                          // Current state of the device_handle instance.
+	size_t number_of_previous_failures;                                 // Number of times the device has failed in sequence; this value is reset to 0 if device succeeds to authenticate, send and/or recv messages.
+	size_t number_of_send_event_complete_failures;                      // Number of times on_event_send_complete was called in row with an error.
+	time_t time_of_last_state_change;                                   // Time the device_handle last changed state; used to track timeouts of device_start_async and device_stop.
+	unsigned int max_state_change_timeout_secs;                         // Maximum number of seconds allowed for device_handle to complete start and stop state changes.
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
     // the methods portion
-    IOTHUBTRANSPORT_AMQP_METHODS_HANDLE methods_handle;
+    IOTHUBTRANSPORT_AMQP_METHODS_HANDLE methods_handle;                 // Handle to instance of module that deals with device methods for AMQP.
     // is subscription for methods needed?
-    bool subscribe_methods_needed;
+    bool subscribe_methods_needed;                                       // Indicates if should subscribe for device methods.
     // is the transport subscribed for methods?
-    bool subscribed_for_methods;
+    bool subscribed_for_methods;                                         // Indicates if device is subscribed for device methods.
 #endif
-} AMQP_TRANSPORT_DEVICE_STATE;
+} AMQP_TRANSPORT_DEVICE_INSTANCE;
 
+typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG
+{
+	AMQP_TRANSPORT_DEVICE_INSTANCE* device_state;
+    char* link_name;
+    delivery_number message_id;
+} MESSAGE_DISPOSITION_CONTEXT;
 
-// Auxiliary functions
+// ---------- General Helpers ---------- //
 
-static STRING_HANDLE concat3Params(const char* prefix, const char* infix, const char* suffix)
+// @brief
+//     Evaluates if the ammount of time since start_time is greater or lesser than timeout_in_secs.
+// @param is_timed_out
+//     Set to true if a timeout has been reached, false otherwise. Not set if any failure occurs.
+// @returns
+//     0 if no failures occur, non-zero otherwise.
+static int is_timeout_reached(time_t start_time, unsigned int timeout_in_secs, bool *is_timed_out)
 {
-    STRING_HANDLE result = NULL;
-    char* concat;
-    size_t totalLength = strlen(prefix) + strlen(infix) + strlen(suffix) + 1; // One extra for \0.
+	int result;
+
+	if (start_time == INDEFINITE_TIME)
+	{
+		LogError("Failed to verify timeout (start_time is INDEFINITE)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		time_t current_time;
 
-    if ((concat = (char*)malloc(totalLength)) != NULL)
-    {
-        (void)strcpy(concat, prefix);
-        (void)strcat(concat, infix);
-        (void)strcat(concat, suffix);
-        result = STRING_construct(concat);
-        free(concat);
-    }
-    else
-    {
-        result = NULL;
-    }
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Failed to verify timeout (get_time failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			if (get_difftime(current_time, start_time) >= timeout_in_secs)
+			{
+				*is_timed_out = 1;
+			}
+			else
+			{
+				*is_timed_out = 0;
+			}
 
-    return result;
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
 }
 
-static int getSecondsSinceEpoch(size_t* seconds)
+static STRING_HANDLE get_target_iothub_fqdn(const IOTHUBTRANSPORT_CONFIG* config)
 {
-    int result;
-    time_t current_time;
-    
-    if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
-    {
-        LogError("Failed getting the current local time (get_time() failed)");
-        result = __FAILURE__;
-    }
-    else
-    {
-        *seconds = (size_t)get_difftime(current_time, (time_t)0);
-        
-        result = RESULT_OK;
-    }
-    
-    return result;
+	STRING_HANDLE fqdn;
+
+	if (config->upperConfig->protocolGatewayHostName == NULL)
+	{
+		if ((fqdn = STRING_construct_sprintf("%s.%s", config->upperConfig->iotHubName, config->upperConfig->iotHubSuffix)) == NULL)
+		{
+			LogError("Failed to copy iotHubName and iotHubSuffix (STRING_construct_sprintf failed)");
+		}
+	}
+	else if ((fqdn = STRING_construct(config->upperConfig->protocolGatewayHostName)) == NULL)
+	{
+		LogError("Failed to copy protocolGatewayHostName (STRING_construct failed)");
+	}
+
+	return fqdn;
 }
 
-static STRING_HANDLE create_link_name(const char* deviceId, const char* tag, int index)
-{
-    STRING_HANDLE name = NULL;
-    char name_str[1024];
 
-    if (sprintf(name_str, "link-%s-%s-%i", deviceId, tag, index) <= 0)
-    {
-        LogError("create_link_name failed (sprintf failed)");
-    }
-    else if ((name = STRING_construct(name_str)) == NULL)
-    {
-        LogError("create_link_name failed (STRING_construct failed)");
-    }
-
-    return name;
-}
-
-static STRING_HANDLE create_link_source_name(STRING_HANDLE link_name)
-{
-    STRING_HANDLE name = NULL;
-    char name_str[1024];
+// ---------- Register/Unregister Helpers ---------- //
 
-    if (sprintf(name_str, "%s-source", STRING_c_str(link_name)) <= 0)
-    {
-        LogError("create_link_source_name failed (sprintf failed)");
-    }
-    else if ((name = STRING_construct(name_str)) == NULL)
-    {
-        LogError("create_link_source_name failed (STRING_construct failed)");
-    }
-
-    return name;
-}
-
-static STRING_HANDLE create_link_target_name(STRING_HANDLE link_name)
+static void internal_destroy_amqp_device_instance(AMQP_TRANSPORT_DEVICE_INSTANCE *trdev_inst)
 {
-    STRING_HANDLE name = NULL;
-    char name_str[1024];
+#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
+	if (trdev_inst->methods_handle != NULL)
+	{
+		iothubtransportamqp_methods_destroy(trdev_inst->methods_handle);
+	}
+#endif
+	if (trdev_inst->device_handle != NULL)
+	{
+		device_destroy(trdev_inst->device_handle);
+	}
 
-    if (sprintf(name_str, "%s-target", STRING_c_str(link_name)) <= 0)
-    {
-        LogError("create_link_target_name failed (sprintf failed)");
-    }
-    else if ((name = STRING_construct(name_str)) == NULL)
-    {
-        LogError("create_link_target_name failed (STRING_construct failed)");
-    }
+	if (trdev_inst->device_id != NULL)
+	{
+		STRING_delete(trdev_inst->device_id);
+	}
 
-    return name;
+	free(trdev_inst);
 }
 
-// Auxiliary function to be used on VECTOR_find_if()
-static bool findDeviceById(const void* element, const void* value)
-{
-    const AMQP_TRANSPORT_DEVICE_STATE* device_state = *(const AMQP_TRANSPORT_DEVICE_STATE **)element;
-    const char* deviceId = (const char *)value;
-
-    return (strcmp(STRING_c_str(device_state->deviceId), deviceId) == 0);
-}
-
-static void trackEventInProgress(IOTHUB_MESSAGE_LIST* message, AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    DList_RemoveEntryList(&message->entry);
-    DList_InsertTailList(&device_state->inProgress, &message->entry);
-}
-
-static IOTHUB_MESSAGE_LIST* getNextEventToSend(AMQP_TRANSPORT_DEVICE_STATE* device_state)
+// @brief
+//     Saves the new state, if it is different than the previous one.
+static void on_device_state_changed_callback(void* context, DEVICE_STATE previous_state, DEVICE_STATE new_state)
 {
-    IOTHUB_MESSAGE_LIST* message;
-
-    if (!DList_IsListEmpty(device_state->waitingToSend))
-    {
-        PDLIST_ENTRY list_entry = device_state->waitingToSend->Flink;
-        message = containingRecord(list_entry, IOTHUB_MESSAGE_LIST, entry);
-    }
-    else
-    {
-        message = NULL;
-    }
-
-    return message;
-}
-
-static int isEventInInProgressList(IOTHUB_MESSAGE_LIST* message)
-{
-    return !DList_IsListEmpty(&message->entry);
-}
-
-static void removeEventFromInProgressList(IOTHUB_MESSAGE_LIST* message)
-{
-    DList_RemoveEntryList(&message->entry);
-    DList_InitializeListHead(&message->entry);
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_061: [If `new_state` is the same as `previous_state`, on_device_state_changed_callback shall return]
+	if (context != NULL && new_state != previous_state)
+	{
+		AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context;
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_062: [If `new_state` shall be saved into the `registered_device` instance]
+		registered_device->device_state = new_state;
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_063: [If `registered_device->time_of_last_state_change` shall be set using get_time()]
+		registered_device->time_of_last_state_change = get_time(NULL);
+	}
 }
 
-static void rollEventBackToWaitList(IOTHUB_MESSAGE_LIST* message, AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    removeEventFromInProgressList(message);
-    DList_InsertTailList(device_state->waitingToSend, &message->entry);
-}
-
-static void rollEventsBackToWaitList(AMQP_TRANSPORT_DEVICE_STATE* device_state)
+// @brief    Auxiliary function to be used to find a device in the registered_devices list.
+// @returns  true if the device ids match, false otherwise.
+static bool find_device_by_id_callback(LIST_ITEM_HANDLE list_item, const void* match_context)
 {
-    PDLIST_ENTRY entry = device_state->inProgress.Blink;
+	bool result;
+
+	if (match_context == NULL)
+	{
+		result = false;
+	}
+	else
+	{
+		AMQP_TRANSPORT_DEVICE_INSTANCE* device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item);
 
-    while (entry != &device_state->inProgress)
-    {
-        IOTHUB_MESSAGE_LIST* message = containingRecord(entry, IOTHUB_MESSAGE_LIST, entry);
-        entry = entry->Blink;
-        rollEventBackToWaitList(message, device_state);
-    }
+		if (device_instance == NULL || 
+			device_instance->device_id == NULL || 
+			STRING_c_str(device_instance->device_id) != match_context)
+		{
+			result = false;
+		}
+		else
+		{
+			result = true;
+		}
+	}
+
+	return result;
 }
 
-static void on_message_send_complete(void* context, MESSAGE_SEND_RESULT send_result)
+// @brief       Verifies if a device is already registered within the transport that owns the list of registered devices.
+// @remarks     Returns the correspoding LIST_ITEM_HANDLE in registered_devices, if found.
+// @returns     true if the device is already in the list, false otherwise.
+static bool is_device_registered_ex(SINGLYLINKEDLIST_HANDLE registered_devices, const char* device_id, LIST_ITEM_HANDLE *list_item)
 {
-    IOTHUB_MESSAGE_LIST* message = (IOTHUB_MESSAGE_LIST*)context;
-
-    IOTHUB_CLIENT_CONFIRMATION_RESULT iot_hub_send_result;
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_142: [The callback 'on_message_send_complete' shall pass to the upper layer callback an IOTHUB_CLIENT_CONFIRMATION_OK if the result received is MESSAGE_SEND_OK] 
-    if (send_result == MESSAGE_SEND_OK)
-    {
-        iot_hub_send_result = IOTHUB_CLIENT_CONFIRMATION_OK;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_143: [The callback 'on_message_send_complete' shall pass to the upper layer callback an IOTHUB_CLIENT_CONFIRMATION_ERROR if the result received is MESSAGE_SEND_ERROR]
-    else
-    {
-        iot_hub_send_result = IOTHUB_CLIENT_CONFIRMATION_ERROR;
-    }
+	return ((*list_item = singlylinkedlist_find(registered_devices, find_device_by_id_callback, device_id)) != NULL ? 1 : 0);
+}
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_102: [The callback 'on_message_send_complete' shall invoke the upper layer callback for message received if provided] 
-    if (message->callback != NULL)
-    {
-        message->callback(iot_hub_send_result, message->context);
-    }
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_100: [The callback 'on_message_send_complete' shall remove the target message from the in-progress list after the upper layer callback] 
-    if (isEventInInProgressList(message))
-    {
-        removeEventFromInProgressList(message);
-    }
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_151: [The callback 'on_message_send_complete' shall destroy the message handle (IOTHUB_MESSAGE_HANDLE) using IoTHubMessage_Destroy()]
-    IoTHubMessage_Destroy(message->messageHandle);
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_152: [The callback 'on_message_send_complete' shall destroy the IOTHUB_MESSAGE_LIST instance]
-    free(message); 
+// @brief       Verifies if a device is already registered within the transport that owns the list of registered devices.
+// @returns     true if the device is already in the list, false otherwise.
+static bool is_device_registered(AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance)
+{
+	LIST_ITEM_HANDLE list_item;
+	const char* device_id = STRING_c_str(amqp_device_instance->device_id);
+	return is_device_registered_ex(amqp_device_instance->transport_instance->registered_devices, device_id, &list_item);
 }
 
-static AMQP_VALUE on_message_received(const void* context, MESSAGE_HANDLE message)
+
+// ---------- Callbacks ---------- //
+
+static MESSAGE_CALLBACK_INFO* MESSAGE_CALLBACK_INFO_Create(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, AMQP_TRANSPORT_DEVICE_INSTANCE* device_state)
 {
-    AMQP_VALUE result = NULL;
-    int api_call_result;
-    IOTHUB_MESSAGE_HANDLE iothub_message = NULL;
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_195: [The callback 'on_message_received' shall shall get a IOTHUB_MESSAGE_HANDLE instance out of the uamqp's MESSAGE_HANDLE instance by using IoTHubMessage_CreateFromUamqpMessage()]
-    if ((api_call_result = IoTHubMessage_CreateFromUamqpMessage(message, &iothub_message)) != RESULT_OK)
-    {
-        LogError("Transport failed processing the message received (error = %d).", api_call_result);
+	MESSAGE_CALLBACK_INFO* result;
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_196: [If IoTHubMessage_CreateFromUamqpMessage fails, the callback 'on_message_received' shall reject the incoming message by calling messaging_delivery_rejected() and return.]
-        result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed reading AMQP message");
-    }
-    else
-    {
-        IOTHUBMESSAGE_DISPOSITION_RESULT disposition_result;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_104: [The callback 'on_message_received' shall invoke IoTHubClient_LL_MessageCallback() passing the client and the incoming message handles as parameters] 
-        disposition_result = IoTHubClient_LL_MessageCallback((IOTHUB_CLIENT_LL_HANDLE)context, iothub_message);
+	if ((result = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO))) == NULL)
+	{
+		LogError("Failed creating MESSAGE_CALLBACK_INFO (malloc failed)");
+	}
+	else
+	{
+		MESSAGE_DISPOSITION_CONTEXT* tc;
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_197: [The callback 'on_message_received' shall destroy the IOTHUB_MESSAGE_HANDLE instance after invoking IoTHubClient_LL_MessageCallback().]
-        IoTHubMessage_Destroy(iothub_message);
+		if ((tc = (MESSAGE_DISPOSITION_CONTEXT*)malloc(sizeof(MESSAGE_DISPOSITION_CONTEXT))) == NULL)
+		{
+			LogError("Failed creating MESSAGE_DISPOSITION_CONTEXT (malloc failed)");
+			free(result);
+			result = NULL;
+		}
+		else
+		{
+			if (mallocAndStrcpy_s(&(tc->link_name), disposition_info->source) == 0)
+			{
+				tc->device_state = device_state;
+				tc->message_id = disposition_info->message_id;
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_105: [The callback 'on_message_received' shall return the result of messaging_delivery_accepted() if the IoTHubClient_LL_MessageCallback() returns IOTHUBMESSAGE_ACCEPTED] 
-        if (disposition_result == IOTHUBMESSAGE_ACCEPTED)
-        {
-            result = messaging_delivery_accepted();
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_106: [The callback 'on_message_received' shall return the result of messaging_delivery_released() if the IoTHubClient_LL_MessageCallback() returns IOTHUBMESSAGE_ABANDONED] 
-        else if (disposition_result == IOTHUBMESSAGE_ABANDONED)
-        {
-            result = messaging_delivery_released();
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_107: [The callback 'on_message_received' shall return the result of messaging_delivery_rejected("Rejected by application", "Rejected by application") if the IoTHubClient_LL_MessageCallback() returns IOTHUBMESSAGE_REJECTED] 
-        else if (disposition_result == IOTHUBMESSAGE_REJECTED)
-        {
-            result = messaging_delivery_rejected("Rejected by application", "Rejected by application");
-        }
-    }
+				result->messageHandle = message;
+				result->transportContext = tc;
+			}
+			else
+			{
+				LogError("Failed creating MESSAGE_CALLBACK_INFO (mallocAndStrcyp_s failed)");
+				free(tc);
+				free(result);
+				result = NULL;
+			}
+		}
+	}
 
-    return result;
+	return result;
+}
+
+static void MESSAGE_CALLBACK_INFO_Destroy(MESSAGE_CALLBACK_INFO* message_callback_info)
+{
+	free(message_callback_info->transportContext->link_name);
+	free(message_callback_info->transportContext);
+	free(message_callback_info);
 }
 
-static void destroyConnection(AMQP_TRANSPORT_INSTANCE* transport_state)
+static DEVICE_MESSAGE_DISPOSITION_RESULT get_device_disposition_result_from(IOTHUBMESSAGE_DISPOSITION_RESULT iothubclient_disposition_result)
 {
-    if (transport_state->cbs_connection.cbs_handle != NULL)
-    {
-        cbs_destroy(transport_state->cbs_connection.cbs_handle);
-        transport_state->cbs_connection.cbs_handle = NULL;
-    }
-
-    if (transport_state->session != NULL)
-    {
-        session_destroy(transport_state->session);
-        transport_state->session = NULL;
-    }
-
-    if (transport_state->connection != NULL)
-    {
-        connection_destroy(transport_state->connection);
-        transport_state->connection = NULL;
-    }
+	DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result;
 
-    if (transport_state->cbs_connection.sasl_io != NULL)
-    {
-        xio_destroy(transport_state->cbs_connection.sasl_io);
-        transport_state->cbs_connection.sasl_io = NULL;
-    }
-
-    if (transport_state->cbs_connection.sasl_mechanism != NULL)
-    {
-        saslmechanism_destroy(transport_state->cbs_connection.sasl_mechanism);
-        transport_state->cbs_connection.sasl_mechanism = NULL;
-    }
+	if (iothubclient_disposition_result == IOTHUBMESSAGE_ACCEPTED)
+	{
+		device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED;
+	}
+	else if (iothubclient_disposition_result == IOTHUBMESSAGE_ABANDONED)
+	{
+		device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED;
+	}
+	else if (iothubclient_disposition_result == IOTHUBMESSAGE_REJECTED)
+	{
+		device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED;
+	}
+	else
+	{
+		LogError("Failed getting corresponding DEVICE_MESSAGE_DISPOSITION_RESULT for IOTHUBMESSAGE_DISPOSITION_RESULT (%d is not supported)", iothubclient_disposition_result);
+		device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED;
+	}
 
-    if (transport_state->tls_io != NULL)
-    {
-        /*before destroying, we shall save its options for later use*/
-        transport_state->xioOptions = xio_retrieveoptions(transport_state->tls_io);
-        if (transport_state->xioOptions == NULL)
-        {
-            LogError("unable to retrieve xio_retrieveoptions");
-        }
-        
-        xio_destroy(transport_state->tls_io);
-        transport_state->tls_io = NULL;
-    }
+	return device_disposition_result;
 }
 
-static void on_amqp_management_state_changed(void* context, AMQP_MANAGEMENT_STATE new_amqp_management_state, AMQP_MANAGEMENT_STATE previous_amqp_management_state)
+static DEVICE_MESSAGE_DISPOSITION_RESULT on_message_received(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, void* context)
 {
-    (void)previous_amqp_management_state;
-    AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)context;
+	AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context;
+	DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result;
+	MESSAGE_CALLBACK_INFO* message_data;
 
-    if (transport_state != NULL)
-    {
-        transport_state->connection_state = new_amqp_management_state;
-    }
+	if ((message_data = MESSAGE_CALLBACK_INFO_Create(message, disposition_info, amqp_device_instance)) == NULL)
+	{
+		LogError("Failed processing message received (failed to assemble callback info)");
+		device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED;
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_089: [IoTHubClient_LL_MessageCallback() shall be invoked passing the client and the incoming message handles as parameters]
+		if (IoTHubClient_LL_MessageCallback(amqp_device_instance->iothub_client_handle, message_data) != true)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_090: [If IoTHubClient_LL_MessageCallback() fails, on_message_received_callback shall return DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED]
+			LogError("Failed processing message received (IoTHubClient_LL_MessageCallback failed)");
+			IoTHubMessage_Destroy(message);
+			device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED;
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_091: [If IoTHubClient_LL_MessageCallback() succeeds, on_message_received_callback shall return DEVICE_MESSAGE_DISPOSITION_RESULT_NONE]
+			device_disposition_result = DEVICE_MESSAGE_DISPOSITION_RESULT_NONE;
+		}
+	}
+
+    return device_disposition_result;
 }
 
-static void on_connection_io_error(void* context)
-{
-    AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)context;
-
-    if (transport_state != NULL)
-    {
-        transport_state->connection_state = AMQP_MANAGEMENT_STATE_ERROR;
-    }
-}
 
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
 static void on_methods_error(void* context)
@@ -452,14 +367,14 @@
 static void on_methods_unsubscribed(void* context)
 {
     /* Codess_SRS_IOTHUBTRANSPORT_AMQP_METHODS_12_001: [ `on_methods_unsubscribed` calls iothubtransportamqp_methods_unsubscribe. ]*/
-    AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)context;
+    AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context;
     IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod(device_state);
 }
 
 static int on_method_request_received(void* context, const char* method_name, const unsigned char* request, size_t request_size, IOTHUBTRANSPORT_AMQP_METHOD_HANDLE method_handle)
 {
     int result;
-    AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)context;
+    AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context;
 
     /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_017: [ `on_methods_request_received` shall call the `IoTHubClient_LL_DeviceMethodComplete` passing the method name, request buffer and size and the newly created BUFFER handle. ]*/
     /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_022: [ The status code shall be the return value of the call to `IoTHubClient_LL_DeviceMethodComplete`. ]*/
@@ -475,7 +390,7 @@
     return result;
 }
 
-static int subscribe_methods(AMQP_TRANSPORT_DEVICE_STATE* deviceState)
+static int subscribe_methods(AMQP_TRANSPORT_DEVICE_INSTANCE* deviceState)
 {
     int result;
 
@@ -485,9 +400,16 @@
     }
     else
     {
+		SESSION_HANDLE session_handle;
+
+		if ((amqp_connection_get_session_handle(deviceState->transport_instance->amqp_connection, &session_handle)) != RESULT_OK)
+		{
+			LogError("Device '%s' failed subscribing for methods (failed getting session handle)", STRING_c_str(deviceState->device_id));
+			result = __FAILURE__;
+		}
         /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_024: [ If the device authentication status is AUTHENTICATION_STATUS_OK and `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` was called to register for methods, `IoTHubTransport_AMQP_Common_DoWork` shall call `iothubtransportamqp_methods_subscribe`. ]*/
         /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_027: [ The current session handle shall be passed to `iothubtransportamqp_methods_subscribe`. ]*/
-        if (iothubtransportamqp_methods_subscribe(deviceState->methods_handle, deviceState->transport_state->session, on_methods_error, deviceState, on_method_request_received, deviceState, on_methods_unsubscribed, deviceState) != 0)
+        else if (iothubtransportamqp_methods_subscribe(deviceState->methods_handle, session_handle, on_methods_error, deviceState, on_method_request_received, deviceState, on_methods_unsubscribed, deviceState) != 0)
         {
             LogError("Cannot subscribe for methods");
             result = __FAILURE__;
@@ -503,785 +425,777 @@
 }
 #endif
 
-static void set_session_options(SESSION_HANDLE session)
+
+// ---------- Underlying TLS I/O Helpers ---------- //
+
+// @brief
+//     Retrieves the options of the current underlying TLS I/O instance and saves in the transport instance.
+// @remarks
+//     This is used when the new underlying I/O transport (TLS I/O, or WebSockets, etc) needs to be recreated, 
+//     and the options previously set must persist.
+//
+//     If no TLS I/O instance was created yet, results in failure.
+// @returns
+//     0 if succeeds, non-zero otherwise.
+static int save_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance)
 {
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_065: [IoTHubTransport_AMQP_Common_DoWork shall apply a default value of UINT_MAX for the parameter 'AMQP incoming window'] 
-    if (session_set_incoming_window(session, (uint32_t)DEFAULT_INCOMING_WINDOW_SIZE) != 0)
-    {
-        LogError("Failed to set the AMQP incoming window size.");
-    }
+	int result;
+
+	if (transport_instance->tls_io == NULL)
+	{
+		LogError("failed saving underlying I/O transport options (tls_io instance is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		OPTIONHANDLER_HANDLE fresh_options;
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_115: [IoTHubTransport_AMQP_Common_DoWork shall apply a default value of 100 for the parameter 'AMQP outgoing window'] 
-    if (session_set_outgoing_window(session, DEFAULT_OUTGOING_WINDOW_SIZE) != 0)
-    {
-        LogError("Failed to set the AMQP outgoing window size.");
-    }
+		if ((fresh_options = xio_retrieveoptions(transport_instance->tls_io)) == NULL)
+		{
+			LogError("failed saving underlying I/O transport options (tls_io instance is NULL)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			OPTIONHANDLER_HANDLE previous_options = transport_instance->saved_tls_options;
+			transport_instance->saved_tls_options = fresh_options;
+
+			if (previous_options != NULL)
+			{
+				OptionHandler_Destroy(previous_options);
+			}
+
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+static void destroy_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance)
+{
+	if (transport_instance->saved_tls_options != NULL)
+	{
+		OptionHandler_Destroy(transport_instance->saved_tls_options);
+		transport_instance->saved_tls_options = NULL;
+	}
 }
 
-static int establishConnection(AMQP_TRANSPORT_INSTANCE* transport_state)
+// @brief
+//     Applies TLS I/O options if previously saved to a new TLS I/O instance.
+// @returns
+//     0 if succeeds, non-zero otherwise.
+static int restore_underlying_io_transport_options(AMQP_TRANSPORT_INSTANCE* transport_instance, XIO_HANDLE xio_handle)
+{
+	int result;
+
+	if (transport_instance->saved_tls_options == NULL)
+	{
+		result = RESULT_OK;
+	}
+	else
+	{
+		if (OptionHandler_FeedOptions(transport_instance->saved_tls_options, xio_handle) != OPTIONHANDLER_OK)
+		{
+			LogError("Failed feeding existing options to new TLS instance."); 
+			result = __FAILURE__;
+		}
+		else
+		{
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+// @brief    Destroys the XIO_HANDLE obtained with underlying_io_transport_provider(), saving its options beforehand.
+static void destroy_underlying_io_transport(AMQP_TRANSPORT_INSTANCE* transport_instance)
+{
+	if (transport_instance->tls_io != NULL)
+	{
+		xio_destroy(transport_instance->tls_io);
+		transport_instance->tls_io = NULL;
+	}
+}
+
+// @brief    Invokes underlying_io_transport_provider() and retrieves a new XIO_HANDLE to use for I/O (TLS, or websockets, or w/e is supported).
+// @param    xio_handle: if successfull, set with the new XIO_HANDLE acquired; not changed otherwise.
+// @returns  0 if successfull, non-zero otherwise.
+static int get_new_underlying_io_transport(AMQP_TRANSPORT_INSTANCE* transport_instance, XIO_HANDLE *xio_handle)
+{
+	int result;
+
+	if ((*xio_handle = transport_instance->underlying_io_transport_provider(STRING_c_str(transport_instance->iothub_host_fqdn))) == NULL)
+	{
+		LogError("Failed to obtain a TLS I/O transport layer (underlying_io_transport_provider() failed)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		if (restore_underlying_io_transport_options(transport_instance, *xio_handle) != RESULT_OK)
+		{
+			/*pessimistically hope TLS will fail, be recreated and options re-given*/
+			LogError("Failed to apply options previous saved to new underlying I/O transport instance.");
+		}
+
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+
+// ---------- AMQP connection establishment/tear-down, connectry retry ---------- //
+
+static void on_amqp_connection_state_changed(const void* context, AMQP_CONNECTION_STATE previous_state, AMQP_CONNECTION_STATE new_state)
+{
+	if (context != NULL && new_state != previous_state)
+	{
+		AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)context;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_059: [`new_state` shall be saved in to the transport instance]
+		transport_instance->amqp_connection_state = new_state;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_060: [If `new_state` is AMQP_CONNECTION_STATE_ERROR, the connection shall be flagged as faulty (so the connection retry logic can be triggered)]
+		if (new_state == AMQP_CONNECTION_STATE_ERROR)
+		{
+			LogError("Transport received an ERROR from the amqp_connection (state changed %d->%d); it will be flagged for connection retry.", previous_state, new_state);
+
+			transport_instance->is_connection_retry_required = true;
+		}
+	}
+}
+
+static int establish_amqp_connection(AMQP_TRANSPORT_INSTANCE* transport_instance)
 {
     int result;
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_110: [IoTHubTransport_AMQP_Common_DoWork shall create the TLS IO using transport_state->io_transport_provider callback function] 
-    if (transport_state->tls_io == NULL &&
-        (transport_state->tls_io = transport_state->underlying_io_transport_provider(STRING_c_str(transport_state->iotHubHostFqdn))) == NULL)
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_136: [If transport_state->io_transport_provider_callback fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately]
-        result = __FAILURE__;
-        LogError("Failed to obtain a TLS I/O transport layer.");
-    }
-    else
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_239: [IoTHubTransport_AMQP_Common_DoWork shall apply any TLS I/O saved options to the new TLS instance using OptionHandler_FeedOptions]
-        if (transport_state->xioOptions != NULL)
-        {
-            if (OptionHandler_FeedOptions(transport_state->xioOptions, transport_state->tls_io) != 0)
-            {
-                LogError("unable to replay options to TLS"); /*pessimistically hope TLS will fail, be recreated and options re-given*/
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_240: [If OptionHandler_FeedOptions succeeds, IoTHubTransport_AMQP_Common_DoWork shall destroy any TLS options saved on the transport state]
-                OptionHandler_Destroy(transport_state->xioOptions);
-                transport_state->xioOptions = NULL;
-            }
-        }
-
-        switch (transport_state->preferred_credential_type)
-        {
-            case (DEVICE_KEY):
-            case (DEVICE_SAS_TOKEN):
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_056: [IoTHubTransport_AMQP_Common_DoWork shall create the SASL mechanism using AMQP's saslmechanism_create() API] 
-                if ((transport_state->cbs_connection.sasl_mechanism = saslmechanism_create(saslmssbcbs_get_interface(), NULL)) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_057: [If saslmechanism_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately]
-                    result = __FAILURE__;
-                    LogError("Failed to create a SASL mechanism.");
-                }
-                else
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_060: [IoTHubTransport_AMQP_Common_DoWork shall create the SASL I / O layer using the xio_create() C Shared Utility API]
-                    SASLCLIENTIO_CONFIG sasl_client_config;
-                    sasl_client_config.sasl_mechanism = transport_state->cbs_connection.sasl_mechanism;
-                    sasl_client_config.underlying_io = transport_state->tls_io;
-                    if ((transport_state->cbs_connection.sasl_io = xio_create(saslclientio_get_interface_description(), &sasl_client_config)) == NULL)
-                    {
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_061: [If xio_create() fails creating the SASL I/O layer, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately] 
-                        result = __FAILURE__;
-                        LogError("Failed to create a SASL I/O layer.");
-                    }
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_062: [IoTHubTransport_AMQP_Common_DoWork shall create the connection with the IoT service using connection_create2() AMQP API, passing the SASL I/O layer, IoT Hub FQDN and container ID as parameters (pass NULL for callbacks)] 
-                    else if ((transport_state->connection = connection_create2(transport_state->cbs_connection.sasl_io, STRING_c_str(transport_state->iotHubHostFqdn), DEFAULT_CONTAINER_ID, NULL, NULL, NULL, NULL, on_connection_io_error, (void*)transport_state)) == NULL)
-                    {
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_063: [If connection_create2() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately.] 
-                        result = __FAILURE__;
-                        LogError("Failed to create the AMQP connection.");
-                    }
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_137: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP session session_create() AMQP API, passing the connection instance as parameter]
-                    else if ((transport_state->session = session_create(transport_state->connection, NULL, NULL)) == NULL)
-                    {
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_138 : [If session_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately]
-                        result = __FAILURE__;
-                        LogError("Failed to create the AMQP session.");
-                    }
-                    else
-                    {
-                        set_session_options(transport_state->session);
+	if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET)
+	{
+		LogError("Failed establishing connection (transport doesn't have a preferred authentication mode set; unexpected!).");
+		result = __FAILURE__;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_023: [If `instance->tls_io` is NULL, it shall be set invoking instance->underlying_io_transport_provider()]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_025: [When `instance->tls_io` is created, it shall be set with `instance->saved_tls_options` using OptionHandler_FeedOptions()]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_111: [If OptionHandler_FeedOptions() fails, it shall be ignored]
+	else if (transport_instance->tls_io == NULL &&
+		get_new_underlying_io_transport(transport_instance, &transport_instance->tls_io) != RESULT_OK)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_024: [If instance->underlying_io_transport_provider() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return]
+		LogError("Failed establishing connection (failed to obtain a TLS I/O transport layer).");
+		result = __FAILURE__;
+	}
+	else
+	{
+		AMQP_CONNECTION_CONFIG amqp_connection_config;
+		amqp_connection_config.iothub_host_fqdn = STRING_c_str(transport_instance->iothub_host_fqdn);
+		amqp_connection_config.underlying_io_transport = transport_instance->tls_io;
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_029: [`instance->is_trace_on` shall be set into `AMQP_CONNECTION_CONFIG->is_trace_on`]
+		amqp_connection_config.is_trace_on = transport_instance->is_trace_on;
+		amqp_connection_config.on_state_changed_callback = on_amqp_connection_state_changed;
+		amqp_connection_config.on_state_changed_context = transport_instance;
 
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_066: [IoTHubTransport_AMQP_Common_DoWork shall establish the CBS connection using the cbs_create() AMQP API] 
-                        if ((transport_state->cbs_connection.cbs_handle = cbs_create(transport_state->session, on_amqp_management_state_changed, NULL)) == NULL)
-                        {
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_067: [If cbs_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately] 
-                            result = __FAILURE__;
-                            LogError("Failed to create the CBS connection.");
-                        }
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_139: [IoTHubTransport_AMQP_Common_DoWork shall open the CBS connection using the cbs_open() AMQP API] 
-                        else if (cbs_open(transport_state->cbs_connection.cbs_handle) != 0)
-                        {
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_140: [If cbs_open() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately]
-                            result = __FAILURE__;
-                            LogError("Failed to open the connection with CBS.");
-                        }
-                        else
-                        {
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_199: [The value of the option `logtrace` saved by the transport instance shall be applied to each new connection instance using connection_set_trace().]
-                            connection_set_trace(transport_state->connection, transport_state->is_trace_on);
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_200: [The value of the option `logtrace` saved by the transport instance shall be applied to each new SASL_IO instance using xio_setoption().]
-                            (void)xio_setoption(transport_state->cbs_connection.sasl_io, OPTION_LOG_TRACE, &transport_state->is_trace_on);
-                            result = RESULT_OK;
-                        }
-                    }
-                }
-                break;
-            }
-            case(X509):
-            {
-                /*Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_006: [ IoTHubTransport_AMQP_Common_DoWork shall not establish a CBS connection. ]*/
-                /*Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_005: [ IoTHubTransport_AMQP_Common_DoWork shall create the connection with the IoT service using connection_create2() AMQP API, passing the TLS I/O layer, IoT Hub FQDN and container ID as parameters (pass NULL for callbacks) ]*/
-                if ((transport_state->connection = connection_create2(transport_state->tls_io, STRING_c_str(transport_state->iotHubHostFqdn), DEFAULT_CONTAINER_ID, NULL, NULL, NULL, NULL, on_connection_io_error, (void*)transport_state)) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_063: [If connection_create2() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately.] 
-                    result = __FAILURE__;
-                    LogError("Failed to create the AMQP connection.");
-                }
-                else
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_137: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP session session_create() AMQP API, passing the connection instance as parameter]
-                    if ((transport_state->session = session_create(transport_state->connection, NULL, NULL)) == NULL)
-                    {
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_138 : [If session_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately]
-                        result = __FAILURE__;
-                        LogError("Failed to create the AMQP session.");
-                    }
-                    else
-                    {
-                        set_session_options(transport_state->session);
-                    
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_199: [The value of the option `logtrace` saved by the transport instance shall be applied to each new connection instance using connection_set_trace().]
-                        connection_set_trace(transport_state->connection, transport_state->is_trace_on);
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_201: [The value of the option `logtrace` saved by the transport instance shall be applied to each new TLS_IO instance using xio_setoption().]
-                        (void)xio_setoption(transport_state->tls_io, OPTION_LOG_TRACE, &transport_state->is_trace_on);
-                        result = RESULT_OK;
-                    }
-                }
-                break;
-            }
-            default:
-            {
-                LogError("internal error: unexpected enum value for transport_state->credential.credentialType = %d", transport_state->preferred_credential_type);
-                result = __FAILURE__;
-                break;
-            }
-        }/*switch*/
-    }
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_027: [If `transport->preferred_authentication_method` is CBS, AMQP_CONNECTION_CONFIG shall be set with `create_sasl_io` = true and `create_cbs_connection` = true]
+		if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS)
+		{
+			amqp_connection_config.create_sasl_io = true;
+			amqp_connection_config.create_cbs_connection = true;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_028: [If `transport->preferred_credential_method` is X509, AMQP_CONNECTION_CONFIG shall be set with `create_sasl_io` = false and `create_cbs_connection` = false]
+		else if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_X509)
+		{
+			amqp_connection_config.create_sasl_io = false;
+			amqp_connection_config.create_cbs_connection = false;
+		}
+		// If new AMQP_TRANSPORT_AUTHENTICATION_MODE values are added, they need to be covered here.
 
-    if (result != RESULT_OK)
-    {
-        destroyConnection(transport_state);
-    }
+		transport_instance->amqp_connection_state = AMQP_CONNECTION_STATE_CLOSED;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_026: [If `transport->connection` is NULL, it shall be created using amqp_connection_create()]
+		if ((transport_instance->amqp_connection = amqp_connection_create(&amqp_connection_config)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_030: [If amqp_connection_create() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return]
+			LogError("Failed establishing connection (failed to create the amqp_connection instance).");
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_110: [If amqp_connection_create() succeeds, IoTHubTransport_AMQP_Common_DoWork shall proceed to invoke amqp_connection_do_work]
+			result = RESULT_OK;
+		}
+	}
 
     return result;
 }
 
-static void attachDeviceClientTypeToLink(LINK_HANDLE link)
+static void prepare_device_for_connection_retry(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device)
 {
-    fields attach_properties;
-    AMQP_VALUE deviceClientTypeKeyName;
-    AMQP_VALUE deviceClientTypeValue;
-    int result;
-
-    //
-    // Attempt to add the device client type string to the attach properties.
-    // If this doesn't happen, well, this isn't that important.  We can operate
-    // without this property.  It's worth noting that even though we are going
-    // on, the reasons any of these operations fail don't bode well for the
-    // actual upcoming attach.
-    //
-
-    if ((attach_properties = amqpvalue_create_map()) == NULL)
-    {
-        LogError("Failed to create the map for device client type.");
-    }
-    else
-    {
-        if ((deviceClientTypeKeyName = amqpvalue_create_symbol("com.microsoft:client-version")) == NULL)
-        {
-            LogError("Failed to create the key name for the device client type.");
-        }
-        else
-        {
-            if ((deviceClientTypeValue = amqpvalue_create_string(CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION)) == NULL)
-            {
-                LogError("Failed to create the key value for the device client type.");
-            }
-            else
-            {
-                if ((result = amqpvalue_set_map_value(attach_properties, deviceClientTypeKeyName, deviceClientTypeValue)) != 0)
-                {
-                    LogError("Failed to set the property map for the device client type.  Error code is: %d", result);
-                }
-                else if ((result = link_set_attach_properties(link, attach_properties)) != 0)
-                {
-                    LogError("Unable to attach the device client type to the link properties. Error code is: %d", result);
-                }
+#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_032: [ Each `instance->registered_devices` shall unsubscribe from receiving C2D method requests by calling `iothubtransportamqp_methods_unsubscribe`]
+    iothubtransportamqp_methods_unsubscribe(registered_device->methods_handle);
+	registered_device->subscribed_for_methods = 0;
+#endif
 
-                amqpvalue_destroy(deviceClientTypeValue);
-            }
-
-            amqpvalue_destroy(deviceClientTypeKeyName);
-        }
-
-        amqpvalue_destroy(attach_properties);
-    }
-}
-
-static void destroyEventSender(AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    if (device_state->message_sender != NULL)
-    {
-        messagesender_destroy(device_state->message_sender);
-        device_state->message_sender = NULL;
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_031: [device_stop() shall be invoked on all `instance->registered_devices` that are not already stopped]
+	if (registered_device->device_state != DEVICE_STATE_STOPPED)
+	{
+		if (device_stop(registered_device->device_handle) != RESULT_OK)
+		{
+			LogError("Failed preparing device '%s' for connection retry (device_stop failed)", STRING_c_str(registered_device->device_id));
+		}
+	}
 
-        link_destroy(device_state->sender_link);
-        device_state->sender_link = NULL;
-    }
-}
-
-static void on_event_sender_state_changed(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state)
-{
-    if (context != NULL)
-    {
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)context;
-
-        if (device_state->transport_state->is_trace_on)
-        {
-            LogInfo("Event sender state changed [%s, %d->%d]", STRING_c_str(device_state->deviceId), previous_state, new_state);
-        }
-
-        device_state->message_sender_state = new_state;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_192: [If a message sender instance changes its state to MESSAGE_SENDER_STATE_ERROR (first transition only) the connection retry logic shall be triggered]
-        if (new_state != previous_state && new_state == MESSAGE_SENDER_STATE_ERROR)
-        {
-            device_state->transport_state->connection_state = AMQP_MANAGEMENT_STATE_ERROR;
-        }
-    }
+	registered_device->number_of_previous_failures = 0;
+	registered_device->number_of_send_event_complete_failures = 0;
 }
 
-static int createEventSender(AMQP_TRANSPORT_DEVICE_STATE* device_state)
+static void prepare_for_connection_retry(AMQP_TRANSPORT_INSTANCE* transport_instance)
 {
-    int result;
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_034: [`instance->tls_io` options shall be saved on `instance->saved_tls_options` using xio_retrieveoptions()]
+	if (save_underlying_io_transport_options(transport_instance) != RESULT_OK)
+	{
+		LogError("Failed saving TLS I/O options while preparing for connection retry; failure will be ignored");
+	}
 
-    STRING_HANDLE link_name = NULL;
-    STRING_HANDLE source_name = NULL;
-    AMQP_VALUE source = NULL;
-    AMQP_VALUE target = NULL;
+	LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(transport_instance->registered_devices);
+
+	while (list_item != NULL)
+	{
+		AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item);
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_251: [Every new message_sender AMQP link shall be created using unique link and source names per device, per connection]
-    if ((link_name = create_link_name(STRING_c_str(device_state->deviceId), MESSAGE_SENDER_LINK_NAME_TAG, device_state->transport_state->link_count++)) == NULL)
-    {
-        LogError("Failed creating a name for the AMQP message sender link.");
-        result = __FAILURE__;
-    }
-    else if ((source_name = create_link_source_name(link_name)) == NULL)
-    {
-        LogError("Failed creating a name for the AMQP message sender source.");
-        result = __FAILURE__;
-    }
-    else if ((source = messaging_create_source(STRING_c_str(source_name))) == NULL)
-    {
-        LogError("Failed creating AMQP messaging source attribute.");
-        result = __FAILURE__;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_252: [The message_sender AMQP link shall be created using the `target` address created according to SRS_IOTHUBTRANSPORTAMQP_09_014]
-    else if ((target = messaging_create_target(STRING_c_str(device_state->targetAddress))) == NULL)
-    {
-        LogError("Failed creating AMQP messaging target attribute.");
-        result = __FAILURE__;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_068: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP link using link_create(), with role as 'role_sender'] 
-    else if ((device_state->sender_link = link_create(device_state->transport_state->session, STRING_c_str(link_name), role_sender, source, target)) == NULL)
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_069: [If IoTHubTransport_AMQP_Common_DoWork fails to create the AMQP link for sending messages, the function shall fail and return immediately, flagging the connection to be re-stablished] 
-        LogError("Failed creating AMQP link for message sender.");
-        result = __FAILURE__;
-    }
-    else
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_119: [IoTHubTransport_AMQP_Common_DoWork shall apply a default value of 65536 for the parameter 'Link MAX message size']
-        if (link_set_max_message_size(device_state->sender_link, MESSAGE_SENDER_MAX_LINK_SIZE) != RESULT_OK)
-        {
-            LogError("Failed setting AMQP link max message size.");
-        }
+		if (registered_device == NULL)
+		{
+			LogError("Failed preparing device for connection retry (singlylinkedlist_item_get_value failed)");
+		}
+		else
+		{
+			prepare_device_for_connection_retry(registered_device);
+		}
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_249: [The message sender link should have a property set with the type and version of the IoT Hub client application, set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_250: [If the message sender link fails to have the client type and version set on its properties, the failure shall be ignored]
-        attachDeviceClientTypeToLink(device_state->sender_link);
+		list_item = singlylinkedlist_get_next_item(list_item);
+	}
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_070: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP message sender using messagesender_create() AMQP API] 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_191: [IoTHubTransport_AMQP_Common_DoWork shall create each AMQP message sender tracking its state changes with a callback function]
-        if ((device_state->message_sender = messagesender_create(device_state->sender_link, on_event_sender_state_changed, (void*)device_state)) == NULL)
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_071: [IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately if the AMQP message sender instance fails to be created, flagging the connection to be re-established] 
-            LogError("Could not allocate AMQP message sender");
-            result = __FAILURE__;
-        }
-        else
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_072: [IoTHubTransport_AMQP_Common_DoWork shall open the AMQP message sender using messagesender_open() AMQP API] 
-            if (messagesender_open(device_state->message_sender) != RESULT_OK)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_073: [IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately if the AMQP message sender instance fails to be opened, flagging the connection to be re-established] 
-                LogError("Failed opening the AMQP message sender.");
-                result = __FAILURE__;
-            }
-            else
-            {
-                result = RESULT_OK;
-            }
-        }
-    }
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_033: [`instance->connection` shall be destroyed using amqp_connection_destroy()]
+	amqp_connection_destroy(transport_instance->amqp_connection);
+	transport_instance->amqp_connection = NULL;
+    transport_instance->amqp_connection_state = AMQP_CONNECTION_STATE_CLOSED;
 
-    if (link_name != NULL)
-        STRING_delete(link_name);
-    if (source_name != NULL)
-        STRING_delete(source_name);
-    if (source != NULL)
-        amqpvalue_destroy(source);
-    if (target != NULL)
-        amqpvalue_destroy(target);
-
-    return result;
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_035: [`instance->tls_io` shall be destroyed using xio_destroy()]
+	destroy_underlying_io_transport(transport_instance);
 }
 
-static int destroyMessageReceiver(AMQP_TRANSPORT_DEVICE_STATE* device_state)
+
+// @brief    Verifies if the crendentials used by the device match the requirements and authentication mode currently supported by the transport.
+// @returns  true if credentials are good, false otherwise.
+static bool is_device_credential_acceptable(const IOTHUB_DEVICE_CONFIG* device_config, AMQP_TRANSPORT_AUTHENTICATION_MODE preferred_authentication_mode)
 {
-    int result;
+    bool result;
 
-    if (device_state->message_receiver == NULL)
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_003: [IoTHubTransport_AMQP_Common_Register shall return NULL if both deviceKey and deviceSasToken are not NULL.]
+	if ((device_config->deviceSasToken != NULL) && (device_config->deviceKey != NULL))
+	{
+		LogError("Credential of device '%s' is not acceptable (must provide EITHER deviceSasToken OR deviceKey)", device_config->deviceId);
+		result = false;
+	}
+    else if (preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET)
     {
-        result = RESULT_OK;
+        result = true;
+    }
+    else if (preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_X509 && (device_config->deviceKey != NULL || device_config->deviceSasToken != NULL))
+    {
+		LogError("Credential of device '%s' is not acceptable (transport is using X509 certificate authentication, but device config contains deviceKey or sasToken)", device_config->deviceId);
+        result = false;
+    }
+    else if (preferred_authentication_mode != AMQP_TRANSPORT_AUTHENTICATION_MODE_X509 && (device_config->deviceKey == NULL && device_config->deviceSasToken == NULL))
+    {
+		LogError("Credential of device '%s' is not acceptable (transport is using CBS authentication, but device config does not contain deviceKey nor sasToken)", device_config->deviceId);
+        result = false;
     }
     else
     {
-        if (messagereceiver_close(device_state->message_receiver) != RESULT_OK)
-        {
-            LogError("Failed closing the AMQP message receiver.");
-            result = __FAILURE__;
-        }
-        else
-        {
-            messagereceiver_destroy(device_state->message_receiver);
-
-            device_state->message_receiver = NULL;
-
-            link_destroy(device_state->receiver_link);
-
-            device_state->receiver_link = NULL;
-
-            result = RESULT_OK;
-        }
-    }
-
-    return result;
-}
-
-static void on_message_receiver_state_changed(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state)
-{
-    if (context != NULL)
-    {
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)context;
-
-        if (device_state->transport_state->is_trace_on)
-        {
-            LogInfo("Message receiver state changed [%s; %d->%d]", STRING_c_str(device_state->deviceId), previous_state, new_state);
-        }
-
-        device_state->message_receiver_state = new_state;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_190: [If a message_receiver instance changes its state to MESSAGE_RECEIVER_STATE_ERROR (first transition only) the connection retry logic shall be triggered]
-        if (new_state != previous_state && new_state == MESSAGE_RECEIVER_STATE_ERROR)
-        {
-            device_state->transport_state->connection_state = AMQP_MANAGEMENT_STATE_ERROR;
-        }
-    }
-}
-
-static int createMessageReceiver(AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    int result;
-
-    STRING_HANDLE link_name = NULL;
-    STRING_HANDLE target_name = NULL;
-    AMQP_VALUE source = NULL;
-    AMQP_VALUE target = NULL;
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_246: [Every new message_receiver AMQP link shall be created using unique link and target names per device, per connection]
-    if ((link_name = create_link_name(STRING_c_str(device_state->deviceId), MESSAGE_RECEIVER_LINK_NAME_TAG, device_state->transport_state->link_count++)) == NULL)
-    {
-        LogError("Failed creating a name for the AMQP message receiver link.");
-                    result = __FAILURE__;
-    }
-    else if ((target_name = create_link_target_name(link_name)) == NULL)
-    {
-        LogError("Failed creating a name for the AMQP message receiver target.");
-                    result = __FAILURE__;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_253: [The message_receiver AMQP link shall be created using the `source` address created according to SRS_IOTHUBTRANSPORTAMQP_09_053]
-    else if ((source = messaging_create_source(STRING_c_str(device_state->messageReceiveAddress))) == NULL)
-    {
-        LogError("Failed creating AMQP message receiver source attribute.");
-        result = __FAILURE__;
-    }
-    else if ((target = messaging_create_target(STRING_c_str(target_name))) == NULL)
-    {
-        LogError("Failed creating AMQP message receiver target attribute.");
-        result = __FAILURE__;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_074: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP link using link_create(), with role as 'role_receiver'] 
-    else if ((device_state->receiver_link = link_create(device_state->transport_state->session, STRING_c_str(link_name), role_receiver, source, target)) == NULL)
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_075: [If IoTHubTransport_AMQP_Common_DoWork fails to create the AMQP link for receiving messages, the function shall fail and return immediately, flagging the connection to be re-stablished] 
-        LogError("Failed creating AMQP link for message receiver.");
-        result = __FAILURE__;
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_076: [IoTHubTransport_AMQP_Common_DoWork shall set the receiver link settle mode as receiver_settle_mode_first] 
-    else if (link_set_rcv_settle_mode(device_state->receiver_link, receiver_settle_mode_first) != RESULT_OK)
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_141: [If IoTHubTransport_AMQP_Common_DoWork fails to set the settle mode on the AMQP link for receiving messages, the function shall fail and return immediately, flagging the connection to be re-stablished]
-        LogError("Failed setting AMQP link settle mode for message receiver.");
-        result = __FAILURE__;
-    }
-    else
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_119: [IoTHubTransport_AMQP_Common_DoWork shall apply a default value of 65536 for the parameter 'Link MAX message size']
-        if (link_set_max_message_size(device_state->receiver_link, MESSAGE_RECEIVER_MAX_LINK_SIZE) != RESULT_OK)
-        {
-            LogError("Failed setting AMQP link max message size for message receiver.");
-        }
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_247: [The message receiver link should have a property set with the type and version of the IoT Hub client application, set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_248: [If the message receiver link fails to have the client type and version set on its properties, the failure shall be ignored]
-        attachDeviceClientTypeToLink(device_state->receiver_link);
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_077: [IoTHubTransport_AMQP_Common_DoWork shall create the AMQP message receiver using messagereceiver_create() AMQP API] 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_189: [IoTHubTransport_AMQP_Common_DoWork shall create each AMQP message_receiver tracking its state changes with a callback function]
-        if ((device_state->message_receiver = messagereceiver_create(device_state->receiver_link, on_message_receiver_state_changed, (void*)device_state)) == NULL)
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_078: [IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately if the AMQP message receiver instance fails to be created, flagging the connection to be re-established] 
-            LogError("Could not allocate AMQP message receiver.");
-            result = __FAILURE__;
-        }
-        else
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_079: [IoTHubTransport_AMQP_Common_DoWork shall open the AMQP message receiver using messagereceiver_open() AMQP API, passing a callback function for handling C2D incoming messages] 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_123: [IoTHubTransport_AMQP_Common_DoWork shall create each AMQP message_receiver passing the 'on_message_received' as the callback function] 
-            if (messagereceiver_open(device_state->message_receiver, on_message_received, (const void*)device_state->iothub_client_handle) != RESULT_OK)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_080: [IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately if the AMQP message receiver instance fails to be opened, flagging the connection to be re-established] 
-                LogError("Failed opening the AMQP message receiver.");
-                result = __FAILURE__;
-            }
-            else
-            {
-                result = RESULT_OK;
-            }
-        }
-    }
-
-    if (link_name != NULL)
-        STRING_delete(link_name);
-    if (target_name != NULL)
-        STRING_delete(target_name);
-    if (source != NULL)
-        amqpvalue_destroy(source);
-    if (target != NULL)
-        amqpvalue_destroy(target);
-
-    return result;
-}
-
-static int sendPendingEvents(AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    int result = RESULT_OK;
-    IOTHUB_MESSAGE_LIST* message;
-
-    while ((message = getNextEventToSend(device_state)) != NULL)
-    {
-        result = __FAILURE__;
-
-        MESSAGE_HANDLE amqp_message = NULL;
-        bool is_message_error = false;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_086: [IoTHubTransport_AMQP_Common_DoWork shall move queued events to an "in-progress" list right before processing them for sending]
-        trackEventInProgress(message, device_state);
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_193: [IoTHubTransport_AMQP_Common_DoWork shall get a MESSAGE_HANDLE instance out of the event's IOTHUB_MESSAGE_HANDLE instance by using message_create_from_iothub_message().]
-        if ((result = message_create_from_iothub_message(message->messageHandle, &amqp_message)) != RESULT_OK)
-        {
-            LogError("Failed creating AMQP message (error=%d).", result);
-            result = __FAILURE__;
-            is_message_error = true;
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_097: [IoTHubTransport_AMQP_Common_DoWork shall pass the MESSAGE_HANDLE intance to uAMQP for sending (along with on_message_send_complete callback) using messagesender_send()] 
-        else if (messagesender_send(device_state->message_sender, amqp_message, on_message_send_complete, message) != RESULT_OK)
-        {
-            LogError("Failed sending the AMQP message.");
-            result = __FAILURE__;
-        }
-        else
-        {
-            result = RESULT_OK;
-        }
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_194: [IoTHubTransport_AMQP_Common_DoWork shall destroy the MESSAGE_HANDLE instance after messagesender_send() is invoked.]
-        if (amqp_message != NULL)
-        {
-            // It can be destroyed because AMQP keeps a clone of the message.
-            message_destroy(amqp_message);
-        }
-
-        if (result != RESULT_OK)
-        {
-            if (is_message_error)
-            {
-                on_message_send_complete(message, MESSAGE_SEND_ERROR);
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_111: [If message_create_from_iothub_message() fails, IoTHubTransport_AMQP_Common_DoWork notify the failure, roll back the event to waitToSend list and return]
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_113: [If messagesender_send() fails, IoTHubTransport_AMQP_Common_DoWork notify the failure, roll back the event to waitToSend list and return]
-                rollEventBackToWaitList(message, device_state);
-                break;
-            }
-        }
-    }
-
-    return result;
-}
-
-static void prepareDeviceForConnectionRetry(AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    if (authentication_reset(device_state->authentication) != RESULT_OK)
-    {
-        LogError("Failed resetting the authenticatication state of device %s", STRING_c_str(device_state->deviceId));
-    }
-
-#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-    iothubtransportamqp_methods_unsubscribe(device_state->methods_handle);
-    device_state->subscribed_for_methods = false;
-#endif
-
-    destroyMessageReceiver(device_state);
-    destroyEventSender(device_state);
-    rollEventsBackToWaitList(device_state);
-}
-
-static void prepareForConnectionRetry(AMQP_TRANSPORT_INSTANCE* transport_state)
-{
-    size_t number_of_registered_devices = VECTOR_size(transport_state->registered_devices);
-
-    for (size_t i = 0; i < number_of_registered_devices; i++)
-    {
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = *(AMQP_TRANSPORT_DEVICE_STATE**)VECTOR_element(transport_state->registered_devices, i);
-
-        prepareDeviceForConnectionRetry(device_state);
-    }
-
-    destroyConnection(transport_state);
-    transport_state->connection_state = AMQP_MANAGEMENT_STATE_IDLE;
-}
-
-static int is_credential_compatible(const IOTHUB_DEVICE_CONFIG* device_config, AMQP_TRANSPORT_CREDENTIAL_TYPE preferred_authentication_type)
-{
-    int result;
-
-    if (preferred_authentication_type == CREDENTIAL_NOT_BUILD)
-    {
-        result = RESULT_OK;
-    }
-    else if (preferred_authentication_type == X509 && (device_config->deviceKey != NULL || device_config->deviceSasToken != NULL))
-    {
-        LogError("Incompatible credentials: transport is using X509 certificate authentication, but device config contains deviceKey and/or sasToken");
-        result = __FAILURE__;
-    }
-    else if (preferred_authentication_type != X509 && (device_config->deviceKey == NULL && device_config->deviceSasToken == NULL))
-    {
-        LogError("Incompatible credentials: transport is using CBS authentication, but device config does not contain deviceKey nor sasToken");
-        result = __FAILURE__;
-    }
-    else
-    {
-        result = RESULT_OK;
+        result = true;
     }
 
     return result;
 }
 
 
-// API functions
+
+//---------- DoWork Helpers ----------//
+
+static IOTHUB_MESSAGE_LIST* get_next_event_to_send(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device)
+{
+	IOTHUB_MESSAGE_LIST* message;
+
+	if (!DList_IsListEmpty(registered_device->waiting_to_send))
+	{
+		PDLIST_ENTRY list_entry = registered_device->waiting_to_send->Flink;
+		message = containingRecord(list_entry, IOTHUB_MESSAGE_LIST, entry);
+		(void)DList_RemoveEntryList(list_entry);
+	}
+	else
+	{
+		message = NULL;
+	}
+
+	return message;
+}
+
+// @brief    "Parses" the D2C_EVENT_SEND_RESULT (from iothubtransport_amqp_device module) into a IOTHUB_CLIENT_CONFIRMATION_RESULT.
+static IOTHUB_CLIENT_CONFIRMATION_RESULT get_iothub_client_confirmation_result_from(D2C_EVENT_SEND_RESULT result)
+{
+	IOTHUB_CLIENT_CONFIRMATION_RESULT iothub_send_result;
+
+	switch (result)
+	{
+		case D2C_EVENT_SEND_COMPLETE_RESULT_OK:
+			iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_OK;
+			break;
+		case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE:
+		case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING:
+			iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_ERROR;
+			break;
+		case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT:
+			iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT;
+			break;
+		case D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED:
+			iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY;
+			break;
+		case D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN:
+		default:
+			iothub_send_result = IOTHUB_CLIENT_CONFIRMATION_ERROR;
+			break;
+	}
+
+	return iothub_send_result;
+}
+
+// @brief
+//     Callback function for device_send_event_async.
+static void on_event_send_complete(IOTHUB_MESSAGE_LIST* message, D2C_EVENT_SEND_RESULT result, void* context)
+{
+	AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)context;
+
+	if (result != D2C_EVENT_SEND_COMPLETE_RESULT_OK && result != D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED)
+	{
+		registered_device->number_of_send_event_complete_failures++;
+	}
+	else
+	{
+		registered_device->number_of_send_event_complete_failures = 0;
+	}
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_056: [If `message->callback` is not NULL, it shall invoked with the `iothub_send_result`]
+	if (message->callback != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_050: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_OK, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_OK]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_051: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_052: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_053: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_054: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_055: [If result is D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN, `iothub_send_result` shall be set using IOTHUB_CLIENT_CONFIRMATION_ERROR]
+		IOTHUB_CLIENT_CONFIRMATION_RESULT iothub_send_result = get_iothub_client_confirmation_result_from(result);
+
+		message->callback(iothub_send_result, message->context);
+	}
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_057: [`message->messageHandle` shall be destroyed using IoTHubMessage_Destroy]
+	IoTHubMessage_Destroy(message->messageHandle);
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_058: [`message` shall be destroyed using free]
+	free(message);
+}
+
+// @brief
+//     Gets events from wait to send list and sends to service in the order they were added.
+// @returns
+//     0 if all events could be sent to the next layer successfully, non-zero otherwise.
+static int send_pending_events(AMQP_TRANSPORT_DEVICE_INSTANCE* device_state)
+{
+	int result;
+	IOTHUB_MESSAGE_LIST* message;
+
+	result = RESULT_OK;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_047: [If the registered device is started, each event on `registered_device->wait_to_send_list` shall be removed from the list and sent using device_send_event_async()]
+	while ((message = get_next_event_to_send(device_state)) != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_048: [device_send_event_async() shall be invoked passing `on_event_send_complete`]
+		if (device_send_event_async(device_state->device_handle, message, on_event_send_complete, device_state) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_049: [If device_send_event_async() fails, `on_event_send_complete` shall be invoked passing EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING and return]
+			LogError("Device '%s' failed to send message (device_send_event_async failed)", STRING_c_str(device_state->device_id));
+			result = __FAILURE__;
+
+			on_event_send_complete(message, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, device_state);
+			break;
+		}
+	}
+
+	return result;
+}
+
+// @brief
+//     Auxiliary function for the public DoWork API, performing DoWork activities (authenticate, messaging) for a specific device.
+// @requires
+//     The transport to have a valid instance of AMQP_CONNECTION (from which to obtain SESSION_HANDLE and CBS_HANDLE)
+// @returns
+//     0 if no errors occur, non-zero otherwise.
+static int IoTHubTransport_AMQP_Common_Device_DoWork(AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device)
+{
+	int result;
+
+	if (registered_device->device_state != DEVICE_STATE_STARTED)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_036: [If the device state is DEVICE_STATE_STOPPED, it shall be started]
+		if (registered_device->device_state == DEVICE_STATE_STOPPED)
+		{
+			SESSION_HANDLE session_handle;
+			CBS_HANDLE cbs_handle = NULL;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_039: [amqp_connection_get_session_handle() shall be invoked on `instance->connection`]
+			if (amqp_connection_get_session_handle(registered_device->transport_instance->amqp_connection, &session_handle) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_040: [If amqp_connection_get_session_handle() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return]
+				LogError("Failed performing DoWork for device '%s' (failed to get the amqp_connection session_handle)", STRING_c_str(registered_device->device_id));
+				result = __FAILURE__;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_037: [If transport is using CBS authentication, amqp_connection_get_cbs_handle() shall be invoked on `instance->connection`]
+			else if (registered_device->transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS &&
+				amqp_connection_get_cbs_handle(registered_device->transport_instance->amqp_connection, &cbs_handle) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_038: [If amqp_connection_get_cbs_handle() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and return]
+				LogError("Failed performing DoWork for device '%s' (failed to get the amqp_connection cbs_handle)", STRING_c_str(registered_device->device_id));
+				result = __FAILURE__;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_041: [The device handle shall be started using device_start_async()]
+			else if (device_start_async(registered_device->device_handle, session_handle, cbs_handle) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_042: [If device_start_async() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and skip to the next registered device]
+				LogError("Failed performing DoWork for device '%s' (failed to start device)", STRING_c_str(registered_device->device_id));
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else if (registered_device->device_state == DEVICE_STATE_STARTING ||
+                 registered_device->device_state == DEVICE_STATE_STOPPING)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_043: [If the device handle is in state DEVICE_STATE_STARTING or DEVICE_STATE_STOPPING, it shall be checked for state change timeout]
+			bool is_timed_out;
+			if (is_timeout_reached(registered_device->time_of_last_state_change, registered_device->max_state_change_timeout_secs, &is_timed_out) != RESULT_OK)
+			{
+				LogError("Failed performing DoWork for device '%s' (failed tracking timeout of device %d state)", STRING_c_str(registered_device->device_id), registered_device->device_state);
+				registered_device->device_state = DEVICE_STATE_ERROR_AUTH; // if time could not be calculated, the worst must be assumed.
+				result = __FAILURE__;
+			}
+			else if (is_timed_out)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_044: [If the device times out in state DEVICE_STATE_STARTING or DEVICE_STATE_STOPPING, the registered device shall be marked with failure]
+				LogError("Failed performing DoWork for device '%s' (device failed to start or stop within expected timeout)", STRING_c_str(registered_device->device_id));
+				registered_device->device_state = DEVICE_STATE_ERROR_AUTH; // this will cause device to be stopped bellow on the next call to this function.
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else // i.e., DEVICE_STATE_ERROR_AUTH || DEVICE_STATE_ERROR_AUTH_TIMEOUT || DEVICE_STATE_ERROR_MSG
+		{
+			LogError("Failed performing DoWork for device '%s' (device reported state %d; number of previous failures: %d)", 
+				STRING_c_str(registered_device->device_id), registered_device->device_state, registered_device->number_of_previous_failures);
+
+			registered_device->number_of_previous_failures++;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_046: [If the device has failed for MAX_NUMBER_OF_DEVICE_FAILURES in a row, it shall trigger a connection retry on the transport]
+			if (registered_device->number_of_previous_failures >= MAX_NUMBER_OF_DEVICE_FAILURES)
+			{
+				result = __FAILURE__;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_045: [If the registered device has a failure, it shall be stopped using device_stop()]
+			else if (device_stop(registered_device->device_handle) != RESULT_OK)
+			{
+				LogError("Failed to stop reset device '%s' (device_stop failed)", STRING_c_str(registered_device->device_id));
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+	}
+#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_031: [ Once the device is authenticated, `iothubtransportamqp_methods_subscribe` shall be invoked (subsequent DoWork calls shall not call it if already subscribed). ]
+	else if (registered_device->subscribe_methods_needed &&
+		!registered_device->subscribed_for_methods &&
+		subscribe_methods(registered_device) != RESULT_OK)
+	{
+		LogError("Failed performing DoWork for device '%s' (failed registering for device methods)", STRING_c_str(registered_device->device_id));
+		registered_device->number_of_previous_failures++;
+		result = __FAILURE__;
+	}
+#endif
+	else
+	{
+		if (send_pending_events(registered_device) != RESULT_OK)
+		{
+			LogError("Failed performing DoWork for device '%s' (failed sending pending events)", STRING_c_str(registered_device->device_id));
+			registered_device->number_of_previous_failures++;
+			result = __FAILURE__;
+		}
+		else
+		{
+			registered_device->number_of_previous_failures = 0;
+			result = RESULT_OK;
+		}
+	}
+
+	// No harm in invoking this as API will simply exit if the state is not "started".
+	device_do_work(registered_device->device_handle); 
+
+	return result;
+}
+
+
+//---------- SetOption-ish Helpers ----------//
+
+// @brief
+//     Gets all the device-specific options and replicates them into this new registered device.
+// @returns
+//     0 if the function succeeds, non-zero otherwise.
+static int replicate_device_options_to(AMQP_TRANSPORT_DEVICE_INSTANCE* dev_instance, DEVICE_AUTH_MODE auth_mode)
+{
+	int result;
+
+	if (device_set_option(
+		dev_instance->device_handle,
+		DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS,
+		&dev_instance->transport_instance->option_send_event_timeout_secs) != RESULT_OK)
+	{
+		LogError("Failed to apply option DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id));
+		result = __FAILURE__;
+	}
+	else if (auth_mode == DEVICE_AUTH_MODE_CBS)
+	{
+		if (device_set_option(
+			dev_instance->device_handle,
+			DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS,
+			&dev_instance->transport_instance->option_cbs_request_timeout_secs) != RESULT_OK)
+		{
+			LogError("Failed to apply option DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id));
+			result = __FAILURE__;
+		}
+		else if (device_set_option(
+			dev_instance->device_handle,
+			DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS,
+			&dev_instance->transport_instance->option_sas_token_lifetime_secs) != RESULT_OK)
+		{
+			LogError("Failed to apply option DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id));
+			result = __FAILURE__;
+		}
+		else if (device_set_option(
+			dev_instance->device_handle,
+			DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS,
+			&dev_instance->transport_instance->option_sas_token_refresh_time_secs) != RESULT_OK)
+		{
+			LogError("Failed to apply option DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS to device '%s' (device_set_option failed)", STRING_c_str(dev_instance->device_id));
+			result = __FAILURE__;
+		}
+		else
+		{
+			result = RESULT_OK;
+		}
+	}
+	else
+	{
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+// @brief
+//     Translates from the option names supported by iothubtransport_amqp_common to the ones supported by iothubtransport_amqp_device.
+static const char* get_device_option_name_from(const char* iothubclient_option_name)
+{
+	const char* device_option_name;
+
+	if (strcmp(OPTION_SAS_TOKEN_LIFETIME, iothubclient_option_name) == 0)
+	{
+		device_option_name = DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS;
+	}
+	else if (strcmp(OPTION_SAS_TOKEN_REFRESH_TIME, iothubclient_option_name) == 0)
+	{
+		device_option_name = DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS;
+	}
+	else if (strcmp(OPTION_CBS_REQUEST_TIMEOUT, iothubclient_option_name) == 0)
+	{
+		device_option_name = DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS;
+	}
+	else if (strcmp(OPTION_EVENT_SEND_TIMEOUT_SECS, iothubclient_option_name) == 0)
+	{
+		device_option_name = DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS;
+	}
+	else
+	{
+		device_option_name = NULL;
+	}
+
+	return device_option_name;
+}
+
+// @brief
+//     Auxiliary function invoked by IoTHubTransport_AMQP_Common_SetOption to set an option on every registered device.
+// @returns
+//     0 if it succeeds, non-zero otherwise.
+static int IoTHubTransport_AMQP_Common_Device_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, void* value)
+{
+	int result;
+	const char* device_option;
+
+	if ((device_option = get_device_option_name_from(option)) == NULL)
+	{
+		LogError("failed setting option '%s' to registered device (could not match name to options supported by device)", option);
+		result = __FAILURE__;
+	}
+	else
+	{
+		AMQP_TRANSPORT_INSTANCE* instance = (AMQP_TRANSPORT_INSTANCE*)handle;
+		result = RESULT_OK;
+
+		LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->registered_devices);
+
+		while (list_item != NULL)
+		{
+			AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device;
+
+			if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL)
+			{
+				LogError("failed setting option '%s' to registered device (singlylinkedlist_item_get_value failed)", option);
+				result = __FAILURE__;
+				break;
+			}
+			else if (device_set_option(registered_device->device_handle, device_option, value) != RESULT_OK)
+			{
+				LogError("failed setting option '%s' to registered device '%s' (device_set_option failed)",
+					option, STRING_c_str(registered_device->device_id));
+				result = __FAILURE__;
+				break;
+			}
+
+			list_item = singlylinkedlist_get_next_item(list_item);
+		}
+	}
+
+	return result;
+}
+
+static void internal_destroy_instance(AMQP_TRANSPORT_INSTANCE* instance)
+{
+	if (instance != NULL)
+	{
+		if (instance->registered_devices != NULL)
+		{
+			LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->registered_devices);
+
+			while (list_item != NULL)
+			{
+				AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item);
+				list_item = singlylinkedlist_get_next_item(list_item);
+				IoTHubTransport_AMQP_Common_Unregister(registered_device);
+			}
+
+			singlylinkedlist_destroy(instance->registered_devices);
+		}
+
+		if (instance->amqp_connection != NULL)
+		{
+			amqp_connection_destroy(instance->amqp_connection);
+		}
+
+		destroy_underlying_io_transport(instance);
+		destroy_underlying_io_transport_options(instance);
+
+		STRING_delete(instance->iothub_host_fqdn);
+
+		free(instance);
+	}
+}
+
+
+// ---------- API functions ---------- //
 
 TRANSPORT_LL_HANDLE IoTHubTransport_AMQP_Common_Create(const IOTHUBTRANSPORT_CONFIG* config, AMQP_GET_IO_TRANSPORT get_io_transport)
 {
-    AMQP_TRANSPORT_INSTANCE* transport_state = NULL;
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_005: [If parameter config or config->upperConfig are NULL then IoTHubTransport_AMQP_Common_Create shall fail and return NULL.] 
-    if (config == NULL || config->upperConfig == NULL)
-    {
-        LogError("IoTHub AMQP client transport null configuration parameter.");
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_006: [IoTHubTransport_AMQP_Common_Create shall fail and return NULL if any fields of the config->upperConfig structure are NULL.]
-    else if (config->upperConfig->protocol == NULL)
-    {
-        LogError("Invalid configuration (NULL protocol detected)");
-    }
-    else if (config->upperConfig->iotHubName == NULL)
-    {
-        LogError("Invalid configuration (NULL iotHubName detected)");
-    }
-    else if (config->upperConfig->iotHubSuffix == NULL)
-    {
-        LogError("Invalid configuration (NULL iotHubSuffix detected)");
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_134: [IoTHubTransport_AMQP_Common_Create shall fail and return NULL if the combined length of config->iotHubName and config->iotHubSuffix exceeds 254 bytes (RFC1035)]
-    else if ((strlen(config->upperConfig->iotHubName) + strlen(config->upperConfig->iotHubSuffix)) > (RFC1035_MAX_FQDN_LENGTH - 1))
-    {
-        LogError("The lengths of iotHubName and iotHubSuffix together exceed the maximum FQDN length allowed (RFC 1035)");
-    }
-    // Codes_SRS_IOTHUBTRANSPORTAMQP_09_254: [If parameter `get_io_transport` is NULL then IoTHubTransport_AMQP_Common_Create shall fail and return NULL.]
-    else if (get_io_transport == NULL)
-    {
-        LogError("Invalid configuration (get_io_transport is NULL)");
-    }
-    else
-    {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_009: [IoTHubTransport_AMQP_Common_Create shall fail and return NULL if memory allocation of the transport's internal state structure fails.]
-        if ((transport_state = (AMQP_TRANSPORT_INSTANCE*)malloc(sizeof(AMQP_TRANSPORT_INSTANCE))) == NULL)
-        {
-            LogError("Could not allocate AMQP transport state");
-        }
-        else
-        {
-            bool cleanup_required = false;
+	TRANSPORT_LL_HANDLE result;
 
-            transport_state->iotHubHostFqdn = NULL;
-            transport_state->connection = NULL;
-            transport_state->connection_state = AMQP_MANAGEMENT_STATE_IDLE;
-            transport_state->session = NULL;
-            transport_state->tls_io = NULL;
-            transport_state->underlying_io_transport_provider = get_io_transport;
-            transport_state->is_trace_on = false;
-
-            transport_state->cbs_connection.cbs_handle = NULL;
-            transport_state->cbs_connection.sasl_io = NULL;
-            transport_state->cbs_connection.sasl_mechanism = NULL;
-
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_020: [IoTHubTransport_AMQP_Common_Create shall set parameter device_state->sas_token_lifetime with the default value of 3600000 (milliseconds).]
-            transport_state->cbs_connection.sas_token_lifetime = DEFAULT_SAS_TOKEN_LIFETIME_MS;
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_128: [IoTHubTransport_AMQP_Common_Create shall set parameter device_state->sas_token_refresh_time with the default value of sas_token_lifetime/2 (milliseconds).] 
-            transport_state->cbs_connection.sas_token_refresh_time = transport_state->cbs_connection.sas_token_lifetime / 2;
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_129 : [IoTHubTransport_AMQP_Common_Create shall set parameter device_state->cbs_request_timeout with the default value of 30000 (milliseconds).]
-            transport_state->cbs_connection.cbs_request_timeout = DEFAULT_CBS_REQUEST_TIMEOUT_MS;
-
-            transport_state->preferred_credential_type = CREDENTIAL_NOT_BUILD;
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_001: [If `config` or `config->upperConfig` or `get_io_transport` are NULL then IoTHubTransport_AMQP_Common_Create shall fail and return NULL.]
+	if (config == NULL || config->upperConfig == NULL || get_io_transport == NULL)
+    {
+        LogError("IoTHub AMQP client transport null configuration parameter (config=%p, get_io_transport=%p).", config, get_io_transport);
+		result = NULL;
+    }
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_002: [IoTHubTransport_AMQP_Common_Create shall fail and return NULL if `config->upperConfig->protocol` is NULL]
+	else if (config->upperConfig->protocol == NULL)
+    {
+        LogError("Failed to create the AMQP transport common instance (NULL parameter received: protocol=%p, iotHubName=%p, iotHubSuffix=%p)",
+			config->upperConfig->protocol, config->upperConfig->iotHubName, config->upperConfig->iotHubSuffix);
+		result = NULL;
+	}
+	else
+	{
+		AMQP_TRANSPORT_INSTANCE* instance;
 
-            transport_state->xioOptions = NULL; 
-            transport_state->link_count = 0;
-
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_010: [If config->upperConfig->protocolGatewayHostName is NULL, IoTHubTransport_AMQP_Common_Create shall create an immutable string, referred to as iotHubHostFqdn, from the following pieces: config->iotHubName + "." + config->iotHubSuffix.] 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransport_AMQP_Common_Create shall use it as iotHubHostFqdn]
-            if ((transport_state->iotHubHostFqdn = (config->upperConfig->protocolGatewayHostName != NULL ? STRING_construct(config->upperConfig->protocolGatewayHostName) : concat3Params(config->upperConfig->iotHubName, ".", config->upperConfig->iotHubSuffix))) == NULL)
-            {
-                LogError("Failed to set transport_state->iotHubHostFqdn.");
-                cleanup_required = true;
-            }
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_218: [IoTHubTransport_AMQP_Common_Create shall initialize the transport state registered device list with a VECTOR instance.]
-            else if ((transport_state->registered_devices = VECTOR_create(sizeof(IOTHUB_DEVICE_HANDLE))) == NULL)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_219: [If VECTOR_create fails, IoTHubTransport_AMQP_Common_Create shall fail and return.]
-                LogError("Failed to initialize the internal list of registered devices");
-                cleanup_required = true;
-            }
-
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_236: [If IoTHubTransport_AMQP_Common_Create fails it shall free any memory it allocated (iotHubHostFqdn, transport state).]
-            if (cleanup_required)
-            {
-                if (transport_state->iotHubHostFqdn != NULL)
-                    STRING_delete(transport_state->iotHubHostFqdn);
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_003: [Memory shall be allocated for the transport's internal state structure (`instance`)]
+		if ((instance = (AMQP_TRANSPORT_INSTANCE*)malloc(sizeof(AMQP_TRANSPORT_INSTANCE))) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_004: [If malloc() fails, IoTHubTransport_AMQP_Common_Create shall fail and return NULL]
+			LogError("Could not allocate AMQP transport state (malloc failed)");
+			result = NULL;
+		}
+		else
+		{
+			memset(instance, 0, sizeof(AMQP_TRANSPORT_INSTANCE));
 
-                free(transport_state);
-                transport_state = NULL;
-            }
-        }
-    }
-
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_023: [If IoTHubTransport_AMQP_Common_Create succeeds it shall return a non-NULL pointer to the structure that represents the transport.] 
-    return transport_state;
-}
-
-static RESULT device_DoWork(AMQP_TRANSPORT_DEVICE_STATE* device_state)
-{
-    RESULT result = RESULT_SUCCESS;
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_243: [IoTHubTransport_AMQP_Common_DoWork shall retrieve the authenticatication status of the device using deviceauthentication_get_status()]
-    AUTHENTICATION_STATUS auth_status = authentication_get_status(device_state->authentication);
-
-    switch (auth_status)
-    {
-        case AUTHENTICATION_STATUS_IDLE:
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_243: [If the device authentication status is AUTHENTICATION_STATUS_IDLE, IoTHubTransport_AMQP_Common_DoWork shall authenticate it using authentication_authenticate()]
-            if (authentication_authenticate(device_state->authentication) != RESULT_OK)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_146: [If authentication_authenticate() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and process the next device]
-                LogError("Failed authenticating AMQP connection [%s]", STRING_c_str(device_state->deviceId));
-                result = RESULT_RETRYABLE_ERROR;
-            }
-            break;
-        case AUTHENTICATION_STATUS_REFRESH_REQUIRED:
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_081: [If the device authentication status is AUTHENTICATION_STATUS_REFRESH_REQUIRED, IoTHubTransport_AMQP_Common_DoWork shall refresh it using authentication_refresh()]
-            if (authentication_refresh(device_state->authentication) != RESULT_OK)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_082: [**If authentication_refresh() fails, IoTHubTransport_AMQP_Common_DoWork shall fail and process the next device]
-                LogError("AMQP transport failed to refresh authentication [%s]", STRING_c_str(device_state->deviceId));
-                result = RESULT_RETRYABLE_ERROR;
-            }
-            break;
-        case AUTHENTICATION_STATUS_OK:
-#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-            /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_031: [ `iothubtransportamqp_methods_subscribe` shall only be called once (subsequent DoWork calls shall not call it if already subscribed). ]*/
-            if ((device_state->subscribe_methods_needed) &&
-                (subscribe_methods(device_state) != 0))
-            {
-                LogError("Failed subscribing for methods");
-            }
-#endif
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_005: [If `config->upperConfig->protocolGatewayHostName` is NULL, `instance->iothub_target_fqdn` shall be set as `config->upperConfig->iotHubName` + "." + `config->upperConfig->iotHubSuffix`]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_006: [If `config->upperConfig->protocolGatewayHostName` is not NULL, `instance->iothub_target_fqdn` shall be set with a copy of it]
+			if ((instance->iothub_host_fqdn = get_target_iothub_fqdn(config)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_007: [If `instance->iothub_target_fqdn` fails to be set, IoTHubTransport_AMQP_Common_Create shall fail and return NULL]
+				LogError("Failed to obtain the iothub target fqdn.");
+				result = NULL;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_008: [`instance->registered_devices` shall be set using singlylinkedlist_create()]
+			else if ((instance->registered_devices = singlylinkedlist_create()) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_009: [If singlylinkedlist_create() fails, IoTHubTransport_AMQP_Common_Create shall fail and return NULL]
+				LogError("Failed to initialize the internal list of registered devices (singlylinkedlist_create failed)");
+				result = NULL;
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_010: [`get_io_transport` shall be saved on `instance->underlying_io_transport_provider`]
+				instance->underlying_io_transport_provider = get_io_transport;
+				instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET;
+				instance->option_sas_token_lifetime_secs = DEFAULT_SAS_TOKEN_LIFETIME_SECS;
+				instance->option_sas_token_refresh_time_secs = DEFAULT_SAS_TOKEN_REFRESH_TIME_SECS;
+				instance->option_cbs_request_timeout_secs = DEFAULT_CBS_REQUEST_TIMEOUT_SECS;
+				instance->option_send_event_timeout_secs = DEFAULT_EVENT_SEND_TIMEOUT_SECS;
+				
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_012: [If IoTHubTransport_AMQP_Common_Create succeeds it shall return a pointer to `instance`.]
+				result = (TRANSPORT_LL_HANDLE)instance;
+			}
 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_145: [If the device authentication status is AUTHENTICATION_STATUS_OK, IoTHubTransport_AMQP_Common_DoWork shall proceed to sending events, registering for messages]
-
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_121: [IoTHubTransport_AMQP_Common_DoWork shall create an AMQP message_receiver if transport_state->message_receive is NULL and transport_state->receive_messages is true] 
-            if (device_state->receive_messages == true &&
-                device_state->message_receiver == NULL &&
-                createMessageReceiver(device_state) != RESULT_OK)
-            {
-                LogError("Failed creating AMQP transport message receiver [%s]", STRING_c_str(device_state->deviceId));
-                result = RESULT_CRITICAL_ERROR;
-            }
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_122: [IoTHubTransport_AMQP_Common_DoWork shall destroy the transport_state->message_receiver (and set it to NULL) if it exists and transport_state->receive_messages is false] 
-            else if (device_state->receive_messages == false &&
-                device_state->message_receiver != NULL &&
-                destroyMessageReceiver(device_state) != RESULT_OK)
-            {
-                LogError("Failed destroying AMQP transport message receiver [%s]", STRING_c_str(device_state->deviceId));
-            }
-
-            if (device_state->message_sender == NULL &&
-                createEventSender(device_state) != RESULT_OK)
-            {
-                LogError("Failed creating AMQP transport event sender [%s]", STRING_c_str(device_state->deviceId));
-                result = RESULT_CRITICAL_ERROR;
-            }
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_245: [IoTHubTransport_AMQP_Common_DoWork shall skip sending events if the state of the message_sender is not MESSAGE_SENDER_STATE_OPEN]
-            else if (device_state->message_sender_state == MESSAGE_SENDER_STATE_OPEN &&
-                sendPendingEvents(device_state) != RESULT_OK)
-            {
-                LogError("AMQP transport failed sending events [%s]", STRING_c_str(device_state->deviceId));
-                result = RESULT_CRITICAL_ERROR;
-            }
-            break;
-        case AUTHENTICATION_STATUS_FAILURE:
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_083: [If the device authentication status is AUTHENTICATION_STATUS_FAILURE, IoTHubTransport_AMQP_Common_DoWork shall fail and process the next device]
-            LogError("Authentication failed [%s]", STRING_c_str(device_state->deviceId));
-            result = RESULT_CRITICAL_ERROR;
-            break;
-        case AUTHENTICATION_STATUS_TIMEOUT:
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_084: [If the device authentication status is AUTHENTICATION_STATUS_TIMEOUT, IoTHubTransport_AMQP_Common_DoWork shall fail and process the next device]
-            LogError("Authentication timed-out [%s]", STRING_c_str(device_state->deviceId));
-            result = RESULT_CRITICAL_ERROR;
-            break;
-        default:
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_244: [If the device authentication status is AUTHENTICATION_STATUS_IN_PROGRESS, IoTHubTransport_AMQP_Common_DoWork shall skip and process the next device]
-            break;
-    }
+			if (result == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_011: [If IoTHubTransport_AMQP_Common_Create fails it shall free any memory it allocated]
+				internal_destroy_instance(instance);
+			}
+		}
+	}
 
     return result;
 }
@@ -1297,60 +1211,75 @@
 
 void IoTHubTransport_AMQP_Common_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle)
 {
-    (void)iotHubClientHandle;
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_051: [IoTHubTransport_AMQP_Common_DoWork shall fail and return immediately if the transport handle parameter is NULL] 
+    (void)iotHubClientHandle; // unused as of now.
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_016: [If `handle` is NULL, IoTHubTransport_AMQP_Common_DoWork shall return without doing any work]
     if (handle == NULL)
     {
         LogError("IoTHubClient DoWork failed: transport handle parameter is NULL.");
     } 
     else
     {
-        bool trigger_connection_retry = false;
-        AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)handle;
-        size_t number_of_registered_devices = VECTOR_size(transport_state->registered_devices);
+        AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle;
+		LIST_ITEM_HANDLE list_item;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_017: [If `instance->is_connection_retry_required` is true, IoTHubTransport_AMQP_Common_DoWork shall trigger the connection-retry logic and return]
+		if (transport_instance->is_connection_retry_required)
+		{
+			LogError("An error occured on AMQP connection. The connection will be restablished.");
+
+			prepare_for_connection_retry(transport_instance);
+
+			transport_instance->is_connection_retry_required = false;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_018: [If there are no devices registered on the transport, IoTHubTransport_AMQP_Common_DoWork shall skip do_work for devices]
+		else if ((list_item = singlylinkedlist_get_head_item(transport_instance->registered_devices)) != NULL)
+		{
+			// We need to check if there are devices, otherwise the amqp_connection won't be able to be created since
+			// there is not a preferred authentication mode set yet on the transport.
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_237: [IoTHubTransport_AMQP_Common_DoWork shall return immediately if there are no devices registered on the transport]
-        if (number_of_registered_devices > 0)
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_019: [If `instance->amqp_connection` is NULL, it shall be established]
+			if (transport_instance->amqp_connection == NULL && establish_amqp_connection(transport_instance) != RESULT_OK)
+			{
+				LogError("AMQP transport failed to establish connection with service.");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_020: [If the amqp_connection is OPENED, the transport shall iterate through each registered device and perform a device-specific do_work on each]
+			else if (transport_instance->amqp_connection_state == AMQP_CONNECTION_STATE_OPENED)
+			{
+				while (list_item != NULL)
+				{
+					AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device;
+
+					if ((registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)singlylinkedlist_item_get_value(list_item)) == NULL)
+					{
+						LogError("Transport had an unexpected failure during DoWork (failed to fetch a registered_devices list item value)");
+					}
+					else if (registered_device->number_of_send_event_complete_failures >= MAX_NUMBER_OF_DEVICE_FAILURES)
+					{
+						LogError("Device '%s' reported a critical failure (events completed sending with failures); connection retry will be triggered.", STRING_c_str(registered_device->device_id));
+
+						transport_instance->is_connection_retry_required = true;
+					}
+					else if (IoTHubTransport_AMQP_Common_Device_DoWork(registered_device) != RESULT_OK)
+					{
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_021: [If DoWork fails for the registered device for more than MAX_NUMBER_OF_DEVICE_FAILURES, connection retry shall be triggered]
+						if (registered_device->number_of_previous_failures >= MAX_NUMBER_OF_DEVICE_FAILURES)
+						{
+							LogError("Device '%s' reported a critical failure; connection retry will be triggered.", STRING_c_str(registered_device->device_id));
+
+							transport_instance->is_connection_retry_required = true;
+						}
+					}
+
+					list_item = singlylinkedlist_get_next_item(list_item);
+				}
+			}
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_022: [If `instance->amqp_connection` is not NULL, amqp_connection_do_work shall be invoked]
+		if (transport_instance->amqp_connection != NULL)
         {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_238: [If the transport state has a faulty connection state, IoTHubTransport_AMQP_Common_DoWork shall trigger the connection-retry logic]
-            if (transport_state->connection != NULL &&
-                transport_state->connection_state == AMQP_MANAGEMENT_STATE_ERROR)
-            {
-                LogError("An error occured on AMQP connection. The connection will be restablished.");
-                trigger_connection_retry = true;
-            }
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_055: [If the transport handle has a NULL connection, IoTHubTransport_AMQP_Common_DoWork shall instantiate and initialize the AMQP components and establish the connection] 
-            else if (transport_state->connection == NULL &&
-                establishConnection(transport_state) != RESULT_OK)
-            {
-                LogError("AMQP transport failed to establish connection with service.");
-                trigger_connection_retry = true;
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_241: [IoTHubTransport_AMQP_Common_DoWork shall iterate through all its registered devices to process authentication, events to be sent, messages to be received]
-                for (size_t i = 0; i < number_of_registered_devices; i++)
-                {
-                    AMQP_TRANSPORT_DEVICE_STATE* device_state = *(AMQP_TRANSPORT_DEVICE_STATE**)VECTOR_element(transport_state->registered_devices, i);
-
-                    RESULT actionable_result = device_DoWork(device_state);
-
-                    if (actionable_result == RESULT_CRITICAL_ERROR)
-                    {
-                        trigger_connection_retry = true;
-                    }
-                }
-            }
-
-            if (trigger_connection_retry)
-            {
-                prepareForConnectionRetry(transport_state);
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_103: [IoTHubTransport_AMQP_Common_DoWork shall invoke connection_dowork() on AMQP for triggering sending and receiving messages] 
-                connection_dowork(transport_state->connection);
-            }
+			amqp_connection_do_work(transport_instance->amqp_connection);
         }
     }
 }
@@ -1359,7 +1288,7 @@
 {
     int result;
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_037: [IoTHubTransport_AMQP_Common_Subscribe shall fail if the transport handle parameter received is NULL.] 
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_084: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result]
     if (handle == NULL)
     {
         LogError("Invalid handle to IoTHubClient AMQP transport device handle.");
@@ -1367,10 +1296,26 @@
     }
     else
     {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_038: [IoTHubTransport_AMQP_Common_Subscribe shall set transport_handle->receive_messages to true and return success code.]
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
-        device_state->receive_messages = true;
-        result = 0;
+        AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_085: [If `amqp_device_instance` is not registered, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result]
+		if (!is_device_registered(amqp_device_instance))
+		{
+			LogError("Device '%s' failed subscribing to cloud-to-device messages (device is not registered)", STRING_c_str(amqp_device_instance->device_id));
+			result = __FAILURE__;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_086: [device_subscribe_message() shall be invoked passing `on_message_received_callback`]
+		else if (device_subscribe_message(amqp_device_instance->device_handle, on_message_received, amqp_device_instance) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_087: [If device_subscribe_message() fails, IoTHubTransport_AMQP_Common_Subscribe shall return a non-zero result]
+			LogError("Device '%s' failed subscribing to cloud-to-device messages (device_subscribe_message failed)", STRING_c_str(amqp_device_instance->device_id));
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_088: [If no failures occur, IoTHubTransport_AMQP_Common_Subscribe shall return 0]
+			result = RESULT_OK;
+		}
     }
 
     return result;
@@ -1378,16 +1323,25 @@
 
 void IoTHubTransport_AMQP_Common_Unsubscribe(IOTHUB_DEVICE_HANDLE handle)
 {
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_039: [IoTHubTransport_AMQP_Common_Unsubscribe shall fail if the transport handle parameter received is NULL.] 
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_093: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Subscribe shall return]
     if (handle == NULL)
     {
         LogError("Invalid handle to IoTHubClient AMQP transport device handle.");
     }
     else
     {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_040: [IoTHubTransport_AMQP_Common_Unsubscribe shall set transport_handle->receive_messages to false.]
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
-        device_state->receive_messages = false;
+        AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_094: [If `amqp_device_instance` is not registered, IoTHubTransport_AMQP_Common_Subscribe shall return]
+		if (!is_device_registered(amqp_device_instance))
+		{
+			LogError("Device '%s' failed unsubscribing to cloud-to-device messages (device is not registered)", STRING_c_str(amqp_device_instance->device_id));
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_095: [device_unsubscribe_message() shall be invoked passing `amqp_device_instance->device_handle`]
+		else if (device_unsubscribe_message(amqp_device_instance->device_handle) != RESULT_OK)
+		{
+			LogError("Device '%s' failed unsubscribing to cloud-to-device messages (device_unsubscribe_message failed)", STRING_c_str(amqp_device_instance->device_id));
+		}
     }
 }
 
@@ -1420,7 +1374,7 @@
     else
     {
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
+        AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
         /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_026: [ `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` shall remember that a subscribe is to be performed in the next call to DoWork and on success it shall return 0. ]*/
         /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_005: [ If the transport is already subscribed to receive C2D method requests, `IoTHubTransport_AMQP_Common_Subscribe_DeviceMethod` shall perform no additional action and return 0. ]*/
         device_state->subscribe_methods_needed = true;
@@ -1445,14 +1399,15 @@
     else
     {
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
+        AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
 
         /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_008: [ If the transport is not subscribed to receive C2D method requests then `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod` shall do nothing. ]*/
         if (device_state->subscribe_methods_needed)
         {
             /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_007: [ `IoTHubTransport_AMQP_Common_Unsubscribe_DeviceMethod` shall unsubscribe from receiving C2D method requests by calling `iothubtransportamqp_methods_unsubscribe`. ]*/
-            device_state->subscribed_for_methods = false;
-            iothubtransportamqp_methods_unsubscribe(device_state->methods_handle);
+			device_state->subscribed_for_methods = false;
+			device_state->subscribe_methods_needed = false;
+			iothubtransportamqp_methods_unsubscribe(device_state->methods_handle);
         }
 #else
         LogError("Not implemented");
@@ -1467,7 +1422,7 @@
     (void)status_response;
     (void)methodId;
     int result;
-    AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
+    AMQP_TRANSPORT_DEVICE_INSTANCE* device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
     if (device_state != NULL)
     {
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
@@ -1499,34 +1454,40 @@
 {
     IOTHUB_CLIENT_RESULT result;
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_041: [IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter.]
-    if (handle == NULL)
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_096: [If `handle` or `iotHubClientStatus` are NULL, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG]
+	if (handle == NULL || iotHubClientStatus == NULL)
     {
         result = IOTHUB_CLIENT_INVALID_ARG;
-        LogError("Invalid handle to IoTHubClient AMQP transport instance.");
-    }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_041: [IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter.]
-    else if (iotHubClientStatus == NULL)
-    {
-        result = IOTHUB_CLIENT_INVALID_ARG;
-        LogError("Invalid pointer to output parameter IOTHUB_CLIENT_STATUS.");
+        LogError("Failed retrieving the device send status (either handle (%p) or iotHubClientStatus (%p) are NULL)", handle, iotHubClientStatus);
     }
     else
     {
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)handle;
+        AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_state = (AMQP_TRANSPORT_DEVICE_INSTANCE*)handle;
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_043: [IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent.]
-        if (!DList_IsListEmpty(device_state->waitingToSend) || !DList_IsListEmpty(&(device_state->inProgress)))
-        {
-            *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY;
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_042: [IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent.] 
-        else
-        {
-            *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE;
-        }
+		DEVICE_SEND_STATUS device_send_status;
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_097: [IoTHubTransport_AMQP_Common_GetSendStatus shall invoke device_get_send_status()]
+		if (device_get_send_status(amqp_device_state->device_handle, &device_send_status) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_098: [If device_get_send_status() fails, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_ERROR]
+			LogError("Failed retrieving the device send status (device_get_send_status failed)");
+			result = IOTHUB_CLIENT_ERROR;
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_099: [If device_get_send_status() returns DEVICE_SEND_STATUS_BUSY, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY]
+			if (device_send_status == DEVICE_SEND_STATUS_BUSY)
+			{
+				*iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_100: [If device_get_send_status() returns DEVICE_SEND_STATUS_IDLE, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE]
+			else // DEVICE_SEND_STATUS_IDLE
+			{
+				*iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE;
+			}
 
-        result = IOTHUB_CLIENT_OK;
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_109: [If no failures occur, IoTHubTransport_AMQP_Common_GetSendStatus shall return IOTHUB_CLIENT_OK]
+			result = IOTHUB_CLIENT_OK;
+		}
     }
 
     return result;
@@ -1536,137 +1497,122 @@
 {
     IOTHUB_CLIENT_RESULT result;
 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_044: [If handle parameter is NULL then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.]
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_045: [If parameter optionName is NULL then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_046: [If parameter value is NULL then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.]
-    if (
-        (handle == NULL) ||
-        (option == NULL) ||
-        (value == NULL)
-        )
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_101: [If `handle`, `option` or `value` are NULL then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.]
+    if ((handle == NULL) || (option == NULL) || (value == NULL))
     {
-        result = IOTHUB_CLIENT_INVALID_ARG;
-        LogError("Invalid parameter (NULL) passed to AMQP transport SetOption()");
+		LogError("Invalid parameter (NULL) passed to AMQP transport SetOption (handle=%p, options=%p, value=%p)", handle, option, value);
+		result = IOTHUB_CLIENT_INVALID_ARG;
     }
     else
     {
-        AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)handle;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_048: [IotHubTransportAMQP_SetOption shall save and apply the value if the option name is "sas_token_lifetime", returning IOTHUB_CLIENT_OK] 
-        if (strcmp(OPTION_SAS_TOKEN_LIFETIME, option) == 0)
-        {
-            transport_state->cbs_connection.sas_token_lifetime = *((size_t*)value);
-            result = IOTHUB_CLIENT_OK;
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_049: [IotHubTransportAMQP_SetOption shall save and apply the value if the option name is "sas_token_refresh_time", returning IOTHUB_CLIENT_OK] 
-        else if (strcmp(OPTION_SAS_TOKEN_REFRESH_TIME, option) == 0)
-        {
-            transport_state->cbs_connection.sas_token_refresh_time = *((size_t*)value);
-            result = IOTHUB_CLIENT_OK;
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_148: [IotHubTransportAMQP_SetOption shall save and apply the value if the option name is "cbs_request_timeout", returning IOTHUB_CLIENT_OK] 
-        else if (strcmp(OPTION_CBS_REQUEST_TIMEOUT, option) == 0)
-        {
-            transport_state->cbs_connection.cbs_request_timeout = *((size_t*)value);
-            result = IOTHUB_CLIENT_OK;
-        }
-        else if (strcmp(OPTION_LOG_TRACE, option) == 0)
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_198: [If `optionName` is `logtrace`, IoTHubTransport_AMQP_Common_SetOption shall save the value on the transport instance.]
-            transport_state->is_trace_on = *((bool*)value);
+        AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle;
+		bool is_device_specific_option;
 
-            if (transport_state->connection != NULL)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_202: [If `optionName` is `logtrace`, IoTHubTransport_AMQP_Common_SetOption shall apply it using connection_set_trace() to current connection instance if it exists and return IOTHUB_CLIENT_OK.]
-                connection_set_trace(transport_state->connection, transport_state->is_trace_on);
-            }
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_102: [If `option` is a device-specific option, it shall be saved and applied to each registered device using device_set_option()]
+		if (strcmp(OPTION_SAS_TOKEN_LIFETIME, option) == 0)
+		{
+			is_device_specific_option = true;
+			transport_instance->option_sas_token_lifetime_secs = *(size_t*)value;
+		}
+		else if (strcmp(OPTION_SAS_TOKEN_REFRESH_TIME, option) == 0)
+		{
+			is_device_specific_option = true;
+			transport_instance->option_sas_token_refresh_time_secs = *(size_t*)value;
+		}
+		else if (strcmp(OPTION_CBS_REQUEST_TIMEOUT, option) == 0)
+		{
+			is_device_specific_option = true;
+			transport_instance->option_cbs_request_timeout_secs = *(size_t*)value;
+		}
+		else if (strcmp(OPTION_EVENT_SEND_TIMEOUT_SECS, option) == 0)
+		{
+			is_device_specific_option = true;
+			transport_instance->option_send_event_timeout_secs = *(size_t*)value;
+		}
+		else
+		{
+			is_device_specific_option = false;
+		}
 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_203: [If `optionName` is `logtrace`, IoTHubTransport_AMQP_Common_SetOption shall apply it using xio_setoption() to current SASL IO instance if it exists.]
-            if (transport_state->cbs_connection.sasl_io != NULL &&
-                xio_setoption(transport_state->cbs_connection.sasl_io, OPTION_LOG_TRACE, &transport_state->is_trace_on) != RESULT_OK)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_204: [If xio_setoption() fails, IoTHubTransport_AMQP_Common_SetOption shall fail and return IOTHUB_CLIENT_ERROR.]
-                LogError("IoTHubTransport_AMQP_Common_SetOption failed (xio_setoption failed to set logging on SASL IO)");
-                result = IOTHUB_CLIENT_ERROR;
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_205: [If xio_setoption() succeeds, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_OK.]
-                result = IOTHUB_CLIENT_OK;
-            }
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_047: [If the option name does not match one of the options handled by this module, IoTHubTransport_AMQP_Common_SetOption shall pass the value and name to the XIO using xio_setoption().] 
-        else
-        {
-            result = IOTHUB_CLIENT_OK;
+		if (is_device_specific_option)
+		{
+			if (IoTHubTransport_AMQP_Common_Device_SetOption(handle, option, (void*)value) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_103: [If device_set_option() fails, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_ERROR]
+				LogError("transport failed setting option '%s' (failed setting option on one or more registered devices)", option);
+				result = IOTHUB_CLIENT_ERROR;
+			}
+			else
+			{
+				result = IOTHUB_CLIENT_OK;
+			}
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_104: [If `option` is `logtrace`, `value` shall be saved and applied to `instance->connection` using amqp_connection_set_logging()]
+		else if (strcmp(OPTION_LOG_TRACE, option) == 0)
+		{
+		    transport_instance->is_trace_on = *((bool*)value);
 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_007: [ If optionName is x509certificate and the authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]
-            if (strcmp(OPTION_X509_CERT, option) == 0)
-            {
-                if (transport_state->preferred_credential_type == CREDENTIAL_NOT_BUILD)
-                {
-                    transport_state->preferred_credential_type = X509;
-                }
-                else if (transport_state->preferred_credential_type != X509)
-                {
-                    LogError("x509certificate specified, but authentication method is not x509");
-                    result = IOTHUB_CLIENT_INVALID_ARG;
-                }
-            }
-            /*Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_008: [ If optionName is x509privatekey and the authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/
-            else if (strcmp(OPTION_X509_PRIVATE_KEY, option) == 0)
-            {
-                if (transport_state->preferred_credential_type == CREDENTIAL_NOT_BUILD)
-                {
-                    transport_state->preferred_credential_type = X509;
-                }
-                else if (transport_state->preferred_credential_type != X509)
-                {
-                    LogError("x509privatekey specified, but authentication method is not x509");
-                    result = IOTHUB_CLIENT_INVALID_ARG;
-                }
-            }
+			if (transport_instance->amqp_connection != NULL &&
+				amqp_connection_set_logging(transport_instance->amqp_connection, transport_instance->is_trace_on) != RESULT_OK)
+			{
+				LogError("transport failed setting option '%s' (amqp_connection_set_logging failed)", option);
+				result = IOTHUB_CLIENT_ERROR;
+			}
+			else
+			{
+				result = IOTHUB_CLIENT_OK;
+			}
+		}
+		else
+		{
+			result = IOTHUB_CLIENT_OK;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_007: [ If `option` is `x509certificate` and the transport preferred authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_008: [ If `option` is `x509privatekey` and the transport preferred authentication method is not x509 then IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]
+			if (strcmp(OPTION_X509_CERT, option) == 0 || strcmp(OPTION_X509_PRIVATE_KEY, option) == 0)
+			{
+			    if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET)
+			    {
+					transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_X509;
+			    }
+			    else if (transport_instance->preferred_authentication_mode != AMQP_TRANSPORT_AUTHENTICATION_MODE_X509)
+			    {
+			        LogError("transport failed setting option '%s' (preferred authentication method is not x509)", option);
+			        result = IOTHUB_CLIENT_INVALID_ARG;
+			    }
+			}
 
-            if (result != IOTHUB_CLIENT_INVALID_ARG)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_206: [If the TLS IO does not exist, IoTHubTransport_AMQP_Common_SetOption shall create it and save it on the transport instance.]
-                if (transport_state->tls_io == NULL &&
-                    (transport_state->tls_io = transport_state->underlying_io_transport_provider(STRING_c_str(transport_state->iotHubHostFqdn))) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_207: [If IoTHubTransport_AMQP_Common_SetOption fails creating the TLS IO instance, it shall fail and return IOTHUB_CLIENT_ERROR.]
-                    result = IOTHUB_CLIENT_ERROR;
-                    LogError("IoTHubTransport_AMQP_Common_SetOption failed (failed to obtain a TLS I/O transport layer).");
-                }
-                else
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_208: [When a new TLS IO instance is created, IoTHubTransport_AMQP_Common_SetOption shall apply the TLS I/O Options with OptionHandler_FeedOptions() if it is has any saved.]
-                    if (transport_state->xioOptions != NULL)
-                    {
-                        if (OptionHandler_FeedOptions(transport_state->xioOptions, transport_state->tls_io) != 0)
-                        {
-                            LogError("IoTHubTransport_AMQP_Common_SetOption failed (unable to replay options to TLS)");
-                        }
-                        else
-                        {
-                            OptionHandler_Destroy(transport_state->xioOptions);
-                            transport_state->xioOptions = NULL;
-                        }
-                    }
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_105: [If `option` does not match one of the options handled by this module, it shall be passed to `instance->tls_io` using xio_setoption()]
+			if (result != IOTHUB_CLIENT_INVALID_ARG)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_106: [If `instance->tls_io` is NULL, it shall be set invoking instance->underlying_io_transport_provider()]
+				if (transport_instance->tls_io == NULL &&
+					get_new_underlying_io_transport(transport_instance, &transport_instance->tls_io) != RESULT_OK)
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_107: [If instance->underlying_io_transport_provider() fails, IoTHubTransport_AMQP_Common_SetOption shall fail and return IOTHUB_CLIENT_ERROR]
+					LogError("transport failed setting option '%s' (failed to obtain a TLS I/O transport).", option);
+					result = IOTHUB_CLIENT_ERROR;
+				}
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_108: [When `instance->tls_io` is created, IoTHubTransport_AMQP_Common_SetOption shall apply `instance->saved_tls_options` with OptionHandler_FeedOptions()]
+				else if (xio_setoption(transport_instance->tls_io, option, value) != RESULT_OK)
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_001: [If xio_setoption fails, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_ERROR.]
+					LogError("transport failed setting option '%s' (xio_setoption failed)", option);
+					result = IOTHUB_CLIENT_ERROR;
+				}
+				else
+				{
+					if (save_underlying_io_transport_options(transport_instance) != RESULT_OK)
+					{
+						LogError("IoTHubTransport_AMQP_Common_SetOption failed to save underlying I/O options; failure will be ignored");
+					}
 
-                    if (xio_setoption(transport_state->tls_io, option, value) != RESULT_OK)
-                    {
-                        /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_001: [If xio_setoption fails, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_ERROR.] */
-                        result = IOTHUB_CLIENT_ERROR;
-                        LogError("Invalid option (%s) passed to IoTHubTransport_AMQP_Common_SetOption", option);
-                    }
-                    else
-                    {
-                        result = IOTHUB_CLIENT_OK;
-                    }
-                }
-            }
-        }
-    }
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_001: [If no failures occur, IoTHubTransport_AMQP_Common_SetOption shall return IOTHUB_CLIENT_OK.]
+					result = IOTHUB_CLIENT_OK;
+				}
+			}
+		}
+	}
 
     return result;
 }
@@ -1677,178 +1623,145 @@
     UNUSED(iotHubClientHandle);
 #endif
 
-    IOTHUB_DEVICE_HANDLE result = NULL;
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_17_001: [IoTHubTransport_AMQP_Common_Register shall return NULL if device, or waitingToSend are NULL.] 
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_17_005: [IoTHubTransport_AMQP_Common_Register shall return NULL if the TRANSPORT_LL_HANDLE is NULL.]
-    if ((handle == NULL) || (device == NULL) || (waitingToSend == NULL))
+    IOTHUB_DEVICE_HANDLE result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_17_005: [If `handle`, `device`, `iotHubClientHandle` or `waitingToSend` is NULL, IoTHubTransport_AMQP_Common_Register shall return NULL]
+	if ((handle == NULL) || (device == NULL) || (waitingToSend == NULL) || (iotHubClientHandle == NULL))
     {
-        LogError("invalid parameter TRANSPORT_LL_HANDLE handle=%p, const IOTHUB_DEVICE_CONFIG* device=%p, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle=%p, PDLIST_ENTRY waitingToSend=%p",
+        LogError("invalid parameter TRANSPORT_LL_HANDLE handle=%p, const IOTHUB_DEVICE_CONFIG* device=%p, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle=%p, PDLIST_ENTRY waiting_to_send=%p",
             handle, device, iotHubClientHandle, waitingToSend);
+		result = NULL;
     }
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_220: [IoTHubTransport_AMQP_Common_Register shall fail and return NULL if the IOTHUB_CLIENT_LL_HANDLE is NULL.]
-    else if (iotHubClientHandle == NULL)
-    {
-        LogError("IoTHubTransport_AMQP_Common_Register failed (invalid parameter; iotHubClientHandle list is NULL)");
-    }
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_002: [IoTHubTransport_AMQP_Common_Register shall return NULL if `device->deviceId` is NULL.]
+	else if (device->deviceId == NULL)
+	{
+		LogError("Transport failed to register device (device_id provided is NULL)");
+		result = NULL;
+	}
     else
     {
-        AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)handle;
+		LIST_ITEM_HANDLE list_item;
+        AMQP_TRANSPORT_INSTANCE* transport_instance = (AMQP_TRANSPORT_INSTANCE*)handle;
 
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_002: [IoTHubTransport_AMQP_Common_Register shall return NULL if deviceId is NULL.]
-        if (device->deviceId == NULL)
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_064: [If the device is already registered, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
+		if (is_device_registered_ex(transport_instance->registered_devices, device->deviceId, &list_item))
+		{
+			LogError("IoTHubTransport_AMQP_Common_Register failed (device '%s' already registered on this transport instance)", device->deviceId);
+			result = NULL;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_065: [IoTHubTransport_AMQP_Common_Register shall fail and return NULL if the device is not using an authentication mode compatible with the currently used by the transport.]
+		else if (!is_device_credential_acceptable(device, transport_instance->preferred_authentication_mode))
         {
-            LogError("IoTHubTransport_AMQP_Common_Register failed (deviceId provided is NULL)");
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_03_003: [IoTHubTransport_AMQP_Common_Register shall return NULL if both deviceKey and deviceSasToken are not NULL.]
-        else if ((device->deviceSasToken != NULL) && (device->deviceKey != NULL))
-        {
-            LogError("IoTHubTransport_AMQP_Common_Register failed (invalid IOTHUB_DEVICE_CONFIG; must provide EITHER 'deviceSasToken' OR 'deviceKey')");
-        }
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_221: [IoTHubTransport_AMQP_Common_Register shall fail and return NULL if the device is not using an authentication mode compatible with the currently used by the transport.]
-        else if (is_credential_compatible(device, transport_state->preferred_credential_type) != RESULT_OK)
-        {
-            LogError("IoTHubTransport_AMQP_Common_Register failed (transport does not support mixed authentication methods)");
-        }
+            LogError("Transport failed to register device '%s' (device credential was not accepted)", device->deviceId);
+			result = NULL;
+		}
         else
         {
-            AMQP_TRANSPORT_DEVICE_STATE* device_state;
+            AMQP_TRANSPORT_DEVICE_INSTANCE* amqp_device_instance;
 
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_222: [If a device matching the deviceId provided is already registered, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
-            if (VECTOR_find_if(transport_state->registered_devices, findDeviceById, device->deviceId) != NULL)
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_066: [IoTHubTransport_AMQP_Common_Register shall allocate an instance of AMQP_TRANSPORT_DEVICE_INSTANCE to store the state of the new registered device.]
+            if ((amqp_device_instance = (AMQP_TRANSPORT_DEVICE_INSTANCE*)malloc(sizeof(AMQP_TRANSPORT_DEVICE_INSTANCE))) == NULL)
             {
-                LogError("IoTHubTransport_AMQP_Common_Register failed (device '%s' already registered on this transport instance)", device->deviceId);
-            }
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_223: [IoTHubTransport_AMQP_Common_Register shall allocate an instance of AMQP_TRANSPORT_DEVICE_STATE to store the state of the new registered device.]
-            else if ((device_state = (AMQP_TRANSPORT_DEVICE_STATE*)malloc(sizeof(AMQP_TRANSPORT_DEVICE_STATE))) == NULL)
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_224: [If malloc fails to allocate memory for AMQP_TRANSPORT_DEVICE_STATE, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
-                LogError("IoTHubTransport_AMQP_Common_Register failed (malloc failed)");
-            }
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_067: [If malloc fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
+				LogError("Transport failed to register device '%s' (failed to create the device state instance; malloc failed)", device->deviceId);
+				result = NULL;
+			}
             else
             {
-                bool cleanup_required;
-                const char* deviceId = device->deviceId;
-
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_225: [IoTHubTransport_AMQP_Common_Register shall save the handle references to the IoTHubClient, transport, waitingToSend list on the device state.]
-                device_state->iothub_client_handle = iotHubClientHandle;
-                device_state->transport_state = transport_state;
-
-                device_state->waitingToSend = waitingToSend;
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_226: [IoTHubTransport_AMQP_Common_Register shall initialize the device state inProgress list using DList_InitializeListHead().]
-                DList_InitializeListHead(&device_state->inProgress);
-
-                device_state->deviceId = NULL;
-                device_state->authentication = NULL;
-                device_state->devicesPath = NULL;
-                device_state->messageReceiveAddress = NULL;
-                device_state->targetAddress = NULL;
-
-                device_state->receive_messages = false;
-                device_state->message_receiver = NULL;
-                device_state->message_sender = NULL;
-                device_state->message_sender_state = MESSAGE_SENDER_STATE_IDLE;
-                device_state->receiver_link = NULL;
-                device_state->sender_link = NULL;
+				memset(amqp_device_instance, 0, sizeof(AMQP_TRANSPORT_DEVICE_INSTANCE));
+            
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_068: [IoTHubTransport_AMQP_Common_Register shall save the handle references to the IoTHubClient, transport, waitingToSend list on `amqp_device_instance`.]
+				amqp_device_instance->iothub_client_handle = iotHubClientHandle;
+                amqp_device_instance->transport_instance = transport_instance;
+                amqp_device_instance->waiting_to_send = waitingToSend;
+				amqp_device_instance->device_state = DEVICE_STATE_STOPPED;
+				amqp_device_instance->max_state_change_timeout_secs = DEFAULT_DEVICE_STATE_CHANGE_TIMEOUT_SECS;
+     
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-                device_state->subscribe_methods_needed = false;
-                device_state->subscribed_for_methods = false;
+                amqp_device_instance->subscribe_methods_needed = false;
+                amqp_device_instance->subscribed_for_methods = false;
 #endif
-
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_227: [IoTHubTransport_AMQP_Common_Register shall store a copy of config->deviceId into device_state->deviceId.]
-                if ((device_state->deviceId = STRING_construct(deviceId)) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_228: [If STRING_construct fails to copy config->deviceId, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
-                    LogError("IoTHubTransport_AMQP_Common_Register failed to copy the deviceId.");
-                    cleanup_required = true;
-                }
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_012: [IoTHubTransport_AMQP_Common_Register shall create an immutable string, referred to as devicesPath, from the following parts: host_fqdn + "/devices/" + deviceId.] 
-                else if ((device_state->devicesPath = concat3Params(STRING_c_str(transport_state->iotHubHostFqdn), "/devices/", STRING_c_str(device_state->deviceId))) == NULL)
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_069: [A copy of `config->deviceId` shall be saved into `device_state->device_id`]
+                if ((amqp_device_instance->device_id = STRING_construct(device->deviceId)) == NULL)
                 {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_013: [If creating devicesPath fails for any reason then IoTHubTransport_AMQP_Common_Register shall fail and return NULL.] 
-                    LogError("IoTHubTransport_AMQP_Common_Register failed to construct the devicesPath.");
-                    cleanup_required = true;
-                }
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_014: [IoTHubTransport_AMQP_Common_Register shall create an immutable string, referred to as targetAddress, from the following parts: "amqps://" + devicesPath + "/messages/events".]
-                else if ((device_state->targetAddress = concat3Params("amqps://", STRING_c_str(device_state->devicesPath), "/messages/events")) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_015: [If creating the targetAddress fails for any reason then IoTHubTransport_AMQP_Common_Register shall fail and return NULL.] 
-                    LogError("IoTHubTransport_AMQP_Common_Register failed to construct the targetAddress.");
-                    cleanup_required = true;
-                }
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_053: [IoTHubTransport_AMQP_Common_Register shall define the source address for receiving messages as "amqps://" + devicesPath + "/messages/devicebound", stored in the transport handle as messageReceiveAddress]
-                else if ((device_state->messageReceiveAddress = concat3Params("amqps://", STRING_c_str(device_state->devicesPath), "/messages/devicebound")) == NULL)
-                {
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_054: [If creating the messageReceiveAddress fails for any reason then IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
-                    LogError("IoTHubTransport_AMQP_Common_Register failed to construct the messageReceiveAddress.");
-                    cleanup_required = true;
-                }
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_070: [If STRING_construct() fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL]
+					LogError("Transport failed to register device '%s' (failed to copy the deviceId)", device->deviceId);
+					result = NULL;
+				}
                 else
                 {
+					DEVICE_CONFIG device_config;
+					memset(&device_config, 0, sizeof(DEVICE_CONFIG));
+					device_config.device_id = (char*)device->deviceId;
+					device_config.iothub_host_fqdn = (char*)STRING_c_str(transport_instance->iothub_host_fqdn);
+					device_config.device_primary_key = (char*)device->deviceKey;
+					device_config.device_sas_token = (char*)device->deviceSasToken;
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_072: [The configuration for device_create shall be set according to the authentication preferred by IOTHUB_DEVICE_CONFIG]
+					device_config.authentication_mode = (device->deviceKey != NULL || device->deviceSasToken != NULL ? DEVICE_AUTH_MODE_CBS : DEVICE_AUTH_MODE_X509);
+					device_config.on_state_changed_callback = on_device_state_changed_callback;
+					device_config.on_state_changed_context = amqp_device_instance;
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_071: [`amqp_device_instance->device_handle` shall be set using device_create()]
+					if ((amqp_device_instance->device_handle = device_create(&device_config)) == NULL)
+					{
+						// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_073: [If device_create() fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL]
+						LogError("Transport failed to register device '%s' (failed to create the DEVICE_HANDLE instance)", device->deviceId);
+						result = NULL;
+					}
+					else
+					{
+						bool is_first_device_being_registered = (singlylinkedlist_get_head_item(transport_instance->registered_devices) == NULL);
+
 #ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-                    /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_010: [ `IoTHubTransport_AMQP_Common_Create` shall create a new iothubtransportamqp_methods instance by calling `iothubtransportamqp_methods_create` while passing to it the the fully qualified domain name and the device Id. ]*/
-                    device_state->methods_handle = iothubtransportamqp_methods_create(STRING_c_str(transport_state->iotHubHostFqdn), deviceId);
-                    if (device_state->methods_handle == NULL)
-                    {
-                        /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_011: [ If `iothubtransportamqp_methods_create` fails, `IoTHubTransport_AMQP_Common_Create` shall fail and return NULL. ]*/
-                        LogError("Cannot create the methods module");
-                        cleanup_required = true;
-                    }
-                    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_231: [IoTHubTransport_AMQP_Common_Register shall add the device to transport_state->registered_devices using VECTOR_push_back().]
-                    else
+						/* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_010: [ `IoTHubTransport_AMQP_Common_Create` shall create a new iothubtransportamqp_methods instance by calling `iothubtransportamqp_methods_create` while passing to it the the fully qualified domain name and the device Id. ]*/
+						amqp_device_instance->methods_handle = iothubtransportamqp_methods_create(STRING_c_str(transport_instance->iothub_host_fqdn), device->deviceId);
+						if (amqp_device_instance->methods_handle == NULL)
+						{
+							/* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_011: [ If `iothubtransportamqp_methods_create` fails, `IoTHubTransport_AMQP_Common_Create` shall fail and return NULL. ]*/
+							LogError("Transport failed to register device '%s' (Cannot create the methods module)", device->deviceId);
+							result = NULL;
+						}
+						else
 #endif
-                    if (VECTOR_push_back(transport_state->registered_devices, &device_state, 1) != 0)
-                    {
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_232: [If VECTOR_push_back() fails to add the new registered device, IoTHubTransport_AMQP_Common_Register shall clean the memory it allocated, fail and return NULL.]
-                        LogError("IoTHubTransport_AMQP_Common_Register failed to add the new device to its list of registered devices (VECTOR_push_back failed).");
-                        cleanup_required = true;
-                    }
-                    else
-                    {
-                        AUTHENTICATION_CONFIG auth_config;
-                        auth_config.device_id = deviceId;
-                        auth_config.device_key = device->deviceKey;
-                        auth_config.device_sas_token = device->deviceSasToken;
-                        auth_config.cbs_connection = &device_state->transport_state->cbs_connection;
-                        auth_config.iot_hub_host_fqdn = STRING_c_str(device_state->transport_state->iotHubHostFqdn);
+							if (replicate_device_options_to(amqp_device_instance, device_config.authentication_mode) != RESULT_OK)
+							{
+								LogError("Transport failed to register device '%s' (failed to replicate options)", device->deviceId);
+								result = NULL;
+							}
+							// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_074: [IoTHubTransport_AMQP_Common_Register shall add the `amqp_device_instance` to `instance->registered_devices`]
+							else if (singlylinkedlist_add(transport_instance->registered_devices, amqp_device_instance) == NULL)
+							{
+								// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_075: [If it fails to add `amqp_device_instance`, IoTHubTransport_AMQP_Common_Register shall fail and return NULL]
+								LogError("Transport failed to register device '%s' (singlylinkedlist_add failed)", device->deviceId);
+								result = NULL;
+							}
+							else
+							{
+								// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_076: [If the device is the first being registered on the transport, IoTHubTransport_AMQP_Common_Register shall save its authentication mode as the transport preferred authentication mode]
+								if (transport_instance->preferred_authentication_mode == AMQP_TRANSPORT_AUTHENTICATION_MODE_NOT_SET &&
+									is_first_device_being_registered)
+								{
+									if (device_config.authentication_mode == DEVICE_AUTH_MODE_CBS)
+									{
+										transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_CBS;
+									}
+									else
+									{
+										transport_instance->preferred_authentication_mode = AMQP_TRANSPORT_AUTHENTICATION_MODE_X509;
+									}
+								}
 
-                        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_229: [IoTHubTransport_AMQP_Common_Register shall create an authentication state for the device using authentication_create() and store it on the device state.]
-                        if ((device_state->authentication = authentication_create(&auth_config)) == NULL)
-                        {
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_230: [If authentication_create() fails, IoTHubTransport_AMQP_Common_Register shall fail and return NULL.]
-                            LogError("IoTHubTransport_AMQP_Common_Register failed to create an authentication state for the device.");
-                            cleanup_required = true;
-                        }
-                        else
-                        {
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_234: [If the device is the first being registered on the transport, IoTHubTransport_AMQP_Common_Register shall save its authentication mode as the transport preferred authentication mode.]
-                            if (VECTOR_size(transport_state->registered_devices) == 1)
-                            {
-                                transport_state->preferred_credential_type = authentication_get_credential(device_state->authentication)->type;
-                            }
-
-                            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_233: [IoTHubTransport_AMQP_Common_Register shall return its internal device representation as a IOTHUB_DEVICE_HANDLE.]
-                            result = (IOTHUB_DEVICE_HANDLE)device_state;
-                            cleanup_required = false;
-                        }
-                    }
+								// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_078: [IoTHubTransport_AMQP_Common_Register shall return a handle to `amqp_device_instance` as a IOTHUB_DEVICE_HANDLE]
+								result = (IOTHUB_DEVICE_HANDLE)amqp_device_instance;
+							}
+					}
                 }
 
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_233: [If IoTHubTransport_AMQP_Common_Register fails, it shall free all memory it alloacated (destroy deviceId, authentication state, targetAddress, messageReceiveAddress, devicesPath, device state).]
-                if (cleanup_required)
+                if (result == NULL)
                 {
-                    if (device_state->deviceId != NULL)
-                        STRING_delete(device_state->deviceId);
-                    if (device_state->authentication != NULL)
-                        authentication_destroy(device_state->authentication);
-                    if (device_state->targetAddress != NULL)
-                        STRING_delete(device_state->targetAddress);
-                    if (device_state->messageReceiveAddress != NULL)
-                        STRING_delete(device_state->messageReceiveAddress);
-                    if (device_state->devicesPath != NULL)
-                        STRING_delete(device_state->devicesPath);
-
-                    free(device_state);
-                }
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_077: [If IoTHubTransport_AMQP_Common_Register fails, it shall free all memory it allocated]
+					internal_destroy_amqp_device_instance(amqp_device_instance);
+				}
             }
         }
     }
@@ -1858,101 +1771,62 @@
 
 void IoTHubTransport_AMQP_Common_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle)
 {
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_214: [IoTHubTransport_AMQP_Common_Unregister should fail and return if the IOTHUB_DEVICE_HANDLE parameter provided is NULL.]
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_079: [if `deviceHandle` provided is NULL, IoTHubTransport_AMQP_Common_Unregister shall return.]
     if (deviceHandle == NULL)
     {
-        LogError("IoTHubTransport_AMQP_Common_Unregister failed (deviceHandle is NULL).");
+        LogError("Failed to unregister device (deviceHandle is NULL).");
     }
     else
     {
-        AMQP_TRANSPORT_DEVICE_STATE* device_state = (AMQP_TRANSPORT_DEVICE_STATE*)deviceHandle;
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_215: [IoTHubTransport_AMQP_Common_Unregister should fail and return if the IOTHUB_DEVICE_HANDLE parameter provided has a NULL reference to its transport instance.]
-        if (device_state->transport_state == NULL)
-        {
-            LogError("IoTHubTransport_AMQP_Common_Unregister failed (deviceHandle does not have a transport state associated to).");
-        }
-        else
-        {
-            IOTHUB_DEVICE_HANDLE* registered_device_state;
-
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_216: [IoTHubTransport_AMQP_Common_Unregister should fail and return if the device is not registered with this transport.]
-            if ((registered_device_state = VECTOR_find_if(device_state->transport_state->registered_devices, findDeviceById, STRING_c_str(device_state->deviceId))) == NULL)
-            {
-                LogError("IoTHubTransport_AMQP_Common_Unregister failed (device '%s' is not registered on this transport instance)", STRING_c_str(device_state->deviceId));
-            }
-            else
-            {
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_024: [IoTHubTransport_AMQP_Common_Unregister shall destroy the AMQP message_sender.]
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_029: [IoTHubTransport_AMQP_Common_Unregister shall destroy the AMQP message_sender link.]
-                destroyEventSender(device_state);
+        AMQP_TRANSPORT_DEVICE_INSTANCE* registered_device = (AMQP_TRANSPORT_DEVICE_INSTANCE*)deviceHandle;
+		const char* device_id;
+		LIST_ITEM_HANDLE list_item;
 
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_025: [IoTHubTransport_AMQP_Common_Unregister shall destroy the AMQP message_receiver.] 
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_211: [IoTHubTransport_AMQP_Common_Unregister shall destroy the AMQP message_receiver link.]
-                destroyMessageReceiver(device_state);
-
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_036: [IoTHubTransport_AMQP_Common_Unregister shall return the remaining items in inProgress to waitingToSend list.]
-                rollEventsBackToWaitList(device_state);
+		if ((device_id = STRING_c_str(registered_device->device_id)) == NULL)
+		{
+			LogError("Failed to unregister device (failed to get device id char ptr)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_080: [if `deviceHandle` has a NULL reference to its transport instance, IoTHubTransport_AMQP_Common_Unregister shall return.]
+		else if (registered_device->transport_instance == NULL)
+        {
+			LogError("Failed to unregister device '%s' (deviceHandle does not have a transport state associated to).", device_id);
+        }
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_081: [If the device is not registered with this transport, IoTHubTransport_AMQP_Common_Unregister shall return]
+		else if (!is_device_registered_ex(registered_device->transport_instance->registered_devices, device_id, &list_item))
+		{
+			LogError("Failed to unregister device '%s' (device is not registered within this transport).", device_id);
+		}
+		else
+		{
+			// Removing it first so the race hazzard is reduced between this function and DoWork. Best would be to use locks.
+			if (singlylinkedlist_remove(registered_device->transport_instance->registered_devices, list_item) != RESULT_OK)
+			{
+				LogError("Failed to unregister device '%s' (singlylinkedlist_remove failed).", device_id);
+			}
+			else
+			{
+				// TODO: Q: should we go through waiting_to_send list and raise on_event_send_complete with BECAUSE_DESTROY ?
 
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_035: [IoTHubTransport_AMQP_Common_Unregister shall delete its internally-set parameters (targetAddress, messageReceiveAddress, devicesPath, deviceId).]
-                STRING_delete(device_state->targetAddress);
-                STRING_delete(device_state->messageReceiveAddress);
-                STRING_delete(device_state->devicesPath);
-                STRING_delete(device_state->deviceId);
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_217: [IoTHubTransport_AMQP_Common_Unregister shall destroy the authentication state of the device using authentication_destroy.]
-                authentication_destroy(device_state->authentication);
-
-#ifdef WIP_C2D_METHODS_AMQP /* This feature is WIP, do not use yet */
-                /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_012: [IoTHubTransport_AMQP_Common_Unregister shall destroy the C2D methods handler by calling iothubtransportamqp_methods_destroy.]*/
-                iothubtransportamqp_methods_destroy(device_state->methods_handle);
-#endif
-
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_218: [IoTHubTransport_AMQP_Common_Unregister shall remove the device from its list of registered devices using VECTOR_erase().]
-                VECTOR_erase(device_state->transport_state->registered_devices, registered_device_state, 1);
-
-                // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_219: [IoTHubTransport_AMQP_Common_Unregister shall destroy the IOTHUB_DEVICE_HANDLE instance provided.]
-                free(device_state);
-            }
-        }
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_01_012: [IoTHubTransport_AMQP_Common_Unregister shall destroy the C2D methods handler by calling iothubtransportamqp_methods_destroy]
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_083: [IoTHubTransport_AMQP_Common_Unregister shall free all the memory allocated for the `device_instance`]
+				internal_destroy_amqp_device_instance(registered_device);
+			}
+		}
     }
 }
 
 void IoTHubTransport_AMQP_Common_Destroy(TRANSPORT_LL_HANDLE handle)
 {
-    if (handle != NULL)
-    {
-        AMQP_TRANSPORT_INSTANCE* transport_state = (AMQP_TRANSPORT_INSTANCE*)handle;
-
-        size_t numberOfRegisteredDevices = VECTOR_size(transport_state->registered_devices);
-
-        for (size_t i = 0; i < numberOfRegisteredDevices; i++)
-        {
-            // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_209: [IoTHubTransport_AMQP_Common_Destroy shall invoke IoTHubTransport_AMQP_Common_Unregister on each of its registered devices.]
-            IoTHubTransport_AMQP_Common_Unregister(*(AMQP_TRANSPORT_DEVICE_STATE**)VECTOR_element(transport_state->registered_devices, i));
-        }
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_210: [IoTHubTransport_AMQP_Common_Destroy shall its list of registered devices using VECTOR_destroy().]
-        VECTOR_destroy(transport_state->registered_devices);
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_027: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP cbs instance]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_030: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP session.]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_031: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP connection.]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_032: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP SASL I / O transport.]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_033: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP SASL mechanism.]
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_034: [IoTHubTransport_AMQP_Common_Destroy shall destroy the AMQP TLS I/O transport.]
-        destroyConnection(transport_state);
-
-        // CodeS_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_212: [IoTHubTransport_AMQP_Common_Destroy shall destroy the IoTHub FQDN value saved on the transport instance]
-        STRING_delete(transport_state->iotHubHostFqdn);
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_213: [IoTHubTransport_AMQP_Common_Destroy shall destroy any TLS I/O options saved on the transport instance using OptionHandler_Destroy()]
-        if (transport_state->xioOptions != NULL)
-        {
-            OptionHandler_Destroy(transport_state->xioOptions);
-        }
-
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_150: [IoTHubTransport_AMQP_Common_Destroy shall destroy the transport instance]
-        free(transport_state);
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_013: [If `handle` is NULL, IoTHubTransport_AMQP_Common_Destroy shall return immediatelly]
+	if (handle == NULL)
+	{
+		LogError("Failed to destroy AMQP transport instance (handle is NULL)");
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_014: [IoTHubTransport_AMQP_Common_Destroy shall invoke IoTHubTransport_AMQP_Common_Unregister on each of its registered devices.]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_015: [All members of `instance` (including tls_io) shall be destroyed and its memory released]
+		internal_destroy_instance((AMQP_TRANSPORT_INSTANCE*)handle);
     }
 }
 
@@ -1972,16 +1846,103 @@
 STRING_HANDLE IoTHubTransport_AMQP_Common_GetHostname(TRANSPORT_LL_HANDLE handle)
 {
     STRING_HANDLE result;
-    // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_001: [If parameter handle is NULL then IoTHubTransport_AMQP_Common_GetHostname shall return NULL.]
-    if (handle == NULL)
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_001: [If `handle` is NULL, `IoTHubTransport_AMQP_Common_GetHostname` shall return NULL.]
+	if (handle == NULL)
     {
-        result = NULL;
+		LogError("Cannot provide the target host name (transport handle is NULL).");
+
+		result = NULL;
+    }
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_002: [IoTHubTransport_AMQP_Common_GetHostname shall return a copy of `instance->iothub_target_fqdn`.]
+	else if ((result = STRING_clone(((AMQP_TRANSPORT_INSTANCE*)(handle))->iothub_host_fqdn)) == NULL)
+	{
+		LogError("Cannot provide the target host name (STRING_clone failed).");
+	}
+
+    return result;
+}
+
+static DEVICE_MESSAGE_DISPOSITION_INFO* create_device_message_disposition_info_from(MESSAGE_CALLBACK_INFO* message_data)
+{
+	DEVICE_MESSAGE_DISPOSITION_INFO* result;
+
+	if ((result = (DEVICE_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(DEVICE_MESSAGE_DISPOSITION_INFO))) == NULL)
+	{
+		LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (malloc failed)");
+	}
+	else if (mallocAndStrcpy_s(&result->source, message_data->transportContext->link_name) != RESULT_OK)
+	{
+		LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)");
+		free(result);
+		result = NULL;
+	}
+	else
+	{
+		result->message_id = message_data->transportContext->message_id;
+	}
+
+	return result;
+}
+
+static void destroy_device_message_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info)
+{
+	free(device_message_disposition_info->source);
+	free(device_message_disposition_info);
+}
+
+IOTHUB_CLIENT_RESULT IoTHubTransport_AMQP_Common_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition)
+{
+    IOTHUB_CLIENT_RESULT result;
+    if (message_data == NULL)
+    {
+        /* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_001: [If messageData is NULL, IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_INVALID_ARG.] */
+        LogError("Failed sending message disposition (message_data is NULL)");
+        result = IOTHUB_CLIENT_INVALID_ARG;
     }
     else
     {
-        // Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_02_002: [Otherwise IoTHubTransport_AMQP_Common_GetHostname shall return the target IoT Hub FQDN as a STRING_HANDLE.]
-        result = ((AMQP_TRANSPORT_INSTANCE*)(handle))->iotHubHostFqdn;
+		/* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_002: [If any of the messageData fields are NULL, IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_INVALID_ARG.] */
+        if (message_data->messageHandle == NULL || message_data->transportContext == NULL)
+        {
+			LogError("Failed sending message disposition (message_data->messageHandle (%p) or message_data->transportContext (%p) are NULL)", message_data->messageHandle, message_data->transportContext);
+            result = IOTHUB_CLIENT_INVALID_ARG;
+        }
+		else
+		{
+			DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info;
+
+			/* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_004: [IoTHubTransport_AMQP_Common_SendMessageDisposition shall convert the given IOTHUBMESSAGE_DISPOSITION_RESULT to the equivalent AMQP_VALUE and will return the result of calling messagereceiver_send_message_disposition. ] */
+			DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result = get_device_disposition_result_from(disposition);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_112: [A DEVICE_MESSAGE_DISPOSITION_INFO instance shall be created with a copy of the `link_name` and `message_id` contained in `message_data`]  
+			if ((device_message_disposition_info = create_device_message_disposition_info_from(message_data)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_113: [If the DEVICE_MESSAGE_DISPOSITION_INFO fails to be created, `IoTHubTransport_AMQP_Common_SendMessageDisposition()` shall fail and return IOTHUB_CLIENT_ERROR]
+				LogError("Device '%s' failed sending message disposition (failed creating DEVICE_MESSAGE_DISPOSITION_RESULT)", STRING_c_str(message_data->transportContext->device_state->device_id));
+				result = IOTHUB_CLIENT_ERROR;
+			}
+			else
+			{
+				/* Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_10_003: [IoTHubTransport_AMQP_Common_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR if the POST message fails, otherwise return IOTHUB_CLIENT_OK.] */
+				if (device_send_message_disposition(message_data->transportContext->device_state->device_handle, device_message_disposition_info, device_disposition_result) != RESULT_OK)
+				{
+					LogError("Device '%s' failed sending message disposition (device_send_message_disposition failed)", STRING_c_str(message_data->transportContext->device_state->device_id));
+					result = IOTHUB_CLIENT_ERROR;
+				}
+				else
+				{
+					IoTHubMessage_Destroy(message_data->messageHandle);
+					MESSAGE_CALLBACK_INFO_Destroy(message_data);
+					result = IOTHUB_CLIENT_OK;
+				}
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_COMMON_09_114: [`IoTHubTransport_AMQP_Common_SendMessageDisposition()` shall destroy the DEVICE_MESSAGE_DISPOSITION_INFO instance]  
+				destroy_device_message_disposition_info(device_message_disposition_info);
+			}
+        }
     }
+
     return result;
 }
 
--- a/iothubtransport_amqp_common.h	Fri Feb 24 14:00:00 2017 -0800
+++ b/iothubtransport_amqp_common.h	Fri Mar 10 11:46:55 2017 -0800
@@ -5,14 +5,16 @@
 #define IOTHUBTRANSPORTAMQP_COMMON_H
 
 #include "azure_c_shared_utility/strings.h"
+#include "azure_c_shared_utility/umock_c_prod.h"
 #include "iothub_transport_ll.h"
-#include "azure_c_shared_utility/umock_c_prod.h"
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
+static const char* OPTION_EVENT_SEND_TIMEOUT_SECS = "event_send_timeout_secs";
+
 typedef XIO_HANDLE(*AMQP_GET_IO_TRANSPORT)(const char* target_fqdn);
 
 MOCKABLE_FUNCTION(, TRANSPORT_LL_HANDLE, IoTHubTransport_AMQP_Common_Create, const IOTHUBTRANSPORT_CONFIG*, config, AMQP_GET_IO_TRANSPORT, get_io_transport);
@@ -32,6 +34,7 @@
 MOCKABLE_FUNCTION(, IOTHUB_DEVICE_HANDLE, IoTHubTransport_AMQP_Common_Register, TRANSPORT_LL_HANDLE, handle, const IOTHUB_DEVICE_CONFIG*, device, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, PDLIST_ENTRY, waitingToSend);
 MOCKABLE_FUNCTION(, void, IoTHubTransport_AMQP_Common_Unregister, IOTHUB_DEVICE_HANDLE, deviceHandle);
 MOCKABLE_FUNCTION(, STRING_HANDLE, IoTHubTransport_AMQP_Common_GetHostname, TRANSPORT_LL_HANDLE, handle);
+MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubTransport_AMQP_Common_SendMessageDisposition, MESSAGE_CALLBACK_INFO*, message_data, IOTHUBMESSAGE_DISPOSITION_RESULT, disposition);
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_connection.c	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,525 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdlib.h>
+#include <limits.h>
+#include "iothubtransport_amqp_connection.h"
+#include "azure_c_shared_utility/optimize_size.h"
+#include "azure_c_shared_utility/gballoc.h"
+#include "azure_c_shared_utility/strings.h"
+#include "azure_c_shared_utility/uniqueid.h"
+#include "azure_uamqp_c/sasl_mechanism.h"
+#include "azure_uamqp_c/saslclientio.h"
+#include "azure_uamqp_c/sasl_mssbcbs.h"
+#include "azure_uamqp_c/connection.h"
+
+#define RESULT_OK                            0
+#define DEFAULT_CONNECTION_IDLE_TIMEOUT      240000
+#define DEFAULT_INCOMING_WINDOW_SIZE         UINT_MAX
+#define DEFAULT_OUTGOING_WINDOW_SIZE         100
+#define SASL_IO_OPTION_LOG_TRACE             "logtrace"
+#define DEFAULT_UNIQUE_ID_LENGTH             40
+
+typedef struct AMQP_CONNECTION_INSTANCE_TAG
+{
+	STRING_HANDLE iothub_fqdn;
+	XIO_HANDLE underlying_io_transport;
+	CBS_HANDLE cbs_handle;
+	CONNECTION_HANDLE connection_handle;
+	SESSION_HANDLE session_handle;
+	XIO_HANDLE sasl_io;
+	SASL_MECHANISM_HANDLE sasl_mechanism;
+	bool is_trace_on;
+	AMQP_CONNECTION_STATE current_state;
+	ON_AMQP_CONNECTION_STATE_CHANGED on_state_changed_callback;
+	const void* on_state_changed_context;
+} AMQP_CONNECTION_INSTANCE;
+
+static int create_sasl_components(AMQP_CONNECTION_INSTANCE* instance)
+{
+	int result;
+	SASL_MECHANISM_HANDLE sasl_mechanism;
+	XIO_HANDLE sasl_io;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_012: [`instance->sasl_mechanism` shall be created using saslmechanism_create()]
+	if ((sasl_mechanism = saslmechanism_create(saslmssbcbs_get_interface(), NULL)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_013: [If saslmechanism_create() fails, amqp_connection_create() shall fail and return NULL]
+		LogError("Failed creating the SASL mechanism (saslmechanism_create failed)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_014: [A SASLCLIENTIO_CONFIG shall be set with `instance->underlying_io_transport` and `instance->sasl_mechanism`]
+		SASLCLIENTIO_CONFIG sasl_client_config;
+		sasl_client_config.sasl_mechanism = sasl_mechanism;
+		sasl_client_config.underlying_io = instance->underlying_io_transport;
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_015: [`instance->sasl_io` shall be created using xio_create() passing saslclientio_get_interface_description() and the SASLCLIENTIO_CONFIG instance]
+		if ((sasl_io = xio_create(saslclientio_get_interface_description(), &sasl_client_config)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_016: [If xio_create() fails, amqp_connection_create() shall fail and return NULL]
+			LogError("Failed creating the SASL I/O (xio_create failed)");
+			saslmechanism_destroy(sasl_mechanism);
+			result = __FAILURE__;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_017: [The sasl_io "logtrace" option shall be set using xio_setoption(), passing `instance->is_trace_on`]
+		else if (xio_setoption(sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK)
+		{
+			LogError("Failed setting the SASL I/O logging trace option (xio_setoption failed)");
+			xio_destroy(sasl_io);
+			saslmechanism_destroy(sasl_mechanism);
+			result = __FAILURE__;
+		}
+		else
+		{
+			instance->sasl_mechanism = sasl_mechanism;
+			instance->sasl_io = sasl_io;
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+static void update_state(AMQP_CONNECTION_INSTANCE* instance, AMQP_CONNECTION_STATE new_state)
+{
+	if (new_state != instance->current_state)
+	{
+		AMQP_CONNECTION_STATE previous_state = instance->current_state;
+		instance->current_state = new_state;
+
+		if (instance->on_state_changed_callback != NULL)
+		{
+			instance->on_state_changed_callback(instance->on_state_changed_context, previous_state, new_state);
+		}
+	}
+}
+
+static void on_connection_io_error(void* context)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_022: [If the connection calls back with an I/O error, `instance->on_state_changed_callback` shall be invoked if set passing code AMQP_CONNECTION_STATE_ERROR and `instance->on_state_changed_context`]
+	update_state((AMQP_CONNECTION_INSTANCE*)context, AMQP_CONNECTION_STATE_ERROR);
+}
+
+static void on_connection_state_changed(void* context, CONNECTION_STATE new_connection_state, CONNECTION_STATE previous_connection_state)
+{
+	(void)previous_connection_state;
+
+	AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)context;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_063: [If `on_connection_state_changed` is called back, `instance->on_state_changed_callback` shall be invoked, if defined]
+	if (instance->cbs_handle == NULL || instance->sasl_io == NULL)
+	{
+		// connection is using x509 authentication.
+		// At this point uamqp's connection only raises CONNECTION_STATE_START when using X509 auth.
+		// So that should be all we expect to consider the amqp_connection_handle opened.
+		if (new_connection_state == CONNECTION_STATE_START)
+		{
+			update_state(instance, AMQP_CONNECTION_STATE_OPENED);
+		}
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_064: [If `on_connection_state_changed` new state is CONNECTION_STATE_OPENED, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_OPENED]
+	else if (new_connection_state == CONNECTION_STATE_OPENED)
+	{
+		update_state(instance, AMQP_CONNECTION_STATE_OPENED);
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_065: [If `on_connection_state_changed` new state is CONNECTION_STATE_END, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_CLOSED]
+	else if (new_connection_state == CONNECTION_STATE_END)
+	{
+		update_state(instance, AMQP_CONNECTION_STATE_CLOSED);
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_071: [If `on_connection_state_changed` new state is CONNECTION_STATE_ERROR, `instance->on_state_changed_callback` shall be invoked with state AMQP_CONNECTION_STATE_ERROR]
+	else if (new_connection_state == CONNECTION_STATE_ERROR)
+	{
+		update_state(instance, AMQP_CONNECTION_STATE_ERROR);
+	}
+}
+
+static int create_connection_handle(AMQP_CONNECTION_INSTANCE* instance)
+{
+	int result;
+	char* unique_container_id = NULL;
+	XIO_HANDLE connection_io_transport;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_007: [If `instance->sasl_io` is defined it shall be used as parameter `xio` in connection_create2()]
+	if (instance->sasl_io != NULL)
+	{
+		connection_io_transport = instance->sasl_io;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_018: [If `instance->sasl_io` is not defined, `instance->underlying_io_transport` shall be used as parameter `xio` in connection_create2()]
+	else
+	{
+		connection_io_transport = instance->underlying_io_transport;
+	}
+
+	if ((unique_container_id = (char*)malloc(sizeof(char) * DEFAULT_UNIQUE_ID_LENGTH + 1)) == NULL)
+	{
+		result = __LINE__;
+		LogError("Failed creating the AMQP connection (failed creating unique ID container)");
+	}
+	else
+	{
+		memset(unique_container_id, 0, sizeof(char) * DEFAULT_UNIQUE_ID_LENGTH + 1);
+
+		if (UniqueId_Generate(unique_container_id, DEFAULT_UNIQUE_ID_LENGTH) != UNIQUEID_OK)
+		{
+			result = __FAILURE__;
+			LogError("Failed creating the AMQP connection (UniqueId_Generate failed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_019: [`instance->connection_handle` shall be created using connection_create2(), passing the `connection_underlying_io`, `instance->iothub_host_fqdn` and an unique string as container ID]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_020: [connection_create2() shall also receive `on_connection_state_changed` and `on_connection_error` callback functions]
+		else if ((instance->connection_handle = connection_create2(connection_io_transport, STRING_c_str(instance->iothub_fqdn), unique_container_id, NULL, NULL, on_connection_state_changed, (void*)instance, on_connection_io_error, (void*)instance)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_021: [If connection_create2() fails, amqp_connection_create() shall fail and return NULL]
+			result = __FAILURE__;
+			LogError("Failed creating the AMQP connection (connection_create2 failed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_073: [The connection idle timeout parameter shall be set to 240000 milliseconds using connection_set_idle_timeout()]
+		else if (connection_set_idle_timeout(instance->connection_handle, DEFAULT_CONNECTION_IDLE_TIMEOUT) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_074: [If connection_set_idle_timeout() fails, amqp_connection_create() shall fail and return NULL]
+			result = __FAILURE__;
+			LogError("Failed creating the AMQP connection (connection_set_idle_timeout failed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_023: [The connection tracing shall be set using connection_set_trace(), passing `instance->is_trace_on`]
+			connection_set_trace(instance->connection_handle, instance->is_trace_on);
+
+			result = RESULT_OK;
+		}
+	}
+
+	if (unique_container_id != NULL)
+	{
+		free(unique_container_id);
+	}
+
+	return result;
+}
+
+static int create_session_handle(AMQP_CONNECTION_INSTANCE* instance)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_024: [`instance->session_handle` shall be created using session_create(), passing `instance->connection_handle`]
+	if ((instance->session_handle = session_create(instance->connection_handle, NULL, NULL)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_025: [If session_create() fails, amqp_connection_create() shall fail and return NULL]
+		result = __FAILURE__;
+		LogError("Failed creating the AMQP connection (connection_create2 failed)");
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_026: [The `instance->session_handle` incoming window size shall be set as UINT_MAX using session_set_incoming_window()]
+		if (session_set_incoming_window(instance->session_handle, (uint32_t)DEFAULT_INCOMING_WINDOW_SIZE) != 0)
+		{
+			LogError("Failed to set the AMQP session incoming window size.");
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_027: [The `instance->session_handle` outgoing window size shall be set as 100 using session_set_outgoing_window()]
+		if (session_set_outgoing_window(instance->session_handle, DEFAULT_OUTGOING_WINDOW_SIZE) != 0)
+		{
+			LogError("Failed to set the AMQP session outgoing window size.");
+		}
+
+		result = RESULT_OK;
+	}
+	
+	return result;
+}
+
+static int create_cbs_handle(AMQP_CONNECTION_INSTANCE* instance)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_029: [`instance->cbs_handle` shall be created using cbs_create(), passing `instance->session_handle`]
+	if ((instance->cbs_handle = cbs_create(instance->session_handle, NULL, (void*)instance)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_030: [If cbs_create() fails, amqp_connection_create() shall fail and return NULL]
+		result = __FAILURE__;
+		LogError("Failed to create the CBS connection.");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_031: [`instance->cbs_handle` shall be opened using cbs_open()]
+	else if (cbs_open(instance->cbs_handle) != RESULT_OK)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_032: [If cbs_open() fails, amqp_connection_create() shall fail and return NULL]
+		result = __FAILURE__;
+		LogError("Failed to open the connection with CBS.");
+	}
+	else
+	{
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+
+// Public APIS:
+
+void amqp_connection_destroy(AMQP_CONNECTION_HANDLE conn_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_035: [If `conn_handle` is NULL, amqp_connection_destroy() shall fail and return]
+	if (conn_handle != NULL)
+	{
+		AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_036: [amqp_connection_destroy() shall destroy `instance->cbs_handle` if set using cbs_destroy()]
+		if (instance->cbs_handle != NULL)
+		{
+			cbs_destroy(instance->cbs_handle);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_037: [amqp_connection_destroy() shall destroy `instance->session_handle` if set using session_destroy()]
+		if (instance->session_handle != NULL)
+		{
+			session_destroy(instance->session_handle);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_067: [amqp_connection_destroy() shall destroy `instance->connection_handle` if set using connection_destroy()]
+		if (instance->connection_handle != NULL)
+		{
+			connection_destroy(instance->connection_handle);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_038: [amqp_connection_destroy() shall destroy `instance->sasl_io` if set using xio_destroy()]
+		if (instance->sasl_io != NULL)
+		{
+			xio_destroy(instance->sasl_io);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_039: [amqp_connection_destroy() shall destroy `instance->sasl_mechanism` if set using saslmechanism_destroy()]
+		if (instance->sasl_mechanism != NULL)
+		{
+			saslmechanism_destroy(instance->sasl_mechanism);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_059: [amqp_connection_destroy() shall destroy `instance->iothub_host_fqdn` if set using STRING_delete()]
+		if (instance->iothub_fqdn != NULL)
+		{
+			STRING_delete(instance->iothub_fqdn);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_040: [amqp_connection_destroy() shall free the memory allocated for the connection instance]
+		free(instance);
+	}
+}
+
+AMQP_CONNECTION_HANDLE amqp_connection_create(AMQP_CONNECTION_CONFIG* config)
+{
+	AMQP_CONNECTION_HANDLE result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_001: [If `config` is NULL, amqp_connection_create() shall fail and return NULL]
+	if (config == NULL)
+	{
+		result = NULL;
+		LogError("amqp_connection_create failed (config is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_002: [If `config->iothub_host_fqdn` is NULL, amqp_connection_create() shall fail and return NULL]
+	else if (config->iothub_host_fqdn == NULL)
+	{
+		result = NULL;
+		LogError("amqp_connection_create failed (config->iothub_host_fqdn is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_003: [If `config->underlying_io_transport` is NULL, amqp_connection_create() shall fail and return NULL]
+	else if (config->underlying_io_transport == NULL)
+	{
+		result = NULL;
+		LogError("amqp_connection_create failed (config->underlying_io_transport is NULL)");
+	}
+	else
+	{
+		AMQP_CONNECTION_INSTANCE* instance;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_057: [amqp_connection_create() shall allocate memory for an instance of the connection state]
+		if ((instance = (AMQP_CONNECTION_INSTANCE*)malloc(sizeof(AMQP_CONNECTION_INSTANCE))) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_058: [If malloc() fails, amqp_connection_create() shall fail and return NULL]
+			result = NULL;
+			LogError("amqp_connection_create failed (malloc failed)");
+		}
+		else
+		{
+			memset(instance, 0, sizeof(AMQP_CONNECTION_INSTANCE));
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_005: [A copy of `config->iothub_host_fqdn` shall be saved on `instance->iothub_host_fqdn`]
+			if ((instance->iothub_fqdn = STRING_construct(config->iothub_host_fqdn)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_066: [If STRING_construct() fails, amqp_connection_create() shall fail and return NULL]
+				result = NULL;
+				LogError("amqp_connection_create failed (STRING_construct failed)");
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_006: [`config->underlying_io_transport` shall be saved on `instance->underlying_io_transport`]
+				instance->underlying_io_transport = config->underlying_io_transport;
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_008: [`config->is_trace_on` shall be saved on `instance->is_trace_on`]
+				instance->is_trace_on = config->is_trace_on;
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_060: [`config->on_state_changed_callback` shall be saved on `instance->on_state_changed_callback`]
+				instance->on_state_changed_callback = config->on_state_changed_callback;
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_061: [`config->on_state_changed_context` shall be saved on `instance->on_state_changed_context`]
+				instance->on_state_changed_context = config->on_state_changed_context;
+
+				instance->current_state = AMQP_CONNECTION_STATE_CLOSED;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_011: [If `config->create_sasl_io` is true or `config->create_cbs_connection` is true, amqp_connection_create() shall create SASL I/O]
+				if ((config->create_sasl_io || config->create_cbs_connection) && create_sasl_components(instance) != RESULT_OK)
+				{
+					result = NULL;
+					LogError("amqp_connection_create failed (failed creating the SASL components)");
+				}
+				else if (create_connection_handle(instance) != RESULT_OK)
+				{
+					result = NULL;
+					LogError("amqp_connection_create failed (failed creating the AMQP connection)");
+				}
+				else if (create_session_handle(instance) != RESULT_OK)
+				{
+					result = NULL;
+					LogError("amqp_connection_create failed (failed creating the AMQP session)");
+				}
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_028: [Only if `config->create_cbs_connection` is true, amqp_connection_create() shall create and open the CBS_HANDLE]
+				else if (config->create_cbs_connection && create_cbs_handle(instance) != RESULT_OK)
+				{
+					result = NULL;
+					LogError("amqp_connection_create failed (failed creating the CBS handle)");
+				}
+				else
+				{
+					
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_034: [If no failures occur, amqp_connection_create() shall return the handle to the connection state]
+					result = (AMQP_CONNECTION_HANDLE)instance;
+				}
+			}
+
+			if (result == NULL)
+			{
+				amqp_connection_destroy((AMQP_CONNECTION_HANDLE)instance);
+			}
+		}
+	}
+
+	return result;
+}
+
+void amqp_connection_do_work(AMQP_CONNECTION_HANDLE conn_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_041: [If `conn_handle` is NULL, amqp_connection_do_work() shall fail and return]
+	if (conn_handle != NULL)
+	{
+		AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_042: [connection_dowork() shall be invoked passing `instance->connection_handle`]
+		connection_dowork(instance->connection_handle);
+	}
+}
+
+int amqp_connection_get_session_handle(AMQP_CONNECTION_HANDLE conn_handle, SESSION_HANDLE* session_handle)
+{
+	int result;
+	
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_043: [If `conn_handle` is NULL, amqp_connection_get_session_handle() shall fail and return __FAILURE__]
+	if (conn_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("amqp_connection_get_session_handle failed (conn_handle is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_044: [If `session_handle` is NULL, amqp_connection_get_session_handle() shall fail and return __FAILURE__]
+	else if (session_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("amqp_connection_get_session_handle failed (session_handle is NULL)");
+	}
+	else
+	{
+		AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_045: [`session_handle` shall be set to point to `instance->session_handle`]
+		*session_handle = instance->session_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_046: [amqp_connection_get_session_handle() shall return success code 0]
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+int amqp_connection_get_cbs_handle(AMQP_CONNECTION_HANDLE conn_handle, CBS_HANDLE* cbs_handle)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_047: [If `conn_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__]
+	if (conn_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("amqp_connection_get_cbs_handle failed (conn_handle is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_048: [If `cbs_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__]
+	else if (cbs_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("amqp_connection_get_cbs_handle failed (parameter cbs_handle is NULL)");
+	}
+	else
+	{
+		AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_049: [If `instance->cbs_handle` is NULL, amqp_connection_get_cbs_handle() shall fail and return __FAILURE__]
+		if (instance->cbs_handle == NULL)
+		{
+			result = __FAILURE__;
+			LogError("amqp_connection_get_cbs_handle failed (there is not a cbs_handle to be returned)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_050: [`cbs_handle` shall be set to point to `instance->cbs_handle`]
+			*cbs_handle = instance->cbs_handle;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_051: [amqp_connection_get_cbs_handle() shall return success code 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int amqp_connection_set_logging(AMQP_CONNECTION_HANDLE conn_handle, bool is_trace_on)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_052: [If `conn_handle` is NULL, amqp_connection_set_logging() shall fail and return __FAILURE__]
+	if (conn_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("amqp_connection_set_logging failed (conn_handle is NULL)");
+	}
+	else
+	{
+		AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_053: [`instance->is_trace_on` shall be set to `is_trace_on`]
+		instance->is_trace_on = is_trace_on;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_054: [Tracing on `instance->sasl_io` shall be set to `instance->is_trace_on` if the value has changed]
+		if (instance->sasl_io != NULL &&
+			xio_setoption(instance->sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_072: [If xio_setoption() fails, amqp_connection_set_logging() shall fail and return __FAILURE__]
+			result = __FAILURE__;
+			LogError("amqp_connection_set_logging failed (xio_setoption() failed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_055: [Tracing on `instance->connection_handle` shall be set to `instance->is_trace_on` if the value has changed]
+			connection_set_trace(instance->connection_handle, instance->is_trace_on);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_056: [amqp_connection_set_logging() shall return success code 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_connection.h	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,51 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H
+#define IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H
+
+#include "azure_c_shared_utility/umock_c_prod.h"
+#include "azure_c_shared_utility/xio.h"
+#include "azure_uamqp_c/session.h"
+#include "azure_uamqp_c/cbs.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef enum AMQP_CONNECTION_STATE_TAG
+{
+	AMQP_CONNECTION_STATE_OPENED,
+	AMQP_CONNECTION_STATE_CLOSED,
+	AMQP_CONNECTION_STATE_ERROR
+} AMQP_CONNECTION_STATE;
+
+typedef void(*ON_AMQP_CONNECTION_STATE_CHANGED)(const void* context, AMQP_CONNECTION_STATE old_state, AMQP_CONNECTION_STATE new_state);
+
+typedef struct AMQP_CONNECTION_CONFIG_TAG
+{
+	const char* iothub_host_fqdn;
+	XIO_HANDLE underlying_io_transport;
+	bool create_sasl_io;
+	bool create_cbs_connection;
+	bool is_trace_on;
+		
+	ON_AMQP_CONNECTION_STATE_CHANGED on_state_changed_callback;
+	const void* on_state_changed_context;
+} AMQP_CONNECTION_CONFIG;
+
+typedef struct AMQP_CONNECTION_INSTANCE* AMQP_CONNECTION_HANDLE;
+
+MOCKABLE_FUNCTION(, AMQP_CONNECTION_HANDLE, amqp_connection_create, AMQP_CONNECTION_CONFIG*, config);
+MOCKABLE_FUNCTION(, void, amqp_connection_destroy, AMQP_CONNECTION_HANDLE, conn_handle);
+MOCKABLE_FUNCTION(, void, amqp_connection_do_work, AMQP_CONNECTION_HANDLE, conn_handle);
+MOCKABLE_FUNCTION(, int, amqp_connection_get_session_handle, AMQP_CONNECTION_HANDLE, conn_handle, SESSION_HANDLE*, session_handle);
+MOCKABLE_FUNCTION(, int, amqp_connection_get_cbs_handle, AMQP_CONNECTION_HANDLE, conn_handle, CBS_HANDLE*, cbs_handle);
+MOCKABLE_FUNCTION(, int, amqp_connection_set_logging, AMQP_CONNECTION_HANDLE, conn_handle, bool, is_trace_on);
+	
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*IOTHUBTRANSPORTAMQP_AMQP_CONNECTION_H*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_device.c	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,1351 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdlib.h>
+#include "iothubtransport_amqp_messenger.h"
+#include "azure_c_shared_utility/optimize_size.h"
+#include "azure_c_shared_utility/gballoc.h"
+#include "azure_c_shared_utility/agenttime.h" 
+#include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/strings.h"
+#include "iothubtransport_amqp_cbs_auth.h"
+#include "iothubtransport_amqp_device.h"
+
+#define RESULT_OK                                  0
+#define INDEFINITE_TIME                            ((time_t)-1)
+#define DEFAULT_AUTH_STATE_CHANGED_TIMEOUT_SECS    60
+#define DEFAULT_MSGR_STATE_CHANGED_TIMEOUT_SECS    60
+
+static const char* DEVICE_OPTION_SAVED_AUTH_OPTIONS = "saved_device_auth_options";
+static const char* DEVICE_OPTION_SAVED_MESSENGER_OPTIONS = "saved_device_messenger_options";
+
+typedef struct DEVICE_INSTANCE_TAG
+{
+	DEVICE_CONFIG* config;
+	DEVICE_STATE state;
+
+	SESSION_HANDLE session_handle;
+	CBS_HANDLE cbs_handle;
+
+	AUTHENTICATION_HANDLE authentication_handle;
+	AUTHENTICATION_STATE auth_state;
+	AUTHENTICATION_ERROR_CODE auth_error_code;
+	time_t auth_state_last_changed_time;
+	size_t auth_state_change_timeout_secs;
+
+	MESSENGER_HANDLE messenger_handle;
+	MESSENGER_STATE msgr_state;
+	time_t msgr_state_last_changed_time;
+	size_t msgr_state_change_timeout_secs;
+
+	ON_DEVICE_C2D_MESSAGE_RECEIVED on_message_received_callback;
+	void* on_message_received_context;
+} DEVICE_INSTANCE;
+
+typedef struct DEVICE_SEND_EVENT_TASK_TAG
+{
+	ON_DEVICE_D2C_EVENT_SEND_COMPLETE on_event_send_complete_callback;
+	void* on_event_send_complete_context;
+} DEVICE_SEND_EVENT_TASK;
+
+
+// Internal state control
+
+static void update_state(DEVICE_INSTANCE* instance, DEVICE_STATE new_state)
+{
+	if (new_state != instance->state)
+	{
+		DEVICE_STATE previous_state = instance->state;
+		instance->state = new_state;
+
+		if (instance->config->on_state_changed_callback != NULL)
+		{
+			instance->config->on_state_changed_callback(instance->config->on_state_changed_context, previous_state, new_state);
+		}
+	}
+}
+
+static int is_timeout_reached(time_t start_time, size_t timeout_in_secs, int *is_timed_out)
+{
+	int result;
+
+	if (start_time == INDEFINITE_TIME)
+	{
+		LogError("Failed to verify timeout (start_time is INDEFINITE)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		time_t current_time;
+
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Failed to verify timeout (get_time failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			if (get_difftime(current_time, start_time) >= timeout_in_secs)
+			{
+				*is_timed_out = 1;
+			}
+			else
+			{
+				*is_timed_out = 0;
+			}
+
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+
+// Callback Handlers
+
+static D2C_EVENT_SEND_RESULT get_d2c_event_send_result_from(MESSENGER_EVENT_SEND_COMPLETE_RESULT result)
+{
+	D2C_EVENT_SEND_RESULT d2c_esr;
+
+	switch (result)
+	{
+		case MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK:
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_OK;
+			break;
+		case MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE:
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE;
+			break;
+		case MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING:
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING;
+			break;
+		case MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT:
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT;
+			break;
+		case MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED:
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED;
+			break;
+		default:
+			// This is not expected. All states should be mapped.
+			d2c_esr = D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN;
+			break;
+	};
+
+	return d2c_esr;
+}
+
+static void on_event_send_complete_messenger_callback(IOTHUB_MESSAGE_LIST* iothub_message, MESSENGER_EVENT_SEND_COMPLETE_RESULT ev_send_comp_result, void* context)
+{
+	if (iothub_message == NULL || context == NULL)
+	{
+		LogError("on_event_send_complete_messenger_callback was invoked, but either iothub_message (%p) or context (%p) are NULL", iothub_message, context);
+	}
+	else
+	{
+		DEVICE_SEND_EVENT_TASK* send_task = (DEVICE_SEND_EVENT_TASK*)context;
+
+		// Codes_SRS_DEVICE_09_059: [If `ev_send_comp_result` is MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK, D2C_EVENT_SEND_COMPLETE_RESULT_OK shall be reported as `event_send_complete`]
+		// Codes_SRS_DEVICE_09_060: [If `ev_send_comp_result` is MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE shall be reported as `event_send_complete`]
+		// Codes_SRS_DEVICE_09_061: [If `ev_send_comp_result` is MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING shall be reported as `event_send_complete`]
+		// Codes_SRS_DEVICE_09_062: [If `ev_send_comp_result` is MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT shall be reported as `event_send_complete`]
+		// Codes_SRS_DEVICE_09_063: [If `ev_send_comp_result` is MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED, D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED shall be reported as `event_send_complete`]
+		D2C_EVENT_SEND_RESULT device_send_result = get_d2c_event_send_result_from(ev_send_comp_result);
+
+		// Codes_SRS_DEVICE_09_064: [If provided, the user callback and context saved in `send_task` shall be invoked passing the device `event_send_complete`]
+		if (send_task->on_event_send_complete_callback != NULL)
+		{
+			send_task->on_event_send_complete_callback(iothub_message, device_send_result, send_task->on_event_send_complete_context);
+		}
+
+		// Codes_SRS_DEVICE_09_065: [The memory allocated for `send_task` shall be released]
+		free(send_task);
+	}
+}
+
+static void on_authentication_error_callback(void* context, AUTHENTICATION_ERROR_CODE error_code)
+{
+	if (context == NULL)
+	{
+		LogError("on_authentication_error_callback was invoked with error %d, but context is NULL", error_code);
+	}
+	else
+	{ 
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)context;
+		instance->auth_error_code = error_code;
+	}
+}
+
+static void on_authentication_state_changed_callback(void* context, AUTHENTICATION_STATE previous_state, AUTHENTICATION_STATE new_state)
+{
+	if (context == NULL)
+	{
+		LogError("on_authentication_state_changed_callback was invoked with new_state %d, but context is NULL", new_state);
+	}
+	else if (new_state != previous_state)
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)context;
+		instance->auth_state = new_state;
+
+		if ((instance->auth_state_last_changed_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Device '%s' failed to set time of last authentication state change (get_time failed)", instance->config->device_id);
+		}
+	}
+}
+
+static void on_messenger_state_changed_callback(void* context, MESSENGER_STATE previous_state, MESSENGER_STATE new_state)
+{
+	if (context == NULL)
+	{
+		LogError("on_messenger_state_changed_callback was invoked with new_state %d, but context is NULL", new_state);
+	}
+	else if (new_state != previous_state)
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)context;
+		instance->msgr_state = new_state;
+
+		if ((instance->msgr_state_last_changed_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Device '%s' failed to set time of last messenger state change (get_time failed)", instance->config->device_id);
+		}
+	}
+}
+
+static DEVICE_MESSAGE_DISPOSITION_INFO* create_device_message_disposition_info_from(MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info)
+{
+	DEVICE_MESSAGE_DISPOSITION_INFO* device_disposition_info;
+
+	if ((device_disposition_info = (DEVICE_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(DEVICE_MESSAGE_DISPOSITION_INFO))) == NULL)
+	{
+		LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (malloc failed)");
+	}
+	else if (mallocAndStrcpy_s(&device_disposition_info->source, messenger_disposition_info->source) != RESULT_OK)
+	{
+		LogError("Failed creating DEVICE_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)");
+		free(device_disposition_info);
+		device_disposition_info = NULL;
+	}
+	else
+	{
+		device_disposition_info->message_id = messenger_disposition_info->message_id;
+	}
+
+	return device_disposition_info;
+}
+
+static void destroy_device_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info)
+{
+	free(disposition_info->source);
+	free(disposition_info);
+}
+
+static MESSENGER_MESSAGE_DISPOSITION_INFO* create_messenger_disposition_info(DEVICE_MESSAGE_DISPOSITION_INFO* device_disposition_info)
+{
+	MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info;
+
+	if ((messenger_disposition_info = (MESSENGER_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(MESSENGER_MESSAGE_DISPOSITION_INFO))) == NULL)
+	{
+		LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO (malloc failed)");
+	}
+	else if (mallocAndStrcpy_s(&messenger_disposition_info->source, device_disposition_info->source) != RESULT_OK)
+	{
+		LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO (mallocAndStrcpy_s failed)");
+		free(messenger_disposition_info);
+		messenger_disposition_info = NULL;
+	}
+	else
+	{
+		messenger_disposition_info->message_id = device_disposition_info->message_id;
+	}
+
+	return messenger_disposition_info;
+}
+
+static void destroy_messenger_disposition_info(MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info)
+{
+	free(messenger_disposition_info->source);
+	free(messenger_disposition_info);
+}
+
+static MESSENGER_DISPOSITION_RESULT get_messenger_message_disposition_result_from(DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result)
+{
+	MESSENGER_DISPOSITION_RESULT messenger_disposition_result;
+
+	switch (device_disposition_result)
+	{
+		case DEVICE_MESSAGE_DISPOSITION_RESULT_NONE:
+			messenger_disposition_result = MESSENGER_DISPOSITION_RESULT_NONE;
+			break;
+		case DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED:
+			messenger_disposition_result = MESSENGER_DISPOSITION_RESULT_ACCEPTED;
+			break;
+		case DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED:
+			messenger_disposition_result = MESSENGER_DISPOSITION_RESULT_REJECTED;
+			break;
+		case DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED:
+			messenger_disposition_result = MESSENGER_DISPOSITION_RESULT_RELEASED;
+			break;
+		default:
+			LogError("Failed to get the corresponding MESSENGER_DISPOSITION_RESULT (%d is not supported)", device_disposition_result);
+			messenger_disposition_result = MESSENGER_DISPOSITION_RESULT_RELEASED;
+			break;
+	}
+
+	return messenger_disposition_result;
+}
+
+static MESSENGER_DISPOSITION_RESULT on_messenger_message_received_callback(IOTHUB_MESSAGE_HANDLE iothub_message_handle, MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context)
+{
+	MESSENGER_DISPOSITION_RESULT msgr_disposition_result;
+
+	// Codes_SRS_DEVICE_09_070: [If `iothub_message_handle` or `context` is NULL, on_messenger_message_received_callback shall return MESSENGER_DISPOSITION_RESULT_RELEASED]
+	if (iothub_message_handle == NULL || context == NULL)
+	{
+		LogError("Failed receiving incoming C2D message (message handle (%p) or context (%p) is NULL)", iothub_message_handle, context);
+		msgr_disposition_result = MESSENGER_DISPOSITION_RESULT_RELEASED;
+	}
+	else
+	{
+		DEVICE_INSTANCE* device_instance = (DEVICE_INSTANCE*)context;
+
+		if (device_instance->on_message_received_callback == NULL)
+		{
+			LogError("Device '%s' failed receiving incoming C2D message (callback is NULL)", device_instance->config->device_id);
+			msgr_disposition_result = MESSENGER_DISPOSITION_RESULT_RELEASED;
+		}
+		else
+		{
+			DEVICE_MESSAGE_DISPOSITION_INFO* device_message_disposition_info;
+
+			// Codes_SRS_DEVICE_09_119: [A DEVICE_MESSAGE_DISPOSITION_INFO instance shall be created containing a copy of `disposition_info->source` and `disposition_info->message_id`]
+			if ((device_message_disposition_info = create_device_message_disposition_info_from(disposition_info)) == NULL)
+			{
+				// Codes_SRS_DEVICE_09_120: [If the DEVICE_MESSAGE_DISPOSITION_INFO instance fails to be created, on_messenger_message_received_callback shall return MESSENGER_DISPOSITION_RESULT_RELEASED]
+				LogError("Device '%s' failed receiving incoming C2D message (failed creating DEVICE_MESSAGE_DISPOSITION_INFO)", device_instance->config->device_id);
+				msgr_disposition_result = MESSENGER_DISPOSITION_RESULT_RELEASED;
+			}
+			else
+			{
+				// Codes_SRS_DEVICE_09_071: [The user callback shall be invoked, passing the context it provided]
+				DEVICE_MESSAGE_DISPOSITION_RESULT device_disposition_result = device_instance->on_message_received_callback(iothub_message_handle, device_message_disposition_info, device_instance->on_message_received_context);
+
+				// Codes_SRS_DEVICE_09_072: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED, on_messenger_message_received_callback shall return MESSENGER_DISPOSITION_RESULT_ACCEPTED]
+				// Codes_SRS_DEVICE_09_073: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED, on_messenger_message_received_callback shall return MESSENGER_DISPOSITION_RESULT_REJECTED]
+				// Codes_SRS_DEVICE_09_074: [If the user callback returns DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED, on_messenger_message_received_callback shall return MESSENGER_DISPOSITION_RESULT_RELEASED]
+				msgr_disposition_result = get_messenger_message_disposition_result_from(device_disposition_result);
+
+				// Codes_SRS_DEVICE_09_121: [on_messenger_message_received_callback shall release the memory allocated for DEVICE_MESSAGE_DISPOSITION_INFO]
+				destroy_device_disposition_info(device_message_disposition_info);
+			}
+		}
+	}
+
+	return msgr_disposition_result;
+}
+
+
+// Configuration Helpers
+
+static void destroy_device_config(DEVICE_CONFIG* config)
+{
+	if (config != NULL)
+	{
+		free(config->device_id);
+		free(config->iothub_host_fqdn);
+		free(config->device_primary_key);
+		free(config->device_secondary_key);
+		free(config->device_sas_token);
+		free(config);
+	}
+}
+
+static DEVICE_CONFIG* clone_device_config(DEVICE_CONFIG *config)
+{
+	DEVICE_CONFIG* new_config;
+
+	if ((new_config = (DEVICE_CONFIG*)malloc(sizeof(DEVICE_CONFIG))) == NULL)
+	{
+		LogError("Failed copying the DEVICE_CONFIG (malloc failed)");
+	}
+	else
+	{
+		int result;
+
+		memset(new_config, 0, sizeof(DEVICE_CONFIG));
+
+		if (mallocAndStrcpy_s(&new_config->device_id, config->device_id) != RESULT_OK)
+		{
+			LogError("Failed copying the DEVICE_CONFIG (failed copying device_id)");
+			result = __FAILURE__;
+		}
+		else if (mallocAndStrcpy_s(&new_config->iothub_host_fqdn, config->iothub_host_fqdn) != RESULT_OK)
+		{
+			LogError("Failed copying the DEVICE_CONFIG (failed copying iothub_host_fqdn)");
+			result = __FAILURE__;
+		}
+		else if (config->device_sas_token != NULL &&
+			mallocAndStrcpy_s(&new_config->device_sas_token, config->device_sas_token) != RESULT_OK)
+		{
+			LogError("Failed copying the DEVICE_CONFIG (failed copying device_sas_token)");
+			result = __FAILURE__;
+		}
+		else if (config->device_primary_key != NULL &&
+			mallocAndStrcpy_s(&new_config->device_primary_key, config->device_primary_key) != RESULT_OK)
+		{
+			LogError("Failed copying the DEVICE_CONFIG (failed copying device_primary_key)");
+			result = __FAILURE__;
+		}
+		else if (config->device_secondary_key != NULL &&
+			mallocAndStrcpy_s(&new_config->device_secondary_key, config->device_secondary_key) != RESULT_OK)
+		{
+			LogError("Failed copying the DEVICE_CONFIG (failed copying device_secondary_key)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			new_config->authentication_mode = config->authentication_mode;
+			new_config->on_state_changed_callback = config->on_state_changed_callback;
+			new_config->on_state_changed_context = config->on_state_changed_context;
+
+			result = RESULT_OK;
+		}
+
+		if (result != RESULT_OK)
+		{
+			destroy_device_config(new_config);
+			new_config = NULL;
+		}
+	}
+
+	return new_config;
+}
+
+static void set_authentication_config(DEVICE_INSTANCE* device_instance, AUTHENTICATION_CONFIG* auth_config)
+{
+	DEVICE_CONFIG *device_config = device_instance->config;
+
+	auth_config->device_id = device_config->device_id;
+	auth_config->iothub_host_fqdn = device_config->iothub_host_fqdn;
+	auth_config->on_error_callback = on_authentication_error_callback;
+	auth_config->on_error_callback_context = device_instance;
+	auth_config->on_state_changed_callback = on_authentication_state_changed_callback;
+	auth_config->on_state_changed_callback_context = device_instance;
+	auth_config->device_primary_key = device_config->device_primary_key;
+	auth_config->device_secondary_key = device_config->device_secondary_key;
+	auth_config->device_sas_token = device_config->device_sas_token;
+}
+
+
+// Create and Destroy Helpers
+
+static void internal_destroy_device(DEVICE_INSTANCE* instance)
+{
+	if (instance != NULL)
+	{
+		if (instance->messenger_handle != NULL)
+		{
+			messenger_destroy(instance->messenger_handle);
+		}
+
+		if (instance->authentication_handle != NULL)
+		{
+			authentication_destroy(instance->authentication_handle);
+		}
+
+		destroy_device_config(instance->config);
+		free(instance);
+	}
+}
+
+static int create_authentication_instance(DEVICE_INSTANCE *instance)
+{
+	int result;
+	AUTHENTICATION_CONFIG auth_config;
+
+	set_authentication_config(instance, &auth_config);
+
+	if ((instance->authentication_handle = authentication_create(&auth_config)) == NULL)
+	{
+		LogError("Failed creating the AUTHENTICATION_HANDLE (authentication_create failed)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+static int create_messenger_instance(DEVICE_INSTANCE* instance)
+{
+	int result;
+
+	MESSENGER_CONFIG messenger_config;
+	messenger_config.device_id = instance->config->device_id;
+	messenger_config.iothub_host_fqdn = instance->config->iothub_host_fqdn;
+	messenger_config.on_state_changed_callback = on_messenger_state_changed_callback;
+	messenger_config.on_state_changed_context = instance;
+
+	if ((instance->messenger_handle = messenger_create(&messenger_config)) == NULL)
+	{
+		LogError("Failed creating the MESSENGER_HANDLE (messenger_create failed)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+// ---------- Set/Retrieve Options Helpers ----------//
+
+static void* device_clone_option(const char* name, const void* value)
+{
+	void* result;
+
+	if (name == NULL || value == NULL)
+	{
+		LogError("Failed to clone device option (either name (%p) or value (%p) is NULL)", name, value);
+		result = NULL;
+	}
+	else
+	{
+		if (strcmp(DEVICE_OPTION_SAVED_AUTH_OPTIONS, name) == 0 ||
+			strcmp(DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, name) == 0)
+		{
+			if ((result = (void*)OptionHandler_Clone((OPTIONHANDLER_HANDLE)value)) == NULL)
+			{
+				LogError("Failed to clone device option (OptionHandler_Clone failed for option %s)", name);
+			}
+		}
+		else
+		{
+			LogError("Failed to clone device option (option with name '%s' is not suppported)", name);
+			result = NULL;
+		}
+	}
+
+	return result;
+}
+
+static void device_destroy_option(const char* name, const void* value)
+{
+	if (name == NULL || value == NULL)
+	{
+		LogError("Failed to destroy device option (either name (%p) or value (%p) is NULL)", name, value);
+	}
+	else
+	{
+		if (strcmp(name, DEVICE_OPTION_SAVED_AUTH_OPTIONS) == 0 ||
+			strcmp(name, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS) == 0)
+		{
+			OptionHandler_Destroy((OPTIONHANDLER_HANDLE)value);
+		}
+		else
+		{
+			LogError("Failed to clone device option (option with name '%s' is not suppported)", name);
+		}
+	}
+}
+
+
+// Public APIs:
+
+DEVICE_HANDLE device_create(DEVICE_CONFIG *config)
+{
+	DEVICE_INSTANCE *instance;
+
+	// Codes_SRS_DEVICE_09_001: [If `config` or device_id or iothub_host_fqdn or on_state_changed_callback are NULL then device_create shall fail and return NULL]
+	if (config == NULL)
+	{
+		LogError("Failed creating the device instance (config is NULL)");
+		instance = NULL;
+	}
+	else if (config->device_id == NULL)
+	{
+		LogError("Failed creating the device instance (config->device_id is NULL)");
+		instance = NULL;
+	}
+	else if (config->iothub_host_fqdn == NULL)
+	{
+		LogError("Failed creating the device instance (config->iothub_host_fqdn is NULL)");
+		instance = NULL;
+	}
+	else if (config->on_state_changed_callback == NULL)
+	{
+		LogError("Failed creating the device instance (config->on_state_changed_callback is NULL)");
+		instance = NULL;
+	}
+	// Codes_SRS_DEVICE_09_002: [device_create shall allocate memory for the device instance structure]
+	else if ((instance = (DEVICE_INSTANCE*)malloc(sizeof(DEVICE_INSTANCE))) == NULL)
+	{
+		// Codes_SRS_DEVICE_09_003: [If malloc fails, device_create shall fail and return NULL]
+		LogError("Failed creating the device instance (malloc failed)");
+	}
+	else
+	{
+		int result;
+
+		memset(instance, 0, sizeof(DEVICE_INSTANCE));
+
+		// Codes_SRS_DEVICE_09_004: [All `config` parameters shall be saved into `instance`]
+		if ((instance->config = clone_device_config(config)) == NULL)
+		{
+			// Codes_SRS_DEVICE_09_005: [If any `config` parameters fail to be saved into `instance`, device_create shall fail and return NULL]
+			LogError("Failed creating the device instance for device '%s' (failed copying the configuration)", config->device_id);
+			result = __FAILURE__;
+		}
+		// Codes_SRS_DEVICE_09_006: [If `instance->authentication_mode` is DEVICE_AUTH_MODE_CBS, `instance->authentication_handle` shall be set using authentication_create()]
+		else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS &&
+			     create_authentication_instance(instance) != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_007: [If the AUTHENTICATION_HANDLE fails to be created, device_create shall fail and return NULL]
+			LogError("Failed creating the device instance for device '%s' (failed creating the authentication instance)", instance->config->device_id);
+			result = __FAILURE__;
+		}
+		// Codes_SRS_DEVICE_09_008: [`instance->messenger_handle` shall be set using messenger_create()]
+		else if (create_messenger_instance(instance) != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_009: [If the MESSENGER_HANDLE fails to be created, device_create shall fail and return NULL]
+			LogError("Failed creating the device instance for device '%s' (failed creating the messenger instance)", instance->config->device_id);
+			result = __FAILURE__;
+		}
+		else
+		{
+			instance->auth_state = AUTHENTICATION_STATE_STOPPED;
+			instance->msgr_state = MESSENGER_STATE_STOPPED;
+			instance->state = DEVICE_STATE_STOPPED;
+			instance->auth_state_last_changed_time = INDEFINITE_TIME;
+			instance->auth_state_change_timeout_secs = DEFAULT_AUTH_STATE_CHANGED_TIMEOUT_SECS;
+			instance->msgr_state_last_changed_time = INDEFINITE_TIME;
+			instance->msgr_state_change_timeout_secs = DEFAULT_MSGR_STATE_CHANGED_TIMEOUT_SECS;
+
+			result = RESULT_OK;
+		}
+
+		if (result != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_010: [If device_create fails it shall release all memory it has allocated]
+			internal_destroy_device(instance);
+			instance = NULL;
+		}
+	}
+
+	// Codes_SRS_DEVICE_09_011: [If device_create succeeds it shall return a handle to its `instance` structure]
+	return (DEVICE_HANDLE)instance;
+}
+
+int device_start_async(DEVICE_HANDLE handle, SESSION_HANDLE session_handle, CBS_HANDLE cbs_handle)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_017: [If `handle` is NULL, device_start_async shall return a non-zero result]
+	if (handle == NULL)
+	{
+		LogError("Failed starting device (handle is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_DEVICE_09_018: [If the device state is not DEVICE_STATE_STOPPED, device_start_async shall return a non-zero result]
+		if (instance->state != DEVICE_STATE_STOPPED)
+		{
+			LogError("Failed starting device (device is not stopped)");
+			result = __FAILURE__;
+		}
+		// Codes_SRS_DEVICE_09_019: [If `session_handle` is NULL, device_start_async shall return a non-zero result]
+		else if (session_handle == NULL)
+		{
+			LogError("Failed starting device (session_handle is NULL)");
+			result = __FAILURE__;
+		}
+		// Codes_SRS_DEVICE_09_020: [If using CBS authentication and `cbs_handle` is NULL, device_start_async shall return a non-zero result]
+		else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS && cbs_handle == NULL)
+		{
+			LogError("Failed starting device (device using CBS authentication, but cbs_handle is NULL)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_DEVICE_09_021: [`session_handle` and `cbs_handle` shall be saved into the `instance`]
+			instance->session_handle = session_handle;
+			instance->cbs_handle = cbs_handle;
+
+			// Codes_SRS_DEVICE_09_022: [The device state shall be updated to DEVICE_STATE_STARTING, and state changed callback invoked]
+			update_state(instance, DEVICE_STATE_STARTING);
+
+			// Codes_SRS_DEVICE_09_023: [If no failures occur, device_start_async shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+// @brief
+//     stops a device instance (stops messenger and authentication) synchronously.
+// @returns
+//     0 if the function succeeds, non-zero otherwise.
+int device_stop(DEVICE_HANDLE handle)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_024: [If `handle` is NULL, device_stop shall return a non-zero result]
+	if (handle == NULL)
+	{
+		LogError("Failed stopping device (handle is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_DEVICE_09_025: [If the device state is already DEVICE_STATE_STOPPED or DEVICE_STATE_STOPPING, device_stop shall return a non-zero result]
+		if (instance->state == DEVICE_STATE_STOPPED || instance->state == DEVICE_STATE_STOPPING)
+		{
+			LogError("Failed stopping device '%s' (device is already stopped or stopping)", instance->config->device_id);
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_DEVICE_09_026: [The device state shall be updated to DEVICE_STATE_STOPPING, and state changed callback invoked]
+			update_state(instance, DEVICE_STATE_STOPPING);
+
+			// Codes_SRS_DEVICE_09_027: [If `instance->messenger_handle` state is not MESSENGER_STATE_STOPPED, messenger_stop shall be invoked]
+			if (instance->msgr_state != MESSENGER_STATE_STOPPED && 
+				instance->msgr_state != MESSENGER_STATE_STOPPING &&
+				messenger_stop(instance->messenger_handle) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_028: [If messenger_stop fails, the `instance` state shall be updated to DEVICE_STATE_ERROR_MSG and the function shall return non-zero result]
+				LogError("Failed stopping device '%s' (messenger_stop failed)", instance->config->device_id);
+				result = __FAILURE__;
+				update_state(instance, DEVICE_STATE_ERROR_MSG);
+			}
+			// Codes_SRS_DEVICE_09_029: [If CBS authentication is used, if `instance->authentication_handle` state is not AUTHENTICATION_STATE_STOPPED, authentication_stop shall be invoked]
+			else if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS &&
+				instance->auth_state != AUTHENTICATION_STATE_STOPPED &&
+				authentication_stop(instance->authentication_handle) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_030: [If authentication_stop fails, the `instance` state shall be updated to DEVICE_STATE_ERROR_AUTH and the function shall return non-zero result]
+				LogError("Failed stopping device '%s' (authentication_stop failed)", instance->config->device_id);
+				result = __FAILURE__;
+				update_state(instance, DEVICE_STATE_ERROR_AUTH);
+			}
+			else
+			{
+				// Codes_SRS_DEVICE_09_031: [The device state shall be updated to DEVICE_STATE_STOPPED, and state changed callback invoked]
+				update_state(instance, DEVICE_STATE_STOPPED);
+
+				// Codes_SRS_DEVICE_09_032: [If no failures occur, device_stop shall return 0]
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	return result;
+}
+
+void device_do_work(DEVICE_HANDLE handle)
+{
+	// Codes_SRS_DEVICE_09_033: [If `handle` is NULL, device_do_work shall return]
+	if (handle == NULL)
+	{
+		LogError("Failed to perform device_do_work (handle is NULL)");
+	}
+	else
+	{
+		// Cranking the state monster:
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		if (instance->state == DEVICE_STATE_STARTING)
+		{
+			// Codes_SRS_DEVICE_09_034: [If CBS authentication is used and authentication state is AUTHENTICATION_STATE_STOPPED, authentication_start shall be invoked]
+			if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS)
+			{
+				if (instance->auth_state == AUTHENTICATION_STATE_STOPPED)
+				{
+					if (authentication_start(instance->authentication_handle, instance->cbs_handle) != RESULT_OK)
+					{
+						// Codes_SRS_DEVICE_09_035: [If authentication_start fails, the device state shall be updated to DEVICE_STATE_ERROR_AUTH]
+						LogError("Device '%s' failed to be authenticated (authentication_start failed)", instance->config->device_id);
+
+						update_state(instance, DEVICE_STATE_ERROR_AUTH);
+					}
+				}
+				// Codes_SRS_DEVICE_09_036: [If authentication state is AUTHENTICATION_STATE_STARTING, the device shall track the time since last event change and timeout if needed]
+				else if (instance->auth_state == AUTHENTICATION_STATE_STARTING)
+				{
+					int is_timed_out;
+					if (is_timeout_reached(instance->auth_state_last_changed_time, instance->auth_state_change_timeout_secs, &is_timed_out) != RESULT_OK)
+					{
+						LogError("Device '%s' failed verifying the timeout for authentication start (is_timeout_reached failed)", instance->config->device_id);
+						update_state(instance, DEVICE_STATE_ERROR_AUTH);
+					}
+					// Codes_SRS_DEVICE_09_037: [If authentication_start times out, the device state shall be updated to DEVICE_STATE_ERROR_AUTH_TIMEOUT]
+					else if (is_timed_out == 1)
+					{
+						LogError("Device '%s' authentication did not complete starting within expected timeout (%d)", instance->config->device_id, instance->auth_state_change_timeout_secs);
+
+						update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT);
+					}
+				}
+				else if (instance->auth_state == AUTHENTICATION_STATE_ERROR)
+				{
+					// Codes_SRS_DEVICE_09_038: [If authentication state is AUTHENTICATION_STATE_ERROR and error code is AUTH_FAILED, the device state shall be updated to DEVICE_STATE_ERROR_AUTH]
+					if (instance->auth_error_code == AUTHENTICATION_ERROR_AUTH_FAILED)
+					{
+						update_state(instance, DEVICE_STATE_ERROR_AUTH);
+					}
+					// Codes_SRS_DEVICE_09_039: [If authentication state is AUTHENTICATION_STATE_ERROR and error code is TIMEOUT, the device state shall be updated to DEVICE_STATE_ERROR_AUTH_TIMEOUT]
+					else // DEVICE_STATE_ERROR_TIMEOUT
+					{
+						update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT);
+					}
+				}
+				// There is no AUTHENTICATION_STATE_STOPPING
+			}
+
+			// Codes_SRS_DEVICE_09_040: [Messenger shall not be started if using CBS authentication and authentication start has not completed yet]
+			if (instance->config->authentication_mode == DEVICE_AUTH_MODE_X509 || instance->auth_state == AUTHENTICATION_STATE_STARTED)
+			{
+				// Codes_SRS_DEVICE_09_041: [If messenger state is MESSENGER_STATE_STOPPED, messenger_start shall be invoked]
+				if (instance->msgr_state == MESSENGER_STATE_STOPPED)
+				{
+					// Codes_SRS_DEVICE_09_042: [If messenger_start fails, the device state shall be updated to DEVICE_STATE_ERROR_MSG]
+					if (messenger_start(instance->messenger_handle, instance->session_handle) != RESULT_OK)
+					{
+						LogError("Device '%s' messenger failed to be started (messenger_start failed)", instance->config->device_id);
+
+						update_state(instance, DEVICE_STATE_ERROR_MSG);
+					}
+				}
+				// Codes_SRS_DEVICE_09_043: [If messenger state is MESSENGER_STATE_STARTING, the device shall track the time since last event change and timeout if needed]
+				else if (instance->msgr_state == MESSENGER_STATE_STARTING)
+				{
+					int is_timed_out;
+					if (is_timeout_reached(instance->msgr_state_last_changed_time, instance->msgr_state_change_timeout_secs, &is_timed_out) != RESULT_OK)
+					{
+						LogError("Device '%s' failed verifying the timeout for messenger start (is_timeout_reached failed)", instance->config->device_id);
+
+						update_state(instance, DEVICE_STATE_ERROR_MSG);
+					}
+					// Codes_SRS_DEVICE_09_044: [If messenger_start times out, the device state shall be updated to DEVICE_STATE_ERROR_MSG]
+					else if (is_timed_out == 1)
+					{
+						LogError("Device '%s' messenger did not complete starting within expected timeout (%d)", instance->config->device_id, instance->msgr_state_change_timeout_secs);
+
+						update_state(instance, DEVICE_STATE_ERROR_MSG);
+					}
+				}
+				// Codes_SRS_DEVICE_09_045: [If messenger state is MESSENGER_STATE_ERROR, the device state shall be updated to DEVICE_STATE_ERROR_MSG]
+				else if (instance->msgr_state == MESSENGER_STATE_ERROR)
+				{
+					LogError("Device '%s' messenger failed to be started (messenger got into error state)", instance->config->device_id);
+					
+					update_state(instance, DEVICE_STATE_ERROR_MSG);
+				}
+				// Codes_SRS_DEVICE_09_046: [If messenger state is MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_STARTED]
+				else if (instance->msgr_state == MESSENGER_STATE_STARTED)
+				{
+					update_state(instance, DEVICE_STATE_STARTED);
+				}
+			}
+		}
+		else if (instance->state == DEVICE_STATE_STARTED)
+		{
+			// Codes_SRS_DEVICE_09_047: [If CBS authentication is used and authentication state is not AUTHENTICATION_STATE_STARTED, the device state shall be updated to DEVICE_STATE_ERROR_AUTH]
+			if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS &&
+				instance->auth_state != AUTHENTICATION_STATE_STARTED)
+			{
+				LogError("Device '%s' is started but authentication reported unexpected state %d", instance->config->device_id, instance->auth_state);
+
+				if (instance->auth_state != AUTHENTICATION_STATE_ERROR)
+				{
+					if (instance->auth_error_code == AUTHENTICATION_ERROR_AUTH_FAILED)
+					{
+						update_state(instance, DEVICE_STATE_ERROR_AUTH);
+					}
+					else // AUTHENTICATION_ERROR_AUTH_TIMEOUT
+					{
+						update_state(instance, DEVICE_STATE_ERROR_AUTH_TIMEOUT);
+					}
+				}
+				else
+				{
+					update_state(instance, DEVICE_STATE_ERROR_AUTH);
+				}
+			}
+			else
+			{
+				// Codes_SRS_DEVICE_09_048: [If messenger state is not MESSENGER_STATE_STARTED, the device state shall be updated to DEVICE_STATE_ERROR_MSG]
+				if (instance->msgr_state != MESSENGER_STATE_STARTED)
+				{
+					LogError("Device '%s' is started but messenger reported unexpected state %d", instance->config->device_id, instance->msgr_state);
+					update_state(instance, DEVICE_STATE_ERROR_MSG);
+				}
+			}
+		}
+
+		// Invoking the do_works():
+		if (instance->config->authentication_mode == DEVICE_AUTH_MODE_CBS)
+		{
+			if (instance->auth_state != AUTHENTICATION_STATE_STOPPED && instance->auth_state != AUTHENTICATION_STATE_ERROR)
+			{
+				// Codes_SRS_DEVICE_09_049: [If CBS is used for authentication and `instance->authentication_handle` state is not STOPPED or ERROR, authentication_do_work shall be invoked]
+				authentication_do_work(instance->authentication_handle);
+			}
+		}
+
+		if (instance->msgr_state != MESSENGER_STATE_STOPPED && instance->msgr_state != MESSENGER_STATE_ERROR)
+		{
+			// Codes_SRS_DEVICE_09_050: [If `instance->messenger_handle` state is not STOPPED or ERROR, authentication_do_work shall be invoked]
+			messenger_do_work(instance->messenger_handle);
+		}
+	}
+}
+
+void device_destroy(DEVICE_HANDLE handle)
+{
+	// Codes_SRS_DEVICE_09_012: [If `handle` is NULL, device_destroy shall return]
+	if (handle == NULL)
+	{
+		LogError("Failed destroying device handle (handle is NULL)");
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+		// Codes_SRS_DEVICE_09_013: [If the device is in state DEVICE_STATE_STARTED or DEVICE_STATE_STARTING, device_stop() shall be invoked]
+		if (instance->state == DEVICE_STATE_STARTED || instance->state == DEVICE_STATE_STARTING)
+		{
+			(void)device_stop((DEVICE_HANDLE)instance);
+		}
+
+		// Codes_SRS_DEVICE_09_014: [`instance->messenger_handle shall be destroyed using messenger_destroy()`]
+		// Codes_SRS_DEVICE_09_015: [If created, `instance->authentication_handle` shall be destroyed using authentication_destroy()`]
+		// Codes_SRS_DEVICE_09_016: [The contents of `instance->config` shall be detroyed and then it shall be freed]
+		internal_destroy_device((DEVICE_INSTANCE*)handle);
+	}
+}
+
+int device_send_event_async(DEVICE_HANDLE handle, IOTHUB_MESSAGE_LIST* message, ON_DEVICE_D2C_EVENT_SEND_COMPLETE on_device_d2c_event_send_complete_callback, void* context)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_051: [If `handle` are `message` are NULL, device_send_event_async shall return a non-zero result]
+	if (handle == NULL || message == NULL)
+	{
+		LogError("Failed sending event (either handle (%p) or message (%p) are NULL)", handle, message);
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_SEND_EVENT_TASK* send_task;
+
+		// Codes_SRS_DEVICE_09_052: [A structure (`send_task`) shall be created to track the send state of the message]
+		if ((send_task = (DEVICE_SEND_EVENT_TASK*)malloc(sizeof(DEVICE_SEND_EVENT_TASK))) == NULL)
+		{
+			// Codes_SRS_DEVICE_09_053: [If `send_task` fails to be created, device_send_event_async shall return a non-zero value]
+			LogError("Failed sending event (failed creating task to send event)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+			// Codes_SRS_DEVICE_09_054: [`send_task` shall contain the user callback and the context provided]
+			memset(send_task, 0, sizeof(DEVICE_SEND_EVENT_TASK));
+			send_task->on_event_send_complete_callback = on_device_d2c_event_send_complete_callback;
+			send_task->on_event_send_complete_context = context;
+			
+			// Codes_SRS_DEVICE_09_055: [The message shall be sent using messenger_send_async, passing `on_event_send_complete_messenger_callback` and `send_task`]
+			if (messenger_send_async(instance->messenger_handle, message, on_event_send_complete_messenger_callback, (void*)send_task) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_056: [If messenger_send_async fails, device_send_event_async shall return a non-zero value]
+				LogError("Failed sending event (messenger_send_async failed)");
+				// Codes_SRS_DEVICE_09_057: [If any failures occur, device_send_event_async shall release all memory it has allocated]
+				free(send_task);
+				result = __FAILURE__;
+			}
+			else
+			{	
+				// Codes_SRS_DEVICE_09_058: [If no failures occur, device_send_event_async shall return 0]
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	return result;
+}
+
+int device_get_send_status(DEVICE_HANDLE handle, DEVICE_SEND_STATUS *send_status)
+{
+	int result;
+
+
+	// Codes_SRS_DEVICE_09_105: [If `handle` or `send_status` is NULL, device_get_send_status shall return a non-zero result]
+	if (handle == NULL || send_status == NULL)
+	{
+		LogError("Failed getting the device messenger send status (NULL parameter received; handle=%p, send_status=%p)", handle, send_status);
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+		MESSENGER_SEND_STATUS messenger_send_status;
+		
+		// Codes_SRS_DEVICE_09_106: [The status of `instance->messenger_handle` shall be obtained using messenger_get_send_status]
+		if (messenger_get_send_status(instance->messenger_handle, &messenger_send_status) != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_107: [If messenger_get_send_status fails, device_get_send_status shall return a non-zero result]
+			LogError("Failed getting the device messenger send status (messenger_get_send_status failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_DEVICE_09_108: [If messenger_get_send_status returns MESSENGER_SEND_STATUS_IDLE, device_get_send_status return status DEVICE_SEND_STATUS_IDLE]
+			if (messenger_send_status == MESSENGER_SEND_STATUS_IDLE)
+			{
+				*send_status = DEVICE_SEND_STATUS_IDLE;
+			}
+			// Codes_SRS_DEVICE_09_109: [If messenger_get_send_status returns MESSENGER_SEND_STATUS_BUSY, device_get_send_status return status DEVICE_SEND_STATUS_BUSY]
+			else // i.e., messenger_send_status == MESSENGER_SEND_STATUS_BUSY
+			{
+				*send_status = DEVICE_SEND_STATUS_BUSY;
+			}
+
+			// Codes_SRS_DEVICE_09_110: [If device_get_send_status succeeds, it shall return zero as result]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int device_subscribe_message(DEVICE_HANDLE handle, ON_DEVICE_C2D_MESSAGE_RECEIVED on_message_received_callback, void* context)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_066: [If `handle` or `on_message_received_callback` or `context` is NULL, device_subscribe_message shall return a non-zero result]
+	if (handle == NULL || on_message_received_callback == NULL || context == NULL)
+	{
+		LogError("Failed subscribing to C2D messages (either handle (%p), on_message_received_callback (%p) or context (%p) is NULL)",
+			handle, on_message_received_callback, context);
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_DEVICE_09_067: [messenger_subscribe_for_messages shall be invoked passing `on_messenger_message_received_callback` and the user callback and context]
+		if (messenger_subscribe_for_messages(instance->messenger_handle, on_messenger_message_received_callback, handle) != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_068: [If messenger_subscribe_for_messages fails, device_subscribe_message shall return a non-zero result]
+			LogError("Failed subscribing to C2D messages (messenger_subscribe_for_messages failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			instance->on_message_received_callback = on_message_received_callback;
+			instance->on_message_received_context = context;
+
+			// Codes_SRS_DEVICE_09_069: [If no failures occur, device_subscribe_message shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int device_unsubscribe_message(DEVICE_HANDLE handle)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_076: [If `handle` is NULL, device_unsubscribe_message shall return a non-zero result]
+	if (handle == NULL)
+	{
+		LogError("Failed unsubscribing to C2D messages (handle is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		// Codes_SRS_DEVICE_09_077: [messenger_unsubscribe_for_messages shall be invoked passing `instance->messenger_handle`]
+		if (messenger_unsubscribe_for_messages(instance->messenger_handle) != RESULT_OK)
+		{
+			// Codes_SRS_DEVICE_09_078: [If messenger_unsubscribe_for_messages fails, device_unsubscribe_message shall return a non-zero result]
+			LogError("Failed unsubscribing to C2D messages (messenger_unsubscribe_for_messages failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			// Codes_SRS_DEVICE_09_079: [If no failures occur, device_unsubscribe_message shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int device_send_message_disposition(DEVICE_HANDLE device_handle, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, DEVICE_MESSAGE_DISPOSITION_RESULT disposition_result)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_111: [If `device_handle` or `disposition_info` are NULL, device_send_message_disposition() shall fail and return __FAILURE__]
+	if (device_handle == NULL || disposition_info == NULL)
+	{
+		LogError("Failed sending message disposition (either device_handle (%p) or disposition_info (%p) are NULL)", device_handle, disposition_info);
+		result = __FAILURE__;
+	}
+	// Codes_SRS_DEVICE_09_112: [If `disposition_info->source` is NULL, device_send_message_disposition() shall fail and return __FAILURE__]  
+	else if (disposition_info->source == NULL)
+	{
+		LogError("Failed sending message disposition (disposition_info->source is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* device = (DEVICE_INSTANCE*)device_handle;
+		MESSENGER_MESSAGE_DISPOSITION_INFO* messenger_disposition_info;
+
+		// Codes_SRS_DEVICE_09_113: [A MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be created with a copy of the `source` and `message_id` contained in `disposition_info`]  
+		if ((messenger_disposition_info = create_messenger_disposition_info(disposition_info)) == NULL)
+		{
+			// Codes_SRS_DEVICE_09_114: [If the MESSENGER_MESSAGE_DISPOSITION_INFO fails to be created, device_send_message_disposition() shall fail and return __FAILURE__]  
+			LogError("Failed sending message disposition (failed to create MESSENGER_MESSAGE_DISPOSITION_INFO)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			MESSENGER_DISPOSITION_RESULT messenger_disposition_result = get_messenger_message_disposition_result_from(disposition_result);
+
+			// Codes_SRS_DEVICE_09_115: [`messenger_send_message_disposition()` shall be invoked passing the MESSENGER_MESSAGE_DISPOSITION_INFO instance and the corresponding MESSENGER_DISPOSITION_RESULT]  
+			if (messenger_send_message_disposition(device->messenger_handle, messenger_disposition_info, messenger_disposition_result) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_116: [If `messenger_send_message_disposition()` fails, device_send_message_disposition() shall fail and return __FAILURE__]  
+				LogError("Failed sending message disposition (messenger_send_message_disposition failed)");
+				result = __FAILURE__;
+			}
+			else
+			{
+				// Codes_SRS_DEVICE_09_118: [If no failures occurr, device_send_message_disposition() shall return 0]  
+				result = RESULT_OK;
+			}
+
+			// Codes_SRS_DEVICE_09_117: [device_send_message_disposition() shall destroy the MESSENGER_MESSAGE_DISPOSITION_INFO instance]  
+			destroy_messenger_disposition_info(messenger_disposition_info);
+		}
+	}
+
+	return result;
+}
+
+int device_set_retry_policy(DEVICE_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY policy, size_t retry_timeout_limit_in_seconds)
+{
+	(void)retry_timeout_limit_in_seconds;
+	(void)policy;
+	int result;
+
+	// Codes_SRS_DEVICE_09_080: [If `handle` is NULL, device_set_retry_policy shall return a non-zero result]
+	if (handle == NULL)
+	{
+		LogError("Failed setting retry policy (handle is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		// Codes_SRS_DEVICE_09_081: [device_set_retry_policy shall return a non-zero result]
+		LogError("Failed setting retry policy (functionality not supported)");
+		result = __FAILURE__;
+	}
+
+	return result;
+}
+
+int device_set_option(DEVICE_HANDLE handle, const char* name, void* value)
+{
+	int result;
+
+	// Codes_SRS_DEVICE_09_082: [If `handle` or `name` or `value` are NULL, device_set_option shall return a non-zero result]
+	if (handle == NULL || name == NULL || value == NULL)
+	{
+		LogError("failed setting device option (one of the followin are NULL: _handle=%p, name=%p, value=%p)",
+			handle, name, value);
+		result = __FAILURE__;
+	}
+	else
+	{
+		DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+		if (strcmp(DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS, name) == 0 ||
+			strcmp(DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS, name) == 0 ||
+			strcmp(DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS, name) == 0)
+		{
+			// Codes_SRS_DEVICE_09_083: [If `name` refers to authentication but CBS authentication is not used, device_set_option shall return a non-zero result]
+			if (instance->authentication_handle == NULL)
+			{
+				LogError("failed setting option for device '%s' (cannot set authentication option '%s'; not using CBS authentication)", instance->config->device_id, name);
+				result = __FAILURE__;
+			}
+			// Codes_SRS_DEVICE_09_084: [If `name` refers to authentication, it shall be passed along with `value` to authentication_set_option]
+			else if(authentication_set_option(instance->authentication_handle, name, value) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_085: [If authentication_set_option fails, device_set_option shall return a non-zero result]
+				LogError("failed setting option for device '%s' (failed setting authentication option '%s')", instance->config->device_id, name);
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else if (strcmp(DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0)
+		{
+			// Codes_SRS_DEVICE_09_086: [If `name` refers to messenger module, it shall be passed along with `value` to messenger_set_option]
+			if (messenger_set_option(instance->messenger_handle, name, value) != RESULT_OK)
+			{
+				// Codes_SRS_DEVICE_09_087: [If messenger_set_option fails, device_set_option shall return a non-zero result]
+				LogError("failed setting option for device '%s' (failed setting messenger option '%s')", instance->config->device_id, name);
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else if (strcmp(DEVICE_OPTION_SAVED_AUTH_OPTIONS, name) == 0)
+		{
+			// Codes_SRS_DEVICE_09_088: [If `name` is DEVICE_OPTION_SAVED_AUTH_OPTIONS but CBS authentication is not being used, device_set_option shall return a non-zero result]
+			if (instance->authentication_handle == NULL)
+			{
+				LogError("failed setting option for device '%s' (cannot set authentication option '%s'; not using CBS authentication)", instance->config->device_id, name);
+				result = __FAILURE__;
+			}
+			else if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, instance->authentication_handle) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result]
+				LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed for authentication instance)", instance->config->device_id);
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else if (strcmp(DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, name) == 0)
+		{
+			// Codes_SRS_DEVICE_09_089: [If `name` is DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, `value` shall be fed to `instance->messenger_handle` using OptionHandler_FeedOptions]
+			if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, instance->messenger_handle) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result]
+				LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed for messenger instance)", instance->config->device_id);
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else if (strcmp(DEVICE_OPTION_SAVED_OPTIONS, name) == 0)
+		{
+			// Codes_SRS_DEVICE_09_090: [If `name` is DEVICE_OPTION_SAVED_OPTIONS, `value` shall be fed to `instance` using OptionHandler_FeedOptions]
+			if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, handle) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_DEVICE_09_091: [If any call to OptionHandler_FeedOptions fails, device_set_option shall return a non-zero result]
+				LogError("failed setting option for device '%s' (OptionHandler_FeedOptions failed)", instance->config->device_id);
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else
+		{
+			// Codes_SRS_DEVICE_09_092: [If no failures occur, device_set_option shall return 0]
+			LogError("failed setting option for device '%s' (option with name '%s' is not suppported)", instance->config->device_id, name);
+			result = __FAILURE__;
+		}
+	}
+
+	return result;
+}
+
+OPTIONHANDLER_HANDLE device_retrieve_options(DEVICE_HANDLE handle)
+{
+	OPTIONHANDLER_HANDLE result;
+
+	// Codes_SRS_DEVICE_09_093: [If `handle` is NULL, device_retrieve_options shall return NULL]
+	if (handle == NULL)
+	{
+		LogError("Failed to retrieve options from device instance (handle is NULL)");
+		result = NULL;
+	}
+	else
+	{
+		// Codes_SRS_DEVICE_09_094: [A OPTIONHANDLER_HANDLE instance, aka `options` shall be created using OptionHandler_Create]
+		OPTIONHANDLER_HANDLE options = OptionHandler_Create(device_clone_option, device_destroy_option, (pfSetOption)device_set_option);
+
+		if (options == NULL)
+		{
+			// Codes_SRS_DEVICE_09_095: [If OptionHandler_Create fails, device_retrieve_options shall return NULL]
+			LogError("Failed to retrieve options from device instance (OptionHandler_Create failed)");
+			result = NULL;
+		}
+		else
+		{
+			DEVICE_INSTANCE* instance = (DEVICE_INSTANCE*)handle;
+
+			OPTIONHANDLER_HANDLE dependency_options = NULL;
+
+			// Codes_SRS_DEVICE_09_096: [If CBS authentication is used, `instance->authentication_handle` options shall be retrieved using authentication_retrieve_options]
+			if (instance->authentication_handle != NULL &&
+				(dependency_options = authentication_retrieve_options(instance->authentication_handle)) == NULL)
+			{
+				// Codes_SRS_DEVICE_09_097: [If authentication_retrieve_options fails, device_retrieve_options shall return NULL]
+				LogError("Failed to retrieve options from device '%s' (failed to retrieve options from authentication instance)", instance->config->device_id);
+				result = NULL;
+			}
+			// Codes_SRS_DEVICE_09_098: [The authentication options shall be added to `options` using OptionHandler_AddOption as DEVICE_OPTION_SAVED_AUTH_OPTIONS]
+			else if (instance->authentication_handle != NULL &&
+				OptionHandler_AddOption(options, DEVICE_OPTION_SAVED_AUTH_OPTIONS, (const void*)dependency_options) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_DEVICE_09_102: [If any call to OptionHandler_AddOption fails, device_retrieve_options shall return NULL]
+				LogError("Failed to retrieve options from device '%s' (OptionHandler_AddOption failed for option '%s')", instance->config->device_id, DEVICE_OPTION_SAVED_AUTH_OPTIONS);
+				result = NULL;
+			}
+			// Codes_SRS_DEVICE_09_099: [`instance->messenger_handle` options shall be retrieved using messenger_retrieve_options]
+			else if ((dependency_options = messenger_retrieve_options(instance->messenger_handle)) == NULL)
+			{
+				// Codes_SRS_DEVICE_09_100: [If messenger_retrieve_options fails, device_retrieve_options shall return NULL]
+				LogError("Failed to retrieve options from device '%s' (failed to retrieve options from messenger instance)", instance->config->device_id);
+				result = NULL;
+			}
+			// Codes_SRS_DEVICE_09_101: [The messenger options shall be added to `options` using OptionHandler_AddOption as DEVICE_OPTION_SAVED_MESSENGER_OPTIONS]
+			else if (OptionHandler_AddOption(options, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS, (const void*)dependency_options) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_DEVICE_09_102: [If any call to OptionHandler_AddOption fails, device_retrieve_options shall return NULL]
+				LogError("Failed to retrieve options from device '%s' (OptionHandler_AddOption failed for option '%s')", instance->config->device_id, DEVICE_OPTION_SAVED_MESSENGER_OPTIONS);
+				result = NULL;
+			}
+			else
+			{
+				// Codes_SRS_DEVICE_09_104: [If no failures occur, a handle to `options` shall be return]
+				result = options;
+			}
+
+			if (result == NULL)
+			{
+				// Codes_SRS_DEVICE_09_103: [If any failure occurs, any memory allocated by device_retrieve_options shall be destroyed]
+				OptionHandler_Destroy(options);
+			}
+		}
+	}
+
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_device.h	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,111 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H
+#define IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H
+
+#include "azure_c_shared_utility/umock_c_prod.h"
+#include "azure_c_shared_utility/optionhandler.h"
+#include "azure_uamqp_c/session.h"
+#include "azure_uamqp_c/cbs.h"
+#include "iothub_message.h"
+#include "iothub_client_private.h"
+#include "iothubtransport_amqp_device.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// @brief    name of option to apply the instance obtained using device_retrieve_options
+static const char* DEVICE_OPTION_SAVED_OPTIONS = "saved_device_options";
+static const char* DEVICE_OPTION_EVENT_SEND_TIMEOUT_SECS = "event_send_timeout_secs";
+static const char* DEVICE_OPTION_CBS_REQUEST_TIMEOUT_SECS = "cbs_request_timeout_secs";
+static const char* DEVICE_OPTION_SAS_TOKEN_REFRESH_TIME_SECS = "sas_token_refresh_time_secs";
+static const char* DEVICE_OPTION_SAS_TOKEN_LIFETIME_SECS = "sas_token_lifetime_secs";
+
+typedef enum DEVICE_STATE_TAG
+{
+	DEVICE_STATE_STOPPED,
+	DEVICE_STATE_STOPPING,
+	DEVICE_STATE_STARTING,
+	DEVICE_STATE_STARTED,
+	DEVICE_STATE_ERROR_AUTH,
+	DEVICE_STATE_ERROR_AUTH_TIMEOUT,
+	DEVICE_STATE_ERROR_MSG
+} DEVICE_STATE;
+
+typedef enum DEVICE_AUTH_MODE_TAG
+{
+	DEVICE_AUTH_MODE_CBS,
+	DEVICE_AUTH_MODE_X509
+} DEVICE_AUTH_MODE;
+
+typedef enum DEVICE_SEND_STATUS_TAG
+{
+	DEVICE_SEND_STATUS_IDLE,
+	DEVICE_SEND_STATUS_BUSY
+} DEVICE_SEND_STATUS;
+
+typedef enum D2C_EVENT_SEND_RESULT_TAG
+{
+	D2C_EVENT_SEND_COMPLETE_RESULT_OK,
+	D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE,
+	D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING,
+	D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT,
+	D2C_EVENT_SEND_COMPLETE_RESULT_DEVICE_DESTROYED,
+	D2C_EVENT_SEND_COMPLETE_RESULT_ERROR_UNKNOWN
+} D2C_EVENT_SEND_RESULT;
+
+typedef enum DEVICE_MESSAGE_DISPOSITION_RESULT_TAG
+{
+	DEVICE_MESSAGE_DISPOSITION_RESULT_NONE,
+	DEVICE_MESSAGE_DISPOSITION_RESULT_ACCEPTED,
+	DEVICE_MESSAGE_DISPOSITION_RESULT_REJECTED,
+	DEVICE_MESSAGE_DISPOSITION_RESULT_RELEASED
+} DEVICE_MESSAGE_DISPOSITION_RESULT;
+
+typedef struct DEVICE_MESSAGE_DISPOSITION_INFO_TAG
+{
+	unsigned long message_id;
+	char* source;
+} DEVICE_MESSAGE_DISPOSITION_INFO;
+
+typedef void(*ON_DEVICE_STATE_CHANGED)(void* context, DEVICE_STATE previous_state, DEVICE_STATE new_state);
+typedef DEVICE_MESSAGE_DISPOSITION_RESULT(*ON_DEVICE_C2D_MESSAGE_RECEIVED)(IOTHUB_MESSAGE_HANDLE message, DEVICE_MESSAGE_DISPOSITION_INFO* disposition_info, void* context);
+typedef void(*ON_DEVICE_D2C_EVENT_SEND_COMPLETE)(IOTHUB_MESSAGE_LIST* message, D2C_EVENT_SEND_RESULT result, void* context);
+
+typedef struct DEVICE_CONFIG_TAG
+{
+	char* device_id;
+	char* iothub_host_fqdn;
+	DEVICE_AUTH_MODE authentication_mode;
+	ON_DEVICE_STATE_CHANGED on_state_changed_callback;
+	void* on_state_changed_context;
+
+	char* device_primary_key;
+	char* device_secondary_key;
+	char* device_sas_token;
+} DEVICE_CONFIG;
+
+typedef struct DEVICE_INSTANCE* DEVICE_HANDLE;
+
+MOCKABLE_FUNCTION(, DEVICE_HANDLE, device_create, DEVICE_CONFIG*, config);
+MOCKABLE_FUNCTION(, void, device_destroy, DEVICE_HANDLE, handle);
+MOCKABLE_FUNCTION(, int, device_start_async, DEVICE_HANDLE, handle, SESSION_HANDLE, session_handle, CBS_HANDLE, cbs_handle);
+MOCKABLE_FUNCTION(, int, device_stop, DEVICE_HANDLE, handle);
+MOCKABLE_FUNCTION(, void, device_do_work, DEVICE_HANDLE, handle);
+MOCKABLE_FUNCTION(, int, device_send_event_async, DEVICE_HANDLE, handle, IOTHUB_MESSAGE_LIST*, message, ON_DEVICE_D2C_EVENT_SEND_COMPLETE, on_device_d2c_event_send_complete_callback, void*, context);
+MOCKABLE_FUNCTION(, int, device_get_send_status, DEVICE_HANDLE, handle, DEVICE_SEND_STATUS*, send_status);
+MOCKABLE_FUNCTION(, int, device_subscribe_message, DEVICE_HANDLE, handle, ON_DEVICE_C2D_MESSAGE_RECEIVED, on_message_received_callback, void*, context);
+MOCKABLE_FUNCTION(, int, device_unsubscribe_message, DEVICE_HANDLE, handle);
+MOCKABLE_FUNCTION(, int, device_send_message_disposition, DEVICE_HANDLE, device_handle, DEVICE_MESSAGE_DISPOSITION_INFO*, disposition_info, DEVICE_MESSAGE_DISPOSITION_RESULT, disposition_result);
+MOCKABLE_FUNCTION(, int, device_set_retry_policy, DEVICE_HANDLE, handle, IOTHUB_CLIENT_RETRY_POLICY, policy, size_t, retry_timeout_limit_in_seconds);
+MOCKABLE_FUNCTION(, int, device_set_option, DEVICE_HANDLE, handle, const char*, name, void*, value);
+MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, device_retrieve_options, DEVICE_HANDLE, handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IOTHUBTRANSPORTAMQP_AMQP_DEVICE_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_messenger.c	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,1887 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include "azure_c_shared_utility/optimize_size.h"
+#include "azure_c_shared_utility/crt_abstractions.h"
+#include "azure_c_shared_utility/gballoc.h"
+#include "azure_c_shared_utility/agenttime.h" 
+#include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/uniqueid.h"
+#include "azure_c_shared_utility/singlylinkedlist.h"
+#include "azure_uamqp_c/link.h"
+#include "azure_uamqp_c/messaging.h"
+#include "azure_uamqp_c/message_sender.h"
+#include "azure_uamqp_c/message_receiver.h"
+#include "uamqp_messaging.h"
+#include "iothub_client_private.h"
+#include "iothub_client_version.h"
+#include "iothubtransport_amqp_messenger.h"
+
+#define RESULT_OK 0
+#define INDEFINITE_TIME ((time_t)(-1))
+
+#define IOTHUB_DEVICES_PATH_FMT                         "%s/devices/%s"
+#define IOTHUB_EVENT_SEND_ADDRESS_FMT                   "amqps://%s/messages/events"
+#define IOTHUB_MESSAGE_RECEIVE_ADDRESS_FMT              "amqps://%s/messages/devicebound"
+#define MESSAGE_SENDER_LINK_NAME_PREFIX                 "link-snd"
+#define MESSAGE_SENDER_MAX_LINK_SIZE                    UINT64_MAX
+#define MESSAGE_RECEIVER_LINK_NAME_PREFIX               "link-rcv"
+#define MESSAGE_RECEIVER_MAX_LINK_SIZE                  65536
+#define DEFAULT_EVENT_SEND_RETRY_LIMIT                  10
+#define DEFAULT_EVENT_SEND_TIMEOUT_SECS                 600
+#define MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS    300
+#define MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS  300
+#define UNIQUE_ID_BUFFER_SIZE                           37
+#define STRING_NULL_TERMINATOR                          '\0'
+ 
+typedef struct MESSENGER_INSTANCE_TAG
+{
+	STRING_HANDLE device_id;
+	STRING_HANDLE iothub_host_fqdn;
+	SINGLYLINKEDLIST_HANDLE waiting_to_send;
+	SINGLYLINKEDLIST_HANDLE in_progress_list;
+	MESSENGER_STATE state;
+	
+	ON_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback;
+	void* on_state_changed_context;
+
+	bool receive_messages;
+	ON_MESSENGER_MESSAGE_RECEIVED on_message_received_callback;
+	void* on_message_received_context;
+
+	SESSION_HANDLE session_handle;
+	LINK_HANDLE sender_link;
+	MESSAGE_SENDER_HANDLE message_sender;
+	MESSAGE_SENDER_STATE message_sender_current_state;
+	MESSAGE_SENDER_STATE message_sender_previous_state;
+	LINK_HANDLE receiver_link;
+	MESSAGE_RECEIVER_HANDLE message_receiver;
+	MESSAGE_RECEIVER_STATE message_receiver_current_state;
+	MESSAGE_RECEIVER_STATE message_receiver_previous_state;
+
+	size_t event_send_retry_limit;
+	size_t event_send_error_count;
+	size_t event_send_timeout_secs;
+	time_t last_message_sender_state_change_time;
+	time_t last_message_receiver_state_change_time;
+} MESSENGER_INSTANCE;
+
+typedef struct MESSENGER_SEND_EVENT_TASK_TAG
+{
+	IOTHUB_MESSAGE_LIST* message;
+	ON_MESSENGER_EVENT_SEND_COMPLETE on_event_send_complete_callback;
+	void* context;
+	time_t send_time;
+	MESSENGER_INSTANCE *messenger;
+	bool is_timed_out;
+} MESSENGER_SEND_EVENT_TASK;
+
+// @brief
+//     Evaluates if the ammount of time since start_time is greater or lesser than timeout_in_secs.
+// @param is_timed_out
+//     Set to 1 if a timeout has been reached, 0 otherwise. Not set if any failure occurs.
+// @returns
+//     0 if no failures occur, non-zero otherwise.
+static int is_timeout_reached(time_t start_time, size_t timeout_in_secs, int *is_timed_out)
+{
+	int result;
+
+	if (start_time == INDEFINITE_TIME)
+	{
+		LogError("Failed to verify timeout (start_time is INDEFINITE)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		time_t current_time;
+
+		if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
+		{
+			LogError("Failed to verify timeout (get_time failed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			if (get_difftime(current_time, start_time) >= timeout_in_secs)
+			{
+				*is_timed_out = 1;
+			}
+			else
+			{
+				*is_timed_out = 0;
+			}
+
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+static STRING_HANDLE create_devices_path(STRING_HANDLE iothub_host_fqdn, STRING_HANDLE device_id)
+{
+	STRING_HANDLE devices_path;
+
+	if ((devices_path = STRING_new()) == NULL)
+	{
+		LogError("Failed creating devices_path (STRING_new failed)");
+	}
+	else
+        {
+		const char* iothub_host_fqdn_char_ptr = STRING_c_str(iothub_host_fqdn);
+		const char* device_id_char_ptr = STRING_c_str(device_id);
+        	if (STRING_sprintf(devices_path, IOTHUB_DEVICES_PATH_FMT, iothub_host_fqdn_char_ptr, device_id_char_ptr) != RESULT_OK)
+		{
+			STRING_delete(devices_path);
+			devices_path = NULL;
+			LogError("Failed creating devices_path (STRING_sprintf failed)");
+		}
+        }
+
+	return devices_path;
+}
+
+static STRING_HANDLE create_event_send_address(STRING_HANDLE devices_path)
+{
+	STRING_HANDLE event_send_address;
+
+	if ((event_send_address = STRING_new()) == NULL)
+	{
+		LogError("Failed creating the event_send_address (STRING_new failed)");
+	}
+	else
+	{
+		const char* devices_path_char_ptr = STRING_c_str(devices_path);
+		if (STRING_sprintf(event_send_address, IOTHUB_EVENT_SEND_ADDRESS_FMT, devices_path_char_ptr) != RESULT_OK)
+		{
+			STRING_delete(event_send_address);
+			event_send_address = NULL;
+			LogError("Failed creating the event_send_address (STRING_sprintf failed)");
+		}
+	}
+
+	return event_send_address;
+}
+
+static STRING_HANDLE create_event_sender_source_name(STRING_HANDLE link_name)
+{
+	STRING_HANDLE source_name;
+	
+	if ((source_name = STRING_new()) == NULL)
+	{
+		LogError("Failed creating the source_name (STRING_new failed)");
+	}
+	else
+	{
+		const char* link_name_char_ptr = STRING_c_str(link_name);
+		if (STRING_sprintf(source_name, "%s-source", link_name_char_ptr) != RESULT_OK)
+		{
+			STRING_delete(source_name);
+			source_name = NULL;
+			LogError("Failed creating the source_name (STRING_sprintf failed)");
+		}
+	}
+
+	return source_name;
+}
+
+static STRING_HANDLE create_message_receive_address(STRING_HANDLE devices_path)
+{
+	STRING_HANDLE message_receive_address;
+
+	if ((message_receive_address = STRING_new()) == NULL)
+	{
+		LogError("Failed creating the message_receive_address (STRING_new failed)");
+	}
+	else
+	{
+		const char* devices_path_char_ptr = STRING_c_str(devices_path);
+		if (STRING_sprintf(message_receive_address, IOTHUB_MESSAGE_RECEIVE_ADDRESS_FMT, devices_path_char_ptr) != RESULT_OK)
+		{
+			STRING_delete(message_receive_address);
+			message_receive_address = NULL;
+			LogError("Failed creating the message_receive_address (STRING_sprintf failed)");
+		}
+	}
+
+	return message_receive_address;
+}
+
+static STRING_HANDLE create_message_receiver_target_name(STRING_HANDLE link_name)
+{
+	STRING_HANDLE target_name;
+
+	if ((target_name = STRING_new()) == NULL)
+	{
+		LogError("Failed creating the target_name (STRING_new failed)");
+	}
+	else
+	{
+		const char* link_name_char_ptr = STRING_c_str(link_name);
+		if (STRING_sprintf(target_name, "%s-target", link_name_char_ptr) != RESULT_OK)
+		{
+			STRING_delete(target_name);
+			target_name = NULL;
+			LogError("Failed creating the target_name (STRING_sprintf failed)");
+		}
+	}
+
+	return target_name;
+}
+
+static STRING_HANDLE create_link_name(const char* prefix, const char* infix)
+{
+	char* unique_id;
+	STRING_HANDLE tag = NULL;
+
+	if ((unique_id = (char*)malloc(sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1)) == NULL)
+	{
+		LogError("Failed generating an unique tag (malloc failed)");
+	}
+	else
+	{
+        memset(unique_id, 0, sizeof(char) * UNIQUE_ID_BUFFER_SIZE + 1);
+
+		if (UniqueId_Generate(unique_id, UNIQUE_ID_BUFFER_SIZE) != UNIQUEID_OK)
+		{
+			LogError("Failed generating an unique tag (UniqueId_Generate failed)");
+		}
+		else if ((tag = STRING_new()) == NULL)
+		{
+			LogError("Failed generating an unique tag (STRING_new failed)");
+		}
+		else if (STRING_sprintf(tag, "%s-%s-%s", prefix, infix, unique_id) != RESULT_OK)
+		{
+			STRING_delete(tag);
+			tag = NULL;
+			LogError("Failed generating an unique tag (STRING_sprintf failed)");
+		}
+
+		free(unique_id);
+	}
+
+	return tag;
+}
+
+static void update_messenger_state(MESSENGER_INSTANCE* instance, MESSENGER_STATE new_state)
+{
+	if (new_state != instance->state)
+	{
+		MESSENGER_STATE previous_state = instance->state;
+		instance->state = new_state;
+
+		if (instance->on_state_changed_callback != NULL)
+		{
+			instance->on_state_changed_callback(instance->on_state_changed_context, previous_state, new_state);
+		}
+	}
+}
+
+static void attach_device_client_type_to_link(LINK_HANDLE link)
+{
+	fields attach_properties;
+	AMQP_VALUE device_client_type_key_name;
+	AMQP_VALUE device_client_type_value;
+	int result;
+
+	if ((attach_properties = amqpvalue_create_map()) == NULL)
+	{
+		LogError("Failed to create the map for device client type.");
+	}
+	else
+	{
+		if ((device_client_type_key_name = amqpvalue_create_symbol("com.microsoft:client-version")) == NULL)
+		{
+			LogError("Failed to create the key name for the device client type.");
+		}
+		else
+		{
+			if ((device_client_type_value = amqpvalue_create_string(CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION)) == NULL)
+			{
+				LogError("Failed to create the key value for the device client type.");
+			}
+			else
+			{
+				if ((result = amqpvalue_set_map_value(attach_properties, device_client_type_key_name, device_client_type_value)) != 0)
+				{
+					LogError("Failed to set the property map for the device client type (error code is: %d)", result);
+				}
+				else if ((result = link_set_attach_properties(link, attach_properties)) != 0)
+				{
+					LogError("Unable to attach the device client type to the link properties (error code is: %d)", result);
+				}
+
+				amqpvalue_destroy(device_client_type_value);
+			}
+
+			amqpvalue_destroy(device_client_type_key_name);
+		}
+
+		amqpvalue_destroy(attach_properties);
+	}
+}
+
+static void destroy_event_sender(MESSENGER_INSTANCE* instance)
+{
+	if (instance->message_sender != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_060: [`instance->message_sender` shall be destroyed using messagesender_destroy()]
+		messagesender_destroy(instance->message_sender);
+		instance->message_sender = NULL;
+		instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE;
+		instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE;
+		instance->last_message_sender_state_change_time = INDEFINITE_TIME;
+	}
+
+	if (instance->sender_link != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_063: [`instance->sender_link` shall be destroyed using link_destroy()]
+		link_destroy(instance->sender_link);
+		instance->sender_link = NULL;
+	}
+}
+
+static void on_event_sender_state_changed_callback(void* context, MESSAGE_SENDER_STATE new_state, MESSAGE_SENDER_STATE previous_state)
+{
+	if (context == NULL)
+	{
+		LogError("on_event_sender_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored");
+	}
+	else
+	{
+		if (new_state != previous_state)
+		{
+			MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)context;
+			instance->message_sender_current_state = new_state;
+			instance->message_sender_previous_state = previous_state;
+			instance->last_message_sender_state_change_time = get_time(NULL);
+		}
+	}
+}
+
+static int create_event_sender(MESSENGER_INSTANCE* instance)
+{
+	int result;
+
+	STRING_HANDLE link_name = NULL;
+	STRING_HANDLE source_name = NULL;
+	AMQP_VALUE source = NULL;
+	AMQP_VALUE target = NULL;
+	STRING_HANDLE devices_path = NULL;
+	STRING_HANDLE event_send_address = NULL;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_033: [A variable, named `devices_path`, shall be created concatenating `instance->iothub_host_fqdn`, "/devices/" and `instance->device_id`]
+	if ((devices_path = create_devices_path(instance->iothub_host_fqdn, instance->device_id)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_034: [If `devices_path` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (failed creating the 'devices_path')");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_035: [A variable, named `event_send_address`, shall be created concatenating "amqps://", `devices_path` and "/messages/events"]
+	else if ((event_send_address = create_event_send_address(devices_path)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_036: [If `event_send_address` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (failed creating the 'event_send_address')");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_037: [A `link_name` variable shall be created using an unique string label per AMQP session]
+	else if ((link_name = create_link_name(MESSAGE_SENDER_LINK_NAME_PREFIX, STRING_c_str(instance->device_id))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_038: [If `link_name` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (failed creating an unique link name)");
+	}
+	else if ((source_name = create_event_sender_source_name(link_name)) == NULL)
+	{
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (failed creating an unique source name)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_039: [A `source` variable shall be created with messaging_create_source() using an unique string label per AMQP session]
+	else if ((source = messaging_create_source(STRING_c_str(source_name))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_040: [If `source` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (messaging_create_source failed)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_041: [A `target` variable shall be created with messaging_create_target() using `event_send_address`]
+	else if ((target = messaging_create_target(STRING_c_str(event_send_address))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_042: [If `target` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (messaging_create_target failed)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_043: [`instance->sender_link` shall be set using link_create(), passing `instance->session_handle`, `link_name`, "role_sender", `source` and `target` as parameters]
+	else if ((instance->sender_link = link_create(instance->session_handle, STRING_c_str(link_name), role_sender, source, target)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_044: [If link_create() fails, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message sender (link_create failed)");
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_047: [`instance->sender_link` maximum message size shall be set to UINT64_MAX using link_set_max_message_size()]
+		if (link_set_max_message_size(instance->sender_link, MESSAGE_SENDER_MAX_LINK_SIZE) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_048: [If link_set_max_message_size() fails, it shall be logged and ignored.]
+			LogError("Failed setting message sender link max message size.");
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_049: [`instance->sender_link` should have a property "com.microsoft:client-version" set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`, using amqpvalue_set_map_value() and link_set_attach_properties()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_050: [If amqpvalue_set_map_value() or link_set_attach_properties() fail, the failure shall be ignored]
+		attach_device_client_type_to_link(instance->sender_link);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_051: [`instance->message_sender` shall be created using messagesender_create(), passing the `instance->sender_link` and `on_event_sender_state_changed_callback`]
+		if ((instance->message_sender = messagesender_create(instance->sender_link, on_event_sender_state_changed_callback, (void*)instance)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_052: [If messagesender_create() fails, messenger_do_work() shall fail and return]
+			result = __FAILURE__;
+			link_destroy(instance->sender_link);
+			instance->sender_link = NULL;
+			LogError("Failed creating the message sender (messagesender_create failed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_053: [`instance->message_sender` shall be opened using messagesender_open()]
+			if (messagesender_open(instance->message_sender) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_054: [If messagesender_open() fails, messenger_do_work() shall fail and return]
+				result = __FAILURE__;
+				messagesender_destroy(instance->message_sender);
+				instance->message_sender = NULL;
+				link_destroy(instance->sender_link);
+				instance->sender_link = NULL;
+				LogError("Failed opening the AMQP message sender.");
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_055: [Before returning, messenger_do_work() shall release all the temporary memory it has allocated]
+	if (link_name != NULL)
+		STRING_delete(link_name);
+	if (source_name != NULL)
+		STRING_delete(source_name);
+	if (source != NULL)
+		amqpvalue_destroy(source);
+	if (target != NULL)
+		amqpvalue_destroy(target);
+	if (devices_path != NULL)
+		STRING_delete(devices_path);
+	if (event_send_address != NULL)
+		STRING_delete(event_send_address);
+
+	return result;
+}
+
+static void destroy_message_receiver(MESSENGER_INSTANCE* instance)
+{
+	if (instance->message_receiver != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_061: [`instance->message_receiver` shall be closed using messagereceiver_close()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_093: [`instance->message_receiver` shall be closed using messagereceiver_close()]
+		if (messagereceiver_close(instance->message_receiver) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_094: [If messagereceiver_close() fails, it shall be logged and ignored]
+			LogError("Failed closing the AMQP message receiver (this failure will be ignored).");
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_062: [`instance->message_receiver` shall be destroyed using messagereceiver_destroy()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_095: [`instance->message_receiver` shall be destroyed using messagereceiver_destroy()]
+		messagereceiver_destroy(instance->message_receiver);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_096: [`instance->message_receiver` shall be set to NULL]
+		instance->message_receiver = NULL;
+		instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE;
+		instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE;
+		instance->last_message_receiver_state_change_time = INDEFINITE_TIME;
+	}
+
+	if (instance->receiver_link != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_064: [`instance->receiver_link` shall be destroyed using link_destroy()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_097: [`instance->receiver_link` shall be destroyed using link_destroy()]
+		link_destroy(instance->receiver_link);
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_098: [`instance->receiver_link` shall be set to NULL]
+		instance->receiver_link = NULL;
+	}
+}
+
+static void on_message_receiver_state_changed_callback(const void* context, MESSAGE_RECEIVER_STATE new_state, MESSAGE_RECEIVER_STATE previous_state)
+{
+	if (context == NULL)
+	{
+		LogError("on_message_receiver_state_changed_callback was invoked with a NULL context; although unexpected, this failure will be ignored");
+	}
+	else
+	{
+		if (new_state != previous_state)
+		{
+			MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)context;
+			instance->message_receiver_current_state = new_state;
+			instance->message_receiver_previous_state = previous_state;
+			instance->last_message_receiver_state_change_time = get_time(NULL);
+		}
+	}
+}
+
+static MESSENGER_MESSAGE_DISPOSITION_INFO* create_message_disposition_info(MESSENGER_INSTANCE* messenger)
+{
+	MESSENGER_MESSAGE_DISPOSITION_INFO* result;
+
+	if ((result = (MESSENGER_MESSAGE_DISPOSITION_INFO*)malloc(sizeof(MESSENGER_MESSAGE_DISPOSITION_INFO))) == NULL)
+	{
+		LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO container (malloc failed)");
+		result = NULL;
+	}
+	else
+	{
+		delivery_number message_id;
+
+		if (messagereceiver_get_received_message_id(messenger->message_receiver, &message_id) != RESULT_OK)
+		{
+			LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_received_message_id failed)");
+			free(result);
+			result = NULL;
+		}
+		else
+		{
+			const char* link_name;
+
+			if (messagereceiver_get_link_name(messenger->message_receiver, &link_name) != RESULT_OK)
+			{
+				LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO container (messagereceiver_get_link_name failed)");
+				free(result);
+				result = NULL;
+			}
+			else if (mallocAndStrcpy_s(&result->source, link_name) != RESULT_OK)
+			{
+				LogError("Failed creating MESSENGER_MESSAGE_DISPOSITION_INFO container (failed copying link name)");
+				free(result);
+				result = NULL;
+			}
+			else
+			{
+				result->message_id = message_id;
+			}
+		}
+	}
+
+	return result;
+}
+
+static void destroy_message_disposition_info(MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info)
+{
+	free(disposition_info->source);
+	free(disposition_info);
+}
+
+static AMQP_VALUE create_uamqp_disposition_result_from(MESSENGER_DISPOSITION_RESULT disposition_result)
+{
+	AMQP_VALUE uamqp_disposition_result;
+
+	if (disposition_result == MESSENGER_DISPOSITION_RESULT_NONE)
+	{
+		uamqp_disposition_result = NULL; // intentionally not sending an answer.
+	}
+	else if (disposition_result == MESSENGER_DISPOSITION_RESULT_ACCEPTED)
+	{
+		uamqp_disposition_result = messaging_delivery_accepted();
+	}
+	else if (disposition_result == MESSENGER_DISPOSITION_RESULT_RELEASED)
+	{
+		uamqp_disposition_result = messaging_delivery_released();
+	}
+	else if (disposition_result == MESSENGER_DISPOSITION_RESULT_REJECTED)
+	{
+		uamqp_disposition_result = messaging_delivery_rejected("Rejected by application", "Rejected by application");
+	}
+	else
+	{
+		LogError("Failed creating a disposition result for messagereceiver (result %d is not supported)", disposition_result);
+		uamqp_disposition_result = NULL;
+	}
+
+	return uamqp_disposition_result;
+}
+
+static AMQP_VALUE on_message_received_internal_callback(const void* context, MESSAGE_HANDLE message)
+{
+	AMQP_VALUE result;
+	int api_call_result;
+	IOTHUB_MESSAGE_HANDLE iothub_message;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_121: [An IOTHUB_MESSAGE_HANDLE shall be obtained from MESSAGE_HANDLE using IoTHubMessage_CreateFromUamqpMessage()]
+	if ((api_call_result = IoTHubMessage_CreateFromUamqpMessage(message, &iothub_message)) != RESULT_OK)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_122: [If IoTHubMessage_CreateFromUamqpMessage() fails, on_message_received_internal_callback shall return the result of messaging_delivery_rejected()]
+		result = messaging_delivery_rejected("Rejected due to failure reading AMQP message", "Failed reading AMQP message");
+
+		LogError("on_message_received_internal_callback failed (IoTHubMessage_CreateFromUamqpMessage; error = %d).", api_call_result);
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)context;
+		MESSENGER_MESSAGE_DISPOSITION_INFO* message_disposition_info;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_186: [A MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be created containing the source link name and message delivery ID]
+		if ((message_disposition_info = create_message_disposition_info(instance)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_187: [**If the MESSENGER_MESSAGE_DISPOSITION_INFO instance fails to be created, on_message_received_internal_callback shall return messaging_delivery_released()]
+			LogError("on_message_received_internal_callback failed (failed creating MESSENGER_MESSAGE_DISPOSITION_INFO).");
+			result = messaging_delivery_released();
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_123: [`instance->on_message_received_callback` shall be invoked passing the IOTHUB_MESSAGE_HANDLE and MESSENGER_MESSAGE_DISPOSITION_INFO instance]
+			MESSENGER_DISPOSITION_RESULT disposition_result = instance->on_message_received_callback(iothub_message, message_disposition_info, instance->on_message_received_context);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_188: [The memory allocated for the MESSENGER_MESSAGE_DISPOSITION_INFO instance shall be released]
+			destroy_message_disposition_info(message_disposition_info);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_125: [If `instance->on_message_received_callback` returns MESSENGER_DISPOSITION_RESULT_ACCEPTED, on_message_received_internal_callback shall return the result of messaging_delivery_accepted()]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_126: [If `instance->on_message_received_callback` returns MESSENGER_DISPOSITION_RESULT_RELEASED, on_message_received_internal_callback shall return the result of messaging_delivery_released()]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_127: [If `instance->on_message_received_callback` returns MESSENGER_DISPOSITION_RESULT_REJECTED, on_message_received_internal_callback shall return the result of messaging_delivery_rejected()]
+			result = create_uamqp_disposition_result_from(disposition_result);
+		}
+	}
+
+	return result;
+}
+
+static int create_message_receiver(MESSENGER_INSTANCE* instance)
+{
+	int result;
+
+	STRING_HANDLE devices_path = NULL;
+	STRING_HANDLE message_receive_address = NULL;
+	STRING_HANDLE link_name = NULL;
+	STRING_HANDLE target_name = NULL;
+	AMQP_VALUE source = NULL;
+	AMQP_VALUE target = NULL;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_068: [A variable, named `devices_path`, shall be created concatenating `instance->iothub_host_fqdn`, "/devices/" and `instance->device_id`]
+	if ((devices_path = create_devices_path(instance->iothub_host_fqdn, instance->device_id)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_069: [If `devices_path` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (failed creating the 'devices_path')");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_070: [A variable, named `message_receive_address`, shall be created concatenating "amqps://", `devices_path` and "/messages/devicebound"]
+	else if ((message_receive_address = create_message_receive_address(devices_path)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_071: [If `message_receive_address` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (failed creating the 'message_receive_address')");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_072: [A `link_name` variable shall be created using an unique string label per AMQP session]
+	else if ((link_name = create_link_name(MESSAGE_RECEIVER_LINK_NAME_PREFIX, STRING_c_str(instance->device_id))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_073: [If `link_name` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (failed creating an unique link name)");
+	}
+	else if ((target_name = create_message_receiver_target_name(link_name)) == NULL)
+	{
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (failed creating an unique target name)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_074: [A `target` variable shall be created with messaging_create_target() using an unique string label per AMQP session]
+	else if ((target = messaging_create_target(STRING_c_str(target_name))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_075: [If `target` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (messaging_create_target failed)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_076: [A `source` variable shall be created with messaging_create_source() using `message_receive_address`]
+	else if ((source = messaging_create_source(STRING_c_str(message_receive_address))) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_077: [If `source` fails to be created, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (messaging_create_source failed)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_078: [`instance->receiver_link` shall be set using link_create(), passing `instance->session_handle`, `link_name`, "role_receiver", `source` and `target` as parameters]
+	else if ((instance->receiver_link = link_create(instance->session_handle, STRING_c_str(link_name), role_receiver, source, target)) == NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_079: [If link_create() fails, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (link_create failed)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_080: [`instance->receiver_link` settle mode shall be set to "receiver_settle_mode_first" using link_set_rcv_settle_mode(), ]
+	else if (link_set_rcv_settle_mode(instance->receiver_link, receiver_settle_mode_first) != RESULT_OK)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_081: [If link_set_rcv_settle_mode() fails, messenger_do_work() shall fail and return]
+		result = __FAILURE__;
+		LogError("Failed creating the message receiver (link_set_rcv_settle_mode failed)");
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_082: [`instance->receiver_link` maximum message size shall be set to 65536 using link_set_max_message_size()]
+		if (link_set_max_message_size(instance->receiver_link, MESSAGE_RECEIVER_MAX_LINK_SIZE) != RESULT_OK)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_083: [If link_set_max_message_size() fails, it shall be logged and ignored.]
+			LogError("Failed setting message receiver link max message size.");
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_084: [`instance->receiver_link` should have a property "com.microsoft:client-version" set as `CLIENT_DEVICE_TYPE_PREFIX/IOTHUB_SDK_VERSION`, using amqpvalue_set_map_value() and link_set_attach_properties()]
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_085: [If amqpvalue_set_map_value() or link_set_attach_properties() fail, the failure shall be ignored]
+		attach_device_client_type_to_link(instance->receiver_link);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_086: [`instance->message_receiver` shall be created using messagereceiver_create(), passing the `instance->receiver_link` and `on_messagereceiver_state_changed_callback`]
+		if ((instance->message_receiver = messagereceiver_create(instance->receiver_link, on_message_receiver_state_changed_callback, (void*)instance)) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_087: [If messagereceiver_create() fails, messenger_do_work() shall fail and return]
+			result = __FAILURE__;
+			link_destroy(instance->receiver_link);
+			LogError("Failed creating the message receiver (messagereceiver_create failed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_088: [`instance->message_receiver` shall be opened using messagereceiver_open(), passing `on_message_received_internal_callback`]
+			if (messagereceiver_open(instance->message_receiver, on_message_received_internal_callback, (const void*)instance) != RESULT_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_089: [If messagereceiver_open() fails, messenger_do_work() shall fail and return]
+				result = __FAILURE__;
+				messagereceiver_destroy(instance->message_receiver);
+				link_destroy(instance->receiver_link);
+				LogError("Failed opening the AMQP message receiver.");
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	if (devices_path != NULL)
+		STRING_delete(devices_path);
+	if (message_receive_address != NULL)
+		STRING_delete(message_receive_address);
+	if (link_name != NULL)
+		STRING_delete(link_name);
+	if (target_name != NULL)
+		STRING_delete(target_name);
+	if (source != NULL)
+		amqpvalue_destroy(source);
+	if (target != NULL)
+		amqpvalue_destroy(target);
+
+	return result;
+}
+
+static int move_event_to_in_progress_list(MESSENGER_SEND_EVENT_TASK* task)
+{
+	int result; 
+
+	if (singlylinkedlist_add(task->messenger->in_progress_list, (void*)task) == NULL)
+	{
+		result = __FAILURE__;
+		LogError("Failed moving event to in_progress list (singlylinkedlist_add failed)");
+	}
+	else
+	{
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+static bool find_MESSENGER_SEND_EVENT_TASK_on_list(LIST_ITEM_HANDLE list_item, const void* match_context)
+{
+	return (list_item != NULL && singlylinkedlist_item_get_value(list_item) == match_context);
+}
+
+static void remove_event_from_in_progress_list(MESSENGER_SEND_EVENT_TASK *task)
+{
+	LIST_ITEM_HANDLE list_item = singlylinkedlist_find(task->messenger->in_progress_list, find_MESSENGER_SEND_EVENT_TASK_on_list, (void*)task);
+
+	if (list_item != NULL)
+	{
+		if (singlylinkedlist_remove(task->messenger->in_progress_list, list_item) != RESULT_OK)
+		{
+			LogError("Failed removing event from in_progress list (singlylinkedlist_remove failed)");
+		}
+	}
+}
+
+static int copy_events_to_list(SINGLYLINKEDLIST_HANDLE from_list, SINGLYLINKEDLIST_HANDLE to_list)
+{
+	int result;
+	LIST_ITEM_HANDLE list_item;
+
+	result = RESULT_OK;
+	list_item = singlylinkedlist_get_head_item(from_list);
+
+	while (list_item != NULL)
+	{
+		MESSENGER_SEND_EVENT_TASK *task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item);
+
+		if (singlylinkedlist_add(to_list, task) == NULL)
+		{
+			LogError("Failed copying event to destination list (singlylinkedlist_add failed)");
+			result = __FAILURE__;
+			break;
+		}
+		else
+		{
+			list_item = singlylinkedlist_get_next_item(list_item);
+		}
+	}
+
+	return result;
+}
+
+static int singlylinkedlist_clear(SINGLYLINKEDLIST_HANDLE list)
+{
+	int result;
+	LIST_ITEM_HANDLE list_item;
+
+	result = RESULT_OK;
+
+	while ((list_item = singlylinkedlist_get_head_item(list)) != NULL)
+	{
+		if (singlylinkedlist_remove(list, list_item) != RESULT_OK)
+		{
+			LogError("Failed removing items from list (%d)", list);
+			result = __FAILURE__;
+			break;
+		}
+	}
+
+	return result;
+}
+
+static int move_events_to_wait_to_send_list(MESSENGER_INSTANCE* instance)
+{
+	int result;
+	LIST_ITEM_HANDLE list_item;
+
+	if ((list_item = singlylinkedlist_get_head_item(instance->in_progress_list)) == NULL)
+	{
+		result = RESULT_OK;
+	}
+	else
+	{
+		SINGLYLINKEDLIST_HANDLE new_wait_to_send_list;
+
+		if ((new_wait_to_send_list = singlylinkedlist_create()) == NULL)
+		{
+			LogError("Failed moving events back to wait_to_send list (singlylinkedlist_create failed to create new wait_to_send_list)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			SINGLYLINKEDLIST_HANDLE new_in_progress_list;
+		
+			if (copy_events_to_list(instance->in_progress_list, new_wait_to_send_list) != RESULT_OK)
+			{
+				LogError("Failed moving events back to wait_to_send list (failed adding in_progress_list items to new_wait_to_send_list)");
+				singlylinkedlist_destroy(new_wait_to_send_list);
+				result = __FAILURE__;
+			}
+			else if (copy_events_to_list(instance->waiting_to_send, new_wait_to_send_list) != RESULT_OK)
+			{
+				LogError("Failed moving events back to wait_to_send list (failed adding wait_to_send items to new_wait_to_send_list)");
+				singlylinkedlist_destroy(new_wait_to_send_list);
+				result = __FAILURE__;
+			}
+			else if ((new_in_progress_list = singlylinkedlist_create()) == NULL)
+			{
+				LogError("Failed moving events back to wait_to_send list (singlylinkedlist_create failed to create new in_progress_list)");
+				singlylinkedlist_destroy(new_wait_to_send_list);
+				result = __FAILURE__;
+			}
+			else 
+			{
+				singlylinkedlist_destroy(instance->waiting_to_send);
+				singlylinkedlist_destroy(instance->in_progress_list);
+				instance->waiting_to_send = new_wait_to_send_list;
+				instance->in_progress_list = new_in_progress_list;
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	return result;
+}
+
+static void internal_on_event_send_complete_callback(void* context, MESSAGE_SEND_RESULT send_result)
+{ 
+	if (context != NULL)
+	{
+		MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)context;
+
+		if (task->is_timed_out == false)
+		{
+			MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_send_result;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_107: [If no failure occurs, `task->on_event_send_complete_callback` shall be invoked with result EVENT_SEND_COMPLETE_RESULT_OK]  
+			if (send_result == MESSAGE_SEND_OK)
+			{
+				messenger_send_result = MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK;
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_108: [If a failure occurred, `task->on_event_send_complete_callback` shall be invoked with result EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING] 
+			else
+			{
+				messenger_send_result = MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING;
+			}
+
+			task->on_event_send_complete_callback(task->message, messenger_send_result, (void*)task->context);
+		}
+		else
+		{
+			LogInfo("messenger on_event_send_complete_callback invoked for timed out event %p; not firing upper layer callback.", task->message);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_128: [`task` shall be removed from `instance->in_progress_list`]  
+		remove_event_from_in_progress_list(task);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_130: [`task` shall be destroyed using free()]  
+		free(task);
+	}
+}
+
+static MESSENGER_SEND_EVENT_TASK* get_next_event_to_send(MESSENGER_INSTANCE* instance)
+{
+	MESSENGER_SEND_EVENT_TASK* task;
+	LIST_ITEM_HANDLE list_item;
+
+	if ((list_item = singlylinkedlist_get_head_item(instance->waiting_to_send)) == NULL)
+	{
+		task = NULL;
+	}
+	else
+	{
+		task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item);
+
+		if (singlylinkedlist_remove(instance->waiting_to_send, list_item) != RESULT_OK)
+		{
+			LogError("Failed removing item from waiting_to_send list (singlylinkedlist_remove failed)");
+		}
+	}
+
+	return task;
+}
+
+static int send_pending_events(MESSENGER_INSTANCE* instance)
+{
+	int result = RESULT_OK;
+
+	MESSENGER_SEND_EVENT_TASK* task;
+
+	while ((task = get_next_event_to_send(instance)) != NULL)
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_153: [messenger_do_work() shall move each event to be sent from `instance->wait_to_send_list` to `instance->in_progress_list`] 
+		if (move_event_to_in_progress_list(task) != RESULT_OK)
+		{
+			result = __FAILURE__;
+			task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, (void*)task->context);
+			break;
+		}
+		else
+		{
+			int uamqp_result;
+			MESSAGE_HANDLE amqp_message = NULL;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_154: [A MESSAGE_HANDLE shall be obtained out of the event's IOTHUB_MESSAGE_HANDLE instance by using message_create_from_iothub_message()]  
+			if ((uamqp_result = message_create_from_iothub_message(task->message->messageHandle, &amqp_message)) != RESULT_OK)
+			{
+				LogError("Failed sending event message (failed creating AMQP message; error: %d).", uamqp_result);
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_155: [If message_create_from_iothub_message() fails, `task->on_event_send_complete_callback` shall be invoked with result EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE]  
+				task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE, (void*)task->context);
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_160: [If any failure occurs the event shall be removed from `instance->in_progress_list` and destroyed]  
+				remove_event_from_in_progress_list(task);
+				free(task);
+				
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_156: [If message_create_from_iothub_message() fails, messenger_do_work() shall skip to the next event to be sent]  
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_157: [The MESSAGE_HANDLE shall be submitted for sending using messagesender_send(), passing `internal_on_event_send_complete_callback`]  
+				uamqp_result = messagesender_send(instance->message_sender, amqp_message, internal_on_event_send_complete_callback, task);
+				task->send_time = get_time(NULL);
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_159: [The MESSAGE_HANDLE shall be destroyed using message_destroy().]
+				message_destroy(amqp_message);
+
+				if (uamqp_result != RESULT_OK)
+				{
+					LogError("Failed sending event (messagesender_send failed; error: %d)", uamqp_result);
+
+					result = __FAILURE__;
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_158: [If messagesender_send() fails, `task->on_event_send_complete_callback` shall be invoked with result EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING]
+					task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING, (void*)task->context);
+
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_160: [If any failure occurs the event shall be removed from `instance->in_progress_list` and destroyed]  
+					remove_event_from_in_progress_list(task);
+					free(task);
+
+					break;
+				}
+			}
+		}
+	}
+
+	return result;
+}
+
+// @brief
+//     Goes through each task in in_progress_list and checks if the events timed out to be sent.
+// @remarks
+//     If an event is timed out, it is marked as such but not removed, and the upper layer callback is invoked.
+// @returns
+//     0 if no failures occur, non-zero otherwise.
+static int process_event_send_timeouts(MESSENGER_INSTANCE* instance)
+{
+	int result = RESULT_OK;
+
+	if (instance->event_send_timeout_secs > 0)
+	{
+		LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->in_progress_list);
+
+		while (list_item != NULL)
+		{
+			MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item);
+
+			if (task->is_timed_out == false)
+			{
+				int is_timed_out;
+
+				if (is_timeout_reached(task->send_time, instance->event_send_timeout_secs, &is_timed_out) == RESULT_OK)
+				{
+					if (is_timed_out)
+					{
+						task->is_timed_out = true;
+
+						if (task->on_event_send_complete_callback != NULL)
+						{
+							task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT, task->context);
+						}
+					}
+				}
+				else
+				{
+					LogError("messenger failed to evaluate event send timeout of event %d", task->message);
+					result = __FAILURE__;
+				}
+			}
+
+			list_item = singlylinkedlist_get_next_item(list_item);
+		}
+	}
+
+	return result;
+}
+
+// @brief
+//     Removes all the timed out events from the in_progress_list, without invoking callbacks or detroying the messages.
+static void remove_timed_out_events(MESSENGER_INSTANCE* instance)
+{
+	LIST_ITEM_HANDLE list_item = singlylinkedlist_get_head_item(instance->in_progress_list);
+
+	while (list_item != NULL)
+	{
+		MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_item);
+
+		if (task->is_timed_out == true)
+		{
+			remove_event_from_in_progress_list(task);
+
+			free(task);
+		}
+
+		list_item = singlylinkedlist_get_next_item(list_item);
+	}
+}
+
+
+// ---------- Set/Retrieve Options Helpers ----------//
+
+static void* messenger_clone_option(const char* name, const void* value)
+{
+	void* result;
+
+	if (name == NULL)
+	{
+		LogError("Failed to clone messenger option (name is NULL)");
+		result = NULL;
+	}
+	else if (value == NULL)
+	{
+		LogError("Failed to clone messenger option (value is NULL)");
+		result = NULL;
+	}
+	else
+	{
+		if (strcmp(MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0 ||
+			strcmp(MESSENGER_OPTION_SAVED_OPTIONS, name) == 0)
+		{
+			result = (void*)value;
+		}
+		else
+		{
+			LogError("Failed to clone messenger option (option with name '%s' is not suppported)", name);
+			result = NULL;
+		}
+	}
+
+	return result;
+}
+
+static void messenger_destroy_option(const char* name, const void* value)
+{
+	if (name == NULL)
+	{
+		LogError("Failed to destroy messenger option (name is NULL)");
+	}
+	else if (value == NULL)
+	{
+		LogError("Failed to destroy messenger option (value is NULL)");
+	}
+	else
+	{
+		// Nothing to be done for the supported options.
+	}
+}
+
+
+// Public API:
+
+int messenger_subscribe_for_messages(MESSENGER_HANDLE messenger_handle, ON_MESSENGER_MESSAGE_RECEIVED on_message_received_callback, void* context)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_016: [If `messenger_handle` is NULL, messenger_subscribe_for_messages() shall fail and return __FAILURE__]
+	if (messenger_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("messenger_subscribe_for_messages failed (messenger_handle is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_017: [If `instance->receive_messages` is already true, messenger_subscribe_for_messages() shall fail and return __FAILURE__]
+		if (instance->receive_messages)
+		{
+			result = __FAILURE__;
+			LogError("messenger_subscribe_for_messages failed (messenger already subscribed)");
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_018: [If `on_message_received_callback` is NULL, messenger_subscribe_for_messages() shall fail and return __FAILURE__]
+		else if (on_message_received_callback == NULL)
+		{
+			result = __FAILURE__;
+			LogError("messenger_subscribe_for_messages failed (on_message_received_callback is NULL)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_019: [`on_message_received_callback` shall be saved on `instance->on_message_received_callback`]
+			instance->on_message_received_callback = on_message_received_callback;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_020: [`context` shall be saved on `instance->on_message_received_context`]
+			instance->on_message_received_context = context;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_021: [messenger_subscribe_for_messages() shall set `instance->receive_messages` to true]
+			instance->receive_messages = true;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_022: [If no failures occurr, messenger_subscribe_for_messages() shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int messenger_unsubscribe_for_messages(MESSENGER_HANDLE messenger_handle)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_023: [If `messenger_handle` is NULL, messenger_unsubscribe_for_messages() shall fail and return __FAILURE__]
+	if (messenger_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("messenger_unsubscribe_for_messages failed (messenger_handle is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_024: [If `instance->receive_messages` is already false, messenger_unsubscribe_for_messages() shall fail and return __FAILURE__]
+		if (instance->receive_messages == false)
+		{
+			result = __FAILURE__;
+			LogError("messenger_unsubscribe_for_messages failed (messenger is not subscribed)");
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_025: [messenger_unsubscribe_for_messages() shall set `instance->receive_messages` to false]
+			instance->receive_messages = false;
+			
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_026: [messenger_unsubscribe_for_messages() shall set `instance->on_message_received_callback` to NULL]
+			instance->on_message_received_callback = NULL;
+			
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_027: [messenger_unsubscribe_for_messages() shall set `instance->on_message_received_context` to NULL]
+			instance->on_message_received_context = NULL;
+			
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_028: [If no failures occurr, messenger_unsubscribe_for_messages() shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int messenger_send_message_disposition(MESSENGER_HANDLE messenger_handle, MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, MESSENGER_DISPOSITION_RESULT disposition_result)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_179: [If `messenger_handle` or `disposition_info` are NULL, messenger_send_message_disposition() shall fail and return __FAILURE__]  
+	if (messenger_handle == NULL || disposition_info == NULL)
+	{
+		LogError("Failed sending message disposition (either messenger_handle (%p) or disposition_info (%p) are NULL)", messenger_handle, disposition_info);
+		result = __FAILURE__;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_180: [If `disposition_info->source` is NULL, messenger_send_message_disposition() shall fail and return __FAILURE__]  
+	else if (disposition_info->source == NULL)
+	{
+		LogError("Failed sending message disposition (disposition_info->source is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		MESSENGER_INSTANCE* messenger = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_189: [If `messenger_handle->message_receiver` is NULL, messenger_send_message_disposition() shall fail and return __FAILURE__]
+		if (messenger->message_receiver == NULL)
+		{
+			LogError("Failed sending message disposition (message_receiver is not created; check if it is subscribed)");
+			result = __FAILURE__;
+		}
+		else
+		{
+			AMQP_VALUE uamqp_disposition_result;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_181: [An AMQP_VALUE disposition result shall be created corresponding to the `disposition_result` provided]
+			if ((uamqp_disposition_result = create_uamqp_disposition_result_from(disposition_result)) == NULL)
+			{
+				LogError("Failed sending message disposition (disposition result %d is not supported)", disposition_result);
+				result = __FAILURE__;
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_182: [`messagereceiver_send_message_disposition()` shall be invoked passing `disposition_info->source`, `disposition_info->message_id` and the corresponding AMQP_VALUE disposition result]  
+				if (messagereceiver_send_message_disposition(messenger->message_receiver, disposition_info->source, disposition_info->message_id, uamqp_disposition_result) != RESULT_OK)
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_183: [If `messagereceiver_send_message_disposition()` fails, messenger_send_message_disposition() shall fail and return __FAILURE__]  
+					LogError("Failed sending message disposition (messagereceiver_send_message_disposition failed)");
+					result = __FAILURE__;
+				}
+				else
+				{
+					// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_185: [If no failures occurr, messenger_send_message_disposition() shall return 0]  
+					result = RESULT_OK;
+				}
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_184: [messenger_send_message_disposition() shall destroy the AMQP_VALUE disposition result]
+				amqpvalue_destroy(uamqp_disposition_result);
+			}
+		}
+	}
+
+	return result;
+}
+
+int messenger_send_async(MESSENGER_HANDLE messenger_handle, IOTHUB_MESSAGE_LIST* message, ON_MESSENGER_EVENT_SEND_COMPLETE on_messenger_event_send_complete_callback, void* context)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_134: [If `messenger_handle` is NULL, messenger_send_async() shall fail and return a non-zero value]  
+	if (messenger_handle == NULL)
+	{
+		LogError("Failed sending event (messenger_handle is NULL)");
+		result = __FAILURE__;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_135: [If `message` is NULL, messenger_send_async() shall fail and return a non-zero value]  
+	else if (message == NULL)
+	{
+		LogError("Failed sending event (message is NULL)");
+		result = __FAILURE__;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_136: [If `on_event_send_complete_callback` is NULL, messenger_send_async() shall fail and return a non-zero value] 
+	else if (on_messenger_event_send_complete_callback == NULL)
+	{
+		LogError("Failed sending event (on_event_send_complete_callback is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		MESSENGER_SEND_EVENT_TASK *task;
+		MESSENGER_INSTANCE *instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_137: [messenger_send_async() shall allocate memory for a MESSENGER_SEND_EVENT_TASK structure (aka `task`)]  
+		if ((task = (MESSENGER_SEND_EVENT_TASK*)malloc(sizeof(MESSENGER_SEND_EVENT_TASK))) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_138: [If malloc() fails, messenger_send_async() shall fail and return a non-zero value]
+			LogError("Failed sending event (failed to create struct for task; malloc failed)");
+			result = __FAILURE__;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_100: [`task` shall be added to `instance->waiting_to_send` using singlylinkedlist_add()]  
+		else if (singlylinkedlist_add(instance->waiting_to_send, task) == NULL)
+		{
+			LogError("Failed sending event (singlylinkedlist_add failed)");
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_139: [If singlylinkedlist_add() fails, messenger_send_async() shall fail and return a non-zero value]
+			result = __FAILURE__;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_142: [If any failure occurs, messenger_send_async() shall free any memory it has allocated]
+			free(task);
+		}
+		else
+		{
+			memset(task, 0, sizeof(MESSENGER_SEND_EVENT_TASK));
+			task->message = message;
+			task->on_event_send_complete_callback = on_messenger_event_send_complete_callback;
+			task->context = context;
+			task->send_time = INDEFINITE_TIME;
+			task->messenger = instance;
+			task->is_timed_out = false;
+			
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_143: [If no failures occur, messenger_send_async() shall return zero]  
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int messenger_get_send_status(MESSENGER_HANDLE messenger_handle, MESSENGER_SEND_STATUS* send_status)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_144: [If `messenger_handle` is NULL, messenger_get_send_status() shall fail and return a non-zero value] 
+	if (messenger_handle == NULL)
+	{
+		LogError("messenger_get_send_status failed (messenger_handle is NULL)");
+		result = __FAILURE__;
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_145: [If `send_status` is NULL, messenger_get_send_status() shall fail and return a non-zero value] 
+	else if (send_status == NULL)
+	{
+		LogError("messenger_get_send_status failed (send_status is NULL)");
+		result = __FAILURE__;
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+		LIST_ITEM_HANDLE wts_list_head = singlylinkedlist_get_head_item(instance->waiting_to_send);
+		LIST_ITEM_HANDLE ip_list_head = singlylinkedlist_get_head_item(instance->in_progress_list);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_147: [If `instance->in_progress_list` and `instance->wait_to_send_list` are empty, send_status shall be set to MESSENGER_SEND_STATUS_IDLE] 
+		if (wts_list_head == NULL && ip_list_head == NULL)
+		{
+			*send_status = MESSENGER_SEND_STATUS_IDLE;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_148: [Otherwise, send_status shall be set to MESSENGER_SEND_STATUS_BUSY] 
+		else
+		{
+			*send_status = MESSENGER_SEND_STATUS_BUSY;
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_149: [If no failures occur, messenger_get_send_status() shall return 0]
+		result = RESULT_OK;
+	}
+
+	return result;
+}
+
+int messenger_start(MESSENGER_HANDLE messenger_handle, SESSION_HANDLE session_handle)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_029: [If `messenger_handle` is NULL, messenger_start() shall fail and return __FAILURE__]
+	if (messenger_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("messenger_start failed (messenger_handle is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_030: [If `session_handle` is NULL, messenger_start() shall fail and return __FAILURE__]
+	else if (session_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("messenger_start failed (session_handle is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_031: [If `instance->state` is not MESSENGER_STATE_STOPPED, messenger_start() shall fail and return __FAILURE__]
+		if (instance->state != MESSENGER_STATE_STOPPED)
+		{
+			result = __FAILURE__;
+			LogError("messenger_start failed (current state is %d; expected MESSENGER_STATE_STOPPED)", instance->state);
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_032: [`session_handle` shall be saved on `instance->session_handle`]
+			instance->session_handle = session_handle;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_115: [If no failures occurr, `instance->state` shall be set to MESSENGER_STATE_STARTING, and `instance->on_state_changed_callback` invoked if provided]
+			update_messenger_state(instance, MESSENGER_STATE_STARTING);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_056: [If no failures occurr, messenger_start() shall return 0]
+			result = RESULT_OK;
+		}
+	}
+
+	return result;
+}
+
+int messenger_stop(MESSENGER_HANDLE messenger_handle)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_057: [If `messenger_handle` is NULL, messenger_stop() shall fail and return a non-zero value]
+	if (messenger_handle == NULL)
+	{
+		result = __FAILURE__;
+		LogError("messenger_stop failed (messenger_handle is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_058: [If `instance->state` is MESSENGER_STATE_STOPPED, messenger_stop() shall fail and return a non-zero value]
+		if (instance->state == MESSENGER_STATE_STOPPED)
+		{
+			result = __FAILURE__;
+			LogError("messenger_stop failed (messenger is already stopped)");
+		}
+		else
+		{
+			update_messenger_state(instance, MESSENGER_STATE_STOPPING);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_152: [messenger_stop() shall close and destroy `instance->message_sender` and `instance->message_receiver`]  
+			destroy_event_sender(instance);
+			destroy_message_receiver(instance);
+
+			remove_timed_out_events(instance);
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_162: [messenger_stop() shall move all items from `instance->in_progress_list` to the beginning of `instance->wait_to_send_list`]
+			if (move_events_to_wait_to_send_list(instance) != RESULT_OK)
+			{
+				LogError("Messenger failed to move events in progress back to wait_to_send list");
+				
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_163: [If not all items from `instance->in_progress_list` can be moved back to `instance->wait_to_send_list`, `instance->state` shall be set to MESSENGER_STATE_ERROR, and `instance->on_state_changed_callback` invoked]
+				update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				result = __FAILURE__;
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_164: [If all items get successfuly moved back to `instance->wait_to_send_list`, `instance->state` shall be set to MESSENGER_STATE_STOPPED, and `instance->on_state_changed_callback` invoked]
+				update_messenger_state(instance, MESSENGER_STATE_STOPPED);
+				result = RESULT_OK;
+			}
+		}
+	}
+
+	return result;
+}
+
+// @brief
+//     Sets the messenger module state based on the state changes from messagesender and messagereceiver
+static void process_state_changes(MESSENGER_INSTANCE* instance)
+{
+	// Note: messagesender and messagereceiver are still not created or already destroyed 
+	//       when state is MESSENGER_STATE_STOPPED, so no checking is needed there.
+
+	if (instance->state == MESSENGER_STATE_STARTED)
+	{
+		if (instance->message_sender_current_state != MESSAGE_SENDER_STATE_OPEN)
+		{
+			LogError("messagesender reported unexpected state %d while messenger was started", instance->message_sender_current_state);
+			update_messenger_state(instance, MESSENGER_STATE_ERROR);
+		}
+		else if (instance->message_receiver != NULL && instance->message_receiver_current_state != MESSAGE_RECEIVER_STATE_OPEN)
+		{
+			if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_OPENING)
+			{
+				int is_timed_out;
+				if (is_timeout_reached(instance->last_message_receiver_state_change_time, MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK)
+				{
+					LogError("messenger got an error (failed to verify messagereceiver start timeout)");
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+				else if (is_timed_out == 1)
+				{
+					LogError("messenger got an error (messagereceiver failed to start within expected timeout (%d secs))", MAX_MESSAGE_RECEIVER_STATE_CHANGE_TIMEOUT_SECS);
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+			}
+			else if (instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_ERROR ||
+				instance->message_receiver_current_state == MESSAGE_RECEIVER_STATE_IDLE)
+			{
+				LogError("messagereceiver reported unexpected state %d while messenger is starting", instance->message_receiver_current_state);
+				update_messenger_state(instance, MESSENGER_STATE_ERROR);
+			}
+		}
+	}
+	else
+	{
+		if (instance->state == MESSENGER_STATE_STARTING)
+		{
+			if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPEN)
+			{
+				update_messenger_state(instance, MESSENGER_STATE_STARTED);
+			}
+			else if (instance->message_sender_current_state == MESSAGE_SENDER_STATE_OPENING)
+			{
+				int is_timed_out;
+				if (is_timeout_reached(instance->last_message_sender_state_change_time, MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS, &is_timed_out) != RESULT_OK)
+				{
+					LogError("messenger failed to start (failed to verify messagesender start timeout)");
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+				else if (is_timed_out == 1)
+				{
+					LogError("messenger failed to start (messagesender failed to start within expected timeout (%d secs))", MAX_MESSAGE_SENDER_STATE_CHANGE_TIMEOUT_SECS);
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+			}
+			// For this module, the only valid scenario where messagesender state is IDLE is if 
+			// the messagesender hasn't been created yet or already destroyed.
+			else if ((instance->message_sender_current_state == MESSAGE_SENDER_STATE_ERROR) ||
+				(instance->message_sender_current_state == MESSAGE_SENDER_STATE_CLOSING) ||
+				(instance->message_sender_current_state == MESSAGE_SENDER_STATE_IDLE && instance->message_sender != NULL))
+			{
+				LogError("messagesender reported unexpected state %d while messenger is starting", instance->message_sender_current_state);
+				update_messenger_state(instance, MESSENGER_STATE_ERROR);
+			}
+		}
+		// message sender and receiver are stopped/destroyed synchronously, so no need for state control.
+	}
+}
+
+void messenger_do_work(MESSENGER_HANDLE messenger_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_065: [If `messenger_handle` is NULL, messenger_do_work() shall fail and return]
+	if (messenger_handle == NULL)
+	{
+		LogError("messenger_do_work failed (messenger_handle is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		process_state_changes(instance);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_151: [If `instance->state` is MESSENGER_STATE_STARTING, messenger_do_work() shall create and open `instance->message_sender`]
+		if (instance->state == MESSENGER_STATE_STARTING)
+		{
+			if (instance->message_sender == NULL)
+			{
+				if (create_event_sender(instance) != RESULT_OK)
+				{
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+			}
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_066: [If `instance->state` is not MESSENGER_STATE_STARTED, messenger_do_work() shall return]
+		else if (instance->state == MESSENGER_STATE_STARTED)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_067: [If `instance->receive_messages` is true and `instance->message_receiver` is NULL, a message_receiver shall be created]
+			if (instance->receive_messages == true &&
+				instance->message_receiver == NULL &&
+				create_message_receiver(instance) != RESULT_OK)
+			{
+				LogError("messenger_do_work warning (failed creating the message receiver [%s])", STRING_c_str(instance->device_id));
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_092: [If `instance->receive_messages` is false and `instance->message_receiver` is not NULL, it shall be destroyed]
+			else if (instance->receive_messages == false && instance->message_receiver != NULL)
+			{
+				destroy_message_receiver(instance);
+			}
+
+			if (process_event_send_timeouts(instance) != RESULT_OK)
+			{
+				update_messenger_state(instance, MESSENGER_STATE_ERROR);
+			}
+			else if (send_pending_events(instance) != RESULT_OK && instance->event_send_retry_limit > 0)
+			{
+				instance->event_send_error_count++;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_161: [If messenger_do_work() fail sending events for `instance->event_send_retry_limit` times in a row, it shall invoke `instance->on_state_changed_callback`, if provided, with error code MESSENGER_STATE_ERROR]
+				if (instance->event_send_error_count >= instance->event_send_retry_limit)
+				{
+					LogError("messenger_do_work failed (failed sending events; reached max number of consecutive attempts)");
+					update_messenger_state(instance, MESSENGER_STATE_ERROR);
+				}
+			}
+			else
+			{
+				instance->event_send_error_count = 0;
+			}
+		}
+	}
+}
+
+void messenger_destroy(MESSENGER_HANDLE messenger_handle)
+{
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_109: [If `messenger_handle` is NULL, messenger_destroy() shall fail and return]
+	if (messenger_handle == NULL)
+	{
+		LogError("messenger_destroy failed (messenger_handle is NULL)");
+	}
+	else
+	{
+		LIST_ITEM_HANDLE list_node;
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_110: [If the `instance->state` is not MESSENGER_STATE_STOPPED, messenger_destroy() shall invoke messenger_stop()]
+		if (instance->state != MESSENGER_STATE_STOPPED)
+		{
+			(void)messenger_stop(messenger_handle);
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_111: [All elements of `instance->in_progress_list` and `instance->wait_to_send_list` shall be removed, invoking `task->on_event_send_complete_callback` for each with EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED]
+
+		// Note: yes messenger_stop() tried to move all events from in_progress_list to wait_to_send_list, 
+		//       but we need to iterate through in case any events failed to be moved.
+		while ((list_node = singlylinkedlist_get_head_item(instance->in_progress_list)) != NULL)
+		{
+			MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_node);
+
+			(void)singlylinkedlist_remove(instance->in_progress_list, list_node);
+
+			if (task != NULL)
+			{
+				task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED, (void*)task->context);
+				free(task);
+			}
+		}
+
+		while ((list_node = singlylinkedlist_get_head_item(instance->waiting_to_send)) != NULL)
+		{
+			MESSENGER_SEND_EVENT_TASK* task = (MESSENGER_SEND_EVENT_TASK*)singlylinkedlist_item_get_value(list_node);
+
+			(void)singlylinkedlist_remove(instance->waiting_to_send, list_node);
+
+			if (task != NULL)
+			{
+				task->on_event_send_complete_callback(task->message, MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED, (void*)task->context);
+				free(task);
+			}
+		}
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_150: [`instance->in_progress_list` and `instance->wait_to_send_list` shall be destroyed using singlylinkedlist_destroy()]
+		singlylinkedlist_destroy(instance->waiting_to_send);
+		singlylinkedlist_destroy(instance->in_progress_list);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_112: [`instance->iothub_host_fqdn` shall be destroyed using STRING_delete()]
+		STRING_delete(instance->iothub_host_fqdn);
+		
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_113: [`instance->device_id` shall be destroyed using STRING_delete()]
+		STRING_delete(instance->device_id);
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_114: [messenger_destroy() shall destroy `instance` with free()]
+		(void)free(instance);
+	}
+}
+
+MESSENGER_HANDLE messenger_create(const MESSENGER_CONFIG* messenger_config)
+{
+	MESSENGER_HANDLE handle;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_001: [If parameter `messenger_config` is NULL, messenger_create() shall return NULL]
+	if (messenger_config == NULL)
+	{
+		handle = NULL;
+		LogError("messenger_create failed (messenger_config is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_002: [If `messenger_config->device_id` is NULL, messenger_create() shall return NULL]
+	else if (messenger_config->device_id == NULL)
+	{
+		handle = NULL;
+		LogError("messenger_create failed (messenger_config->device_id is NULL)");
+	}
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_003: [If `messenger_config->iothub_host_fqdn` is NULL, messenger_create() shall return NULL]
+	else if (messenger_config->iothub_host_fqdn == NULL)
+	{
+		handle = NULL;
+		LogError("messenger_create failed (messenger_config->iothub_host_fqdn is NULL)");
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_006: [messenger_create() shall allocate memory for the messenger instance structure (aka `instance`)]
+		if ((instance = (MESSENGER_INSTANCE*)malloc(sizeof(MESSENGER_INSTANCE))) == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_007: [If malloc() fails, messenger_create() shall fail and return NULL]
+			handle = NULL;
+			LogError("messenger_create failed (messenger_config->wait_to_send_list is NULL)");
+		}
+		else
+		{
+			memset(instance, 0, sizeof(MESSENGER_INSTANCE));
+			instance->state = MESSENGER_STATE_STOPPED;
+			instance->message_sender_current_state = MESSAGE_SENDER_STATE_IDLE;
+			instance->message_sender_previous_state = MESSAGE_SENDER_STATE_IDLE;
+			instance->message_receiver_current_state = MESSAGE_RECEIVER_STATE_IDLE;
+			instance->message_receiver_previous_state = MESSAGE_RECEIVER_STATE_IDLE;
+			instance->event_send_retry_limit = DEFAULT_EVENT_SEND_RETRY_LIMIT;
+			instance->event_send_timeout_secs = DEFAULT_EVENT_SEND_TIMEOUT_SECS;
+			instance->last_message_sender_state_change_time = INDEFINITE_TIME;
+			instance->last_message_receiver_state_change_time = INDEFINITE_TIME;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_008: [messenger_create() shall save a copy of `messenger_config->device_id` into `instance->device_id`]
+			if ((instance->device_id = STRING_construct(messenger_config->device_id)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_009: [If STRING_construct() fails, messenger_create() shall fail and return NULL]
+				handle = NULL;
+				LogError("messenger_create failed (device_id could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_010: [messenger_create() shall save a copy of `messenger_config->iothub_host_fqdn` into `instance->iothub_host_fqdn`]
+			else if ((instance->iothub_host_fqdn = STRING_construct(messenger_config->iothub_host_fqdn)) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_011: [If STRING_construct() fails, messenger_create() shall fail and return NULL]
+				handle = NULL;
+				LogError("messenger_create failed (iothub_host_fqdn could not be copied; STRING_construct failed)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_165: [`instance->wait_to_send_list` shall be set using singlylinkedlist_create()]
+			else if ((instance->waiting_to_send = singlylinkedlist_create()) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_166: [If singlylinkedlist_create() fails, messenger_create() shall fail and return NULL]  
+				handle = NULL;
+				LogError("messenger_create failed (singlylinkedlist_create failed to create wait_to_send_list)");
+			}
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_132: [`instance->in_progress_list` shall be set using singlylinkedlist_create()]  
+			else if ((instance->in_progress_list = singlylinkedlist_create()) == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_133: [If singlylinkedlist_create() fails, messenger_create() shall fail and return NULL] 
+				handle = NULL;
+				LogError("messenger_create failed (singlylinkedlist_create failed to create in_progress_list)");
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_013: [`messenger_config->on_state_changed_callback` shall be saved into `instance->on_state_changed_callback`]
+				instance->on_state_changed_callback = messenger_config->on_state_changed_callback;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_014: [`messenger_config->on_state_changed_context` shall be saved into `instance->on_state_changed_context`]
+				instance->on_state_changed_context = messenger_config->on_state_changed_context;
+
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_015: [If no failures occurr, messenger_create() shall return a handle to `instance`]
+				handle = (MESSENGER_HANDLE)instance;
+			}
+		}
+
+		if (handle == NULL)
+		{
+			messenger_destroy((MESSENGER_HANDLE)instance);
+		}
+	}
+
+	return handle;
+}
+
+int messenger_set_option(MESSENGER_HANDLE messenger_handle, const char* name, void* value)
+{
+	int result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_167: [If `messenger_handle` or `name` or `value` is NULL, messenger_set_option shall fail and return a non-zero value]
+	if (messenger_handle == NULL || name == NULL || value == NULL)
+	{
+		LogError("messenger_set_option failed (one of the followin are NULL: messenger_handle=%p, name=%p, value=%p)",
+			messenger_handle, name, value);
+		result = __FAILURE__;
+	}
+	else
+	{
+		MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_168: [If name matches MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, `value` shall be saved on `instance->event_send_timeout_secs`]
+		if (strcmp(MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, name) == 0)
+		{
+			instance->event_send_timeout_secs = *((size_t*)value);
+			result = RESULT_OK;
+		}
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_169: [If name matches MESSENGER_OPTION_SAVED_OPTIONS, `value` shall be applied using OptionHandler_FeedOptions]
+		else if (strcmp(MESSENGER_OPTION_SAVED_OPTIONS, name) == 0)
+		{
+			if (OptionHandler_FeedOptions((OPTIONHANDLER_HANDLE)value, messenger_handle) != OPTIONHANDLER_OK)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_170: [If OptionHandler_FeedOptions fails, messenger_set_option shall fail and return a non-zero value]
+				LogError("messenger_set_option failed (OptionHandler_FeedOptions failed)");
+				result = __FAILURE__;
+			}
+			else
+			{
+				result = RESULT_OK;
+			}
+		}
+		else
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_171: [If name does not match any supported option, authentication_set_option shall fail and return a non-zero value]
+			LogError("messenger_set_option failed (option with name '%s' is not suppported)", name);
+			result = __FAILURE__;
+		}
+	}
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_172: [If no errors occur, messenger_set_option shall return 0]
+	return result;
+}
+
+OPTIONHANDLER_HANDLE messenger_retrieve_options(MESSENGER_HANDLE messenger_handle)
+{
+	OPTIONHANDLER_HANDLE result;
+
+	// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_173: [If `messenger_handle` is NULL, messenger_retrieve_options shall fail and return NULL]
+	if (messenger_handle == NULL)
+	{
+		LogError("Failed to retrieve options from messenger instance (messenger_handle is NULL)");
+		result = NULL;
+	}
+	else
+	{
+		// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_174: [An OPTIONHANDLER_HANDLE instance shall be created using OptionHandler_Create]
+		OPTIONHANDLER_HANDLE options = OptionHandler_Create(messenger_clone_option, messenger_destroy_option, (pfSetOption)messenger_set_option);
+
+		if (options == NULL)
+		{
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_175: [If an OPTIONHANDLER_HANDLE instance fails to be created, messenger_retrieve_options shall fail and return NULL]
+			LogError("Failed to retrieve options from messenger instance (OptionHandler_Create failed)");
+			result = NULL;
+		}
+		else
+		{
+			MESSENGER_INSTANCE* instance = (MESSENGER_INSTANCE*)messenger_handle;
+
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_176: [Each option of `instance` shall be added to the OPTIONHANDLER_HANDLE instance using OptionHandler_AddOption]
+			// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_177: [If OptionHandler_AddOption fails, messenger_retrieve_options shall fail and return NULL]
+			if (OptionHandler_AddOption(options, MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS, (void*)&instance->event_send_timeout_secs) != OPTIONHANDLER_OK)
+			{
+				LogError("Failed to retrieve options from messenger instance (OptionHandler_Create failed for option '%s')", MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS);
+				result = NULL;
+			}
+			else
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_179: [If no failures occur, messenger_retrieve_options shall return the OPTIONHANDLER_HANDLE instance]
+				result = options;
+			}
+
+			if (result == NULL)
+			{
+				// Codes_SRS_IOTHUBTRANSPORT_AMQP_MESSENGER_09_178: [If messenger_retrieve_options fails, any allocated memory shall be freed]
+				OptionHandler_Destroy(options);
+			}
+		}
+	}
+
+	return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/iothubtransport_amqp_messenger.h	Fri Mar 10 11:46:55 2017 -0800
@@ -0,0 +1,91 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef IOTHUBTRANSPORT_AMQP_MESSENGER
+#define IOTHUBTRANSPORT_AMQP_MESSENGER
+
+#include "azure_c_shared_utility/umock_c_prod.h"
+#include "azure_c_shared_utility/optionhandler.h"
+#include "azure_uamqp_c/session.h"
+#include "iothub_client_private.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+static const char* MESSENGER_OPTION_EVENT_SEND_TIMEOUT_SECS = "event_send_timeout_secs";
+static const char* MESSENGER_OPTION_SAVED_OPTIONS = "saved_messenger_options";
+
+typedef struct MESSENGER_INSTANCE* MESSENGER_HANDLE;
+
+typedef enum MESSENGER_SEND_STATUS_TAG
+{
+	MESSENGER_SEND_STATUS_IDLE,
+	MESSENGER_SEND_STATUS_BUSY
+} MESSENGER_SEND_STATUS;
+
+typedef enum MESSENGER_EVENT_SEND_COMPLETE_RESULT_TAG
+{
+	MESSENGER_EVENT_SEND_COMPLETE_RESULT_OK,
+	MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_CANNOT_PARSE,
+	MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_FAIL_SENDING,
+	MESSENGER_EVENT_SEND_COMPLETE_RESULT_ERROR_TIMEOUT,
+	MESSENGER_EVENT_SEND_COMPLETE_RESULT_MESSENGER_DESTROYED
+} MESSENGER_EVENT_SEND_COMPLETE_RESULT;
+
+typedef enum MESSENGER_DISPOSITION_RESULT_TAG
+{
+	MESSENGER_DISPOSITION_RESULT_NONE,
+	MESSENGER_DISPOSITION_RESULT_ACCEPTED,
+	MESSENGER_DISPOSITION_RESULT_REJECTED,
+	MESSENGER_DISPOSITION_RESULT_RELEASED
+} MESSENGER_DISPOSITION_RESULT;
+
+typedef enum MESSENGER_STATE_TAG
+{
+	MESSENGER_STATE_STARTING,
+	MESSENGER_STATE_STARTED,
+	MESSENGER_STATE_STOPPING,
+	MESSENGER_STATE_STOPPED,
+	MESSENGER_STATE_ERROR
+} MESSENGER_STATE;
+
+typedef struct MESSENGER_MESSAGE_DISPOSITION_INFO_TAG
+{
+	delivery_number message_id;
+	char* source;
+} MESSENGER_MESSAGE_DISPOSITION_INFO;
+
+typedef void(*ON_MESSENGER_EVENT_SEND_COMPLETE)(IOTHUB_MESSAGE_LIST* iothub_message_list, MESSENGER_EVENT_SEND_COMPLETE_RESULT messenger_event_send_complete_result, void* context);
+typedef void(*ON_MESSENGER_STATE_CHANGED_CALLBACK)(void* context, MESSENGER_STATE previous_state, MESSENGER_STATE new_state);
+typedef MESSENGER_DISPOSITION_RESULT(*ON_MESSENGER_MESSAGE_RECEIVED)(IOTHUB_MESSAGE_HANDLE message, MESSENGER_MESSAGE_DISPOSITION_INFO* disposition_info, void* context);
+
+typedef struct MESSENGER_CONFIG_TAG
+{
+	char* device_id;
+	char* iothub_host_fqdn;
+	ON_MESSENGER_STATE_CHANGED_CALLBACK on_state_changed_callback;
+	void* on_state_changed_context;
+} MESSENGER_CONFIG;
+
+MOCKABLE_FUNCTION(, MESSENGER_HANDLE, messenger_create, const MESSENGER_CONFIG*, messenger_config);
+MOCKABLE_FUNCTION(, int, messenger_send_async, MESSENGER_HANDLE, messenger_handle, IOTHUB_MESSAGE_LIST*, message, ON_MESSENGER_EVENT_SEND_COMPLETE, on_messenger_event_send_complete_callback, void*, context);
+MOCKABLE_FUNCTION(, int, messenger_subscribe_for_messages, MESSENGER_HANDLE, messenger_handle, ON_MESSENGER_MESSAGE_RECEIVED, on_message_received_callback, void*, context);
+MOCKABLE_FUNCTION(, int, messenger_unsubscribe_for_messages, MESSENGER_HANDLE, messenger_handle);
+MOCKABLE_FUNCTION(, int, messenger_send_message_disposition, MESSENGER_HANDLE, messenger_handle, MESSENGER_MESSAGE_DISPOSITION_INFO*, disposition_info, MESSENGER_DISPOSITION_RESULT, disposition_result);
+MOCKABLE_FUNCTION(, int, messenger_get_send_status, MESSENGER_HANDLE, messenger_handle, MESSENGER_SEND_STATUS*, send_status);
+MOCKABLE_FUNCTION(, int, messenger_start, MESSENGER_HANDLE, messenger_handle, SESSION_HANDLE, session_handle);
+MOCKABLE_FUNCTION(, int, messenger_stop, MESSENGER_HANDLE, messenger_handle);
+MOCKABLE_FUNCTION(, void, messenger_do_work, MESSENGER_HANDLE, messenger_handle);
+MOCKABLE_FUNCTION(, void, messenger_destroy, MESSENGER_HANDLE, messenger_handle);
+MOCKABLE_FUNCTION(, int, messenger_set_option, MESSENGER_HANDLE, messenger_handle, const char*, name, void*, value);
+MOCKABLE_FUNCTION(, OPTIONHANDLER_HANDLE, messenger_retrieve_options, MESSENGER_HANDLE, messenger_handle);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*IOTHUBTRANSPORT_AMQP_MESSENGER*/
--- a/iothubtransportamqp.c	Fri Feb 24 14:00:00 2017 -0800
+++ b/iothubtransportamqp.c	Fri Mar 10 11:46:55 2017 -0800
@@ -15,6 +15,9 @@
     TLSIO_CONFIG tls_io_config;
     tls_io_config.hostname = fqdn;
     tls_io_config.port = DEFAULT_IOTHUB_AMQP_PORT;
+    tls_io_config.underlying_io_interface = NULL;
+    tls_io_config.underlying_io_parameters = NULL;
+
     // Codes_SRS_IOTHUBTRANSPORTAMQP_09_002: [getTLSIOTransport shall get `io_interface_description` using platform_get_default_tlsio())]
     const IO_INTERFACE_DESCRIPTION* io_interface_description = platform_get_default_tlsio();
 
@@ -120,6 +123,7 @@
 
 static int IoTHubTransportAMQP_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds)
 {
+    // Codes_SRS_IOTHUBTRANSPORTAMQP_09_020: [IoTHubTransportAMQP_SetRetryPolicy shall call into the IoTHubTransport_AMQP_Common_SetRetryPolicy().]
     return IoTHubTransport_AMQP_Common_SetRetryPolicy(handle, retryPolicy, retryTimeoutLimitInSeconds);
 }
 
@@ -129,8 +133,15 @@
     return IoTHubTransport_AMQP_Common_GetHostname(handle);
 }
 
+static IOTHUB_CLIENT_RESULT IoTHubTransportAMQP_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition)
+{
+    // Codes_SRS_IOTHUBTRANSPORTAMQP_10_001: [IoTHubTransportAMQP_SendMessageDisposition shall send the message disposition by calling into the IoTHubTransport_AMQP_Common_SendMessageDispostion().]
+    return IoTHubTransport_AMQP_Common_SendMessageDisposition(message_data, disposition);
+}
+
 static TRANSPORT_PROVIDER thisTransportProvider = 
 {
+    IoTHubTransportAMQP_SendMessageDisposition,     /*pfIotHubTransport_Send_Message_Disposition IoTHubTransport_Send_Message_Disposition;*/
     IoTHubTransportAMQP_Subscribe_DeviceMethod,     /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/
     IoTHubTransportAMQP_Unsubscribe_DeviceMethod,   /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/
     IoTHubTransportAMQP_DeviceMethod_Response,
@@ -151,6 +162,7 @@
 };
 
 /* Codes_SRS_IOTHUBTRANSPORTAMQP_09_019: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for it's fields:
+IoTHubTransport_SendMessageDisposition = IoTHubTransportAMQP_SendMessageDisposition
 IoTHubTransport_Subscribe_DeviceMethod = IoTHubTransportAMQP_Subscribe_DeviceMethod
 IoTHubTransport_Unsubscribe_DeviceMethod = IoTHubTransportAMQP_Unsubscribe_DeviceMethod
 IoTHubTransport_Subscribe_DeviceTwin = IoTHubTransportAMQP_Subscribe_DeviceTwin
@@ -162,6 +174,7 @@
 IoTHubTransport_Subscribe = IoTHubTransportAMQP_Subscribe
 IoTHubTransport_Unsubscribe = IoTHubTransportAMQP_Unsubscribe
 IoTHubTransport_DoWork = IoTHubTransportAMQP_DoWork
+IoTHubTransport_SetRetryPolicy = IoTHubTransportAMQP_SetRetryPolicy
 IoTHubTransport_SetOption = IoTHubTransportAMQP_SetOption]*/
 extern const TRANSPORT_PROVIDER* AMQP_Protocol(void)
 {
--- a/iothubtransportamqp_auth.c	Fri Feb 24 14:00:00 2017 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,721 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-#include "iothubtransportamqp_auth.h"
-#include "azure_c_shared_utility/optimize_size.h"
-#include "azure_c_shared_utility/agenttime.h" 
-
-#define RESULT_OK 0
-#define INDEFINITE_TIME ((time_t)(-1))
-#define SAS_TOKEN_TYPE "servicebus.windows.net:sastoken"
-
-typedef struct AMQP_TRANSPORT_CBS_STATE_TAG
-{
-	// A component of the SAS token. Currently this must be an empty string.
-	STRING_HANDLE sasTokenKeyName;
-	// Time when the current SAS token was created, in seconds since epoch.
-	size_t current_sas_token_create_time;
-	// Time when the current SAS token was put to CBS, in seconds since epoch.
-	size_t current_sas_token_put_time;
-} AMQP_TRANSPORT_CBS_STATE;
-
-typedef struct AUTHENTICATION_STATE_TAG
-{
-	STRING_HANDLE device_id;
-
-	STRING_HANDLE iot_hub_host_fqdn;
-
-	const AMQP_TRANSPORT_CBS_CONNECTION* cbs_connection;
-
-	AMQP_TRANSPORT_CREDENTIAL credential;
-
-	AMQP_TRANSPORT_CBS_STATE cbs_state;
-
-	AUTHENTICATION_STATUS status;
-} AUTHENTICATION_STATE;
-
-static int getSecondsSinceEpoch(size_t* seconds)
-{
-	int result;
-	time_t current_time;
-
-	if ((current_time = get_time(NULL)) == INDEFINITE_TIME)
-	{
-		LogError("Failed getting the current local time (get_time() failed)");
-		result = __FAILURE__;
-	}
-	else
-	{
-		*seconds = (size_t)get_difftime(current_time, (time_t)0);
-
-		result = RESULT_OK;
-	}
-
-	return result;
-}
-
-static void on_put_token_complete(void* context, CBS_OPERATION_RESULT operation_result, unsigned int status_code, const char* status_description)
-{
-#ifdef NO_LOGGING
-	UNUSED(status_code);
-	UNUSED(status_description);
-#endif
-
-	AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)context;
-
-	if (operation_result == CBS_OPERATION_RESULT_OK)
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_041: [When cbs_put_token() calls back, if the result is CBS_OPERATION_RESULT_OK the state status shall be set to AUTHENTICATION_STATUS_OK]
-		auth_state->status = AUTHENTICATION_STATUS_OK;
-	}
-	else
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_042: [When cbs_put_token() calls back, if the result is not CBS_OPERATION_RESULT_OK the state status shall be set to AUTHENTICATION_STATUS_FAILURE]
-		auth_state->status = AUTHENTICATION_STATUS_FAILURE;
-		LogError("CBS reported status code %u, error: %s for put token operation", status_code, status_description);
-	}
-}
-
-static void on_delete_token_complete(void* context, CBS_OPERATION_RESULT operation_result, unsigned int status_code, const char* status_description)
-{
-#ifdef NO_LOGGING
-	UNUSED(status_code);
-	UNUSED(status_description);
-#endif
-
-	AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)context;
-
-	if (operation_result == CBS_OPERATION_RESULT_OK)
-	{
-		auth_state->status = AUTHENTICATION_STATUS_IDLE;
-	}
-	else
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_066: [If cbs_delete_token calls back with result different than CBS_OPERATION_RESULT_OK, authentication_reset() shall set the state status to AUTHENTICATION_STATUS_FAILURE]
-		auth_state->status = AUTHENTICATION_STATUS_FAILURE;
-		LogError("CBS reported status code %u, error: %s for delete token operation", status_code, status_description);
-	}
-}
-
-static int handSASTokenToCbs(AUTHENTICATION_STATE* auth_state, STRING_HANDLE cbs_audience, STRING_HANDLE sasToken, size_t current_time_in_sec_since_epoch)
-{
-	int result;
-
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_035: [The SAS token provided shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type and `devices_path` as audience]
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_028: [The SAS token shall be sent to CBS using cbs_put_token(), using `servicebus.windows.net:sastoken` as token type and `devices_path` as audience]
-	if (cbs_put_token(auth_state->cbs_connection->cbs_handle, SAS_TOKEN_TYPE, STRING_c_str(cbs_audience), STRING_c_str(sasToken), on_put_token_complete, auth_state) != RESULT_OK)
-	{
-		LogError("Failed applying new SAS token to CBS.");
-		result = __FAILURE__;
-	}
-	else
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_029: [If cbs_put_token() succeeds, authentication_authenticate() shall set the state status to AUTHENTICATION_STATUS_IN_PROGRESS]
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_037: [If cbs_put_token() succeeds, authentication_authenticate() shall set the state status to AUTHENTICATION_STATUS_IN_PROGRESS]
-		auth_state->status = AUTHENTICATION_STATUS_IN_PROGRESS;
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_030: [If cbs_put_token() succeeds, authentication_authenticate() shall set `current_sas_token_put_time` with the current time]
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_038: [If cbs_put_token() succeeds, authentication_authenticate() shall set `current_sas_token_put_time` with the current time]
-		auth_state->cbs_state.current_sas_token_put_time = current_time_in_sec_since_epoch;
-		result = RESULT_OK;
-	}
-
-	return result;
-}
-
-static int verifyAuthenticationTimeout(AUTHENTICATION_STATE* auth_state, bool* timeout_reached)
-{
-	int result;
-	size_t currentTimeInSeconds;
-
-	if (getSecondsSinceEpoch(&currentTimeInSeconds) != RESULT_OK)
-	{
-		LogError("Failed getting the current time to verify if the SAS token needs to be refreshed.");
-		*timeout_reached = true;
-		result = __FAILURE__;
-	}
-	else
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_046: [The authentication timeout shall be computed comparing the last time a SAS token was put (`current_sas_token_put_time`) to `cbs_request_timeout`]
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_052: [The authentication timeout shall be computed comparing the last time the SAS token was put (`current_sas_token_put_time`) to `cbs_request_timeout`]
-		*timeout_reached = ((currentTimeInSeconds - auth_state->cbs_state.current_sas_token_put_time) * 1000 >= auth_state->cbs_connection->cbs_request_timeout) ? true : false;
-		result = RESULT_OK;
-	}
-
-	return result;
-}
-
-static bool isSasTokenRefreshRequired(AUTHENTICATION_STATE* auth_state)
-{
-	bool result;
-	size_t currentTimeInSeconds;
-	if (auth_state->credential.type == DEVICE_SAS_TOKEN)
-	{
-		result = false;
-	}
-	else if (getSecondsSinceEpoch(&currentTimeInSeconds) != RESULT_OK)
-	{
-		LogError("Failed getting the current time to verify if the SAS token needs to be refreshed.");
-		result = true; // Fail safe.
-	}
-	else
-	{
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_049: [The SAS token expiration shall be computed comparing its create time to `sas_token_refresh_time`]
-		result = ((currentTimeInSeconds - auth_state->cbs_state.current_sas_token_create_time) >= (auth_state->cbs_connection->sas_token_refresh_time / 1000)) ? true : false;
-	}
-
-	return result;
-} 
-
-AUTHENTICATION_STATE_HANDLE authentication_create(const AUTHENTICATION_CONFIG* config)
-{
-	AUTHENTICATION_STATE* auth_state = NULL;
-	bool cleanup_required = true;
-
-	// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_001: [If parameter config, config->device_id, config->iot_hub_host_fqdn or config->cbs_connection are NULL, authentication_create() shall fail and return NULL.]
-	if (config == NULL)
-	{
-		LogError("Failed creating the authentication state (config is NULL)");
-	}
-	else if (config->device_id == NULL)
-	{
-		LogError("Failed creating the authentication state (device_id is NULL)");
-	}
-	else if (config->iot_hub_host_fqdn == NULL)
-	{
-		LogError("Failed creating the authentication state (iot_hub_host_fqdn is NULL)");
-	}
-	else if (config->cbs_connection == NULL)
-	{
-		LogError("Failed creating the authentication state (cbs_connection handle is NULL)");
-	}
-	// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_002: [authentication_create() shall allocate memory for a new authenticate state structure AUTHENTICATION_STATE.]
-	else if ((auth_state = (AUTHENTICATION_STATE*)malloc(sizeof(AUTHENTICATION_STATE))) == NULL)
-	{
-		// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_003: [If malloc() fails, authentication_create() shall fail and return NULL.]
-		LogError("Failed creating the authentication state (malloc failed)");
-	}
-	else
-	{
-		auth_state->device_id = NULL;
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_005: [authentication_create() shall save a reference to the `cbs_connection` into the AUTHENTICATION_STATE instance.]
-		auth_state->cbs_connection = config->cbs_connection;
-		auth_state->credential.type = CREDENTIAL_NOT_BUILD;
-		auth_state->credential.data.deviceKey = NULL;
-		auth_state->credential.data.deviceSasToken = NULL;
-		auth_state->credential.data.x509credential.x509certificate = NULL;
-		auth_state->credential.data.x509credential.x509privatekey = NULL;
-		auth_state->cbs_state.sasTokenKeyName = NULL;
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_004: [authentication_create() shall set the initial status of AUTHENTICATION_STATE as AUTHENTICATION_STATUS_IDLE.]
-		auth_state->status = AUTHENTICATION_STATUS_IDLE;
-
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_006: [authentication_create() shall save a copy of `device_config->deviceId` into the AUTHENTICATION_STATE instance.]
-		if ((auth_state->device_id = STRING_construct(config->device_id)) == NULL)
-		{
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_007: [If STRING_construct() fails to copy `device_config->deviceId`, authentication_create() shall fail and return NULL]
-			LogError("Failed creating the authentication state (could not copy the deviceId, STRING_construct failed)");
-		}
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_008: [authentication_create() shall save a copy of `iot_hub_host_fqdn` into the AUTHENTICATION_STATE instance.]
-		else if ((auth_state->iot_hub_host_fqdn = STRING_construct(config->iot_hub_host_fqdn)) == NULL)
-		{
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_009: [If STRING_clone() fails to copy `iot_hub_host_fqdn`, authentication_create() shall fail and return NULL]
-			LogError("Failed creating the authentication state (could not clone the devices_path)");
-		}
-		else
-		{
-			if (config->device_sas_token != NULL)
-			{
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_013: [If the credential type is DEVICE_SAS_TOKEN or DEVICE_KEY and parameter cbs_connection is NULL, authentication_create() shall fail and return NULL]
-				if (auth_state->cbs_connection == NULL)
-				{
-					LogError("authentication_create() failed (credential type is DEVICE_SAS_TOKEN but cbs_connection handle is NULL).");
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_014: [If the credential type is DEVICE_SAS_TOKEN or DEVICE_KEY, authentication_create() shall set sasTokenKeyName in the AUTHENTICATION_STATE as a non-NULL empty string.]
-				else if ((auth_state->cbs_state.sasTokenKeyName = STRING_new()) == NULL)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_015: [If STRING_new() fails and cannot create sasTokenKeyName, authentication_create() shall fail and return NULL]
-					LogError("Failed to allocate device_state->sasTokenKeyName.");
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_010: [If `device_config->deviceSasToken` is not NULL, authentication_create() shall save a copy into the AUTHENTICATION_STATE instance.]
-				else if ((auth_state->credential.data.deviceSasToken = STRING_construct(config->device_sas_token)) == NULL)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_011: [If STRING_construct() fails to copy `device_config->deviceSasToken`, authentication_create() shall fail and return NULL]
-					LogError("unable to STRING_construct for deviceSasToken");
-				}
-				else
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_012: [If `device_config->deviceSasToken` is not NULL, authentication_create() shall set the credential type in the AUTHENTICATION_STATE as DEVICE_SAS_TOKEN.]
-					auth_state->credential.type = DEVICE_SAS_TOKEN;
-					auth_state->cbs_state.current_sas_token_create_time = 0;
-					cleanup_required = false;
-				}
-				
-			}
-			else if (config->device_key != NULL)
-			{
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_013: [If the credential type is DEVICE_SAS_TOKEN or DEVICE_KEY and parameter cbs_connection is NULL, authentication_create() shall fail and return NULL]
-				if (auth_state->cbs_connection == NULL)
-				{
-					LogError("authentication_create() failed (credential type is DEVICE_KEY but cbs_connection handle is NULL).");
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_014: [If the credential type is DEVICE_SAS_TOKEN or DEVICE_KEY, authentication_create() shall set sasTokenKeyName in the AUTHENTICATION_STATE as a non-NULL empty string.]
-				else if ((auth_state->cbs_state.sasTokenKeyName = STRING_new()) == NULL)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_015: [If STRING_new() fails and cannot create sasTokenKeyName, authentication_create() shall fail and return NULL]
-					LogError("Failed to allocate device_state->sasTokenKeyName.");
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_016: [If `device_config->deviceKey` is not NULL, authentication_create() shall save a copy into the AUTHENTICATION_STATE instance.]
-				else if ((auth_state->credential.data.deviceKey = STRING_construct(config->device_key)) == NULL)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_017: [If STRING_construct() fails to copy `device_config->deviceKey`, authentication_create() shall fail and return NULL]
-					LogError("unable to STRING_construct for a deviceKey");
-				}
-				else
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_018: [If `device_config->deviceKey` is not NULL, authentication_create() shall set the credential type in the AUTHENTICATION_STATE as DEVICE_KEY.]
-					auth_state->credential.type = DEVICE_KEY;
-					auth_state->cbs_state.current_sas_token_create_time = 0;
-					cleanup_required = false;
-				}
-			}
-			else
-			{
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_020: [If the credential type is X509 and parameter cbs_connection is not NULL, authentication_create() shall fail and return NULL]
-				if (auth_state->cbs_connection == NULL)
-				{
-					LogError("authentication_create() failed (credential type is X509 but cbs_connection handle is not NULL).");
-				}
-				else
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_019: [If `device_config->deviceKey` and `device_config->deviceSasToken` are NULL, authentication_create() shall set the credential type in the AUTHENTICATION_STATE as X509]
-					auth_state->credential.type = X509;
-					auth_state->credential.data.x509credential.x509certificate = NULL;
-					auth_state->credential.data.x509credential.x509privatekey = NULL;
-					cleanup_required = false;
-				}
-			}
-		}
-	}
-
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_021: [If any failure occurs, authentication_create() shall free any memory it allocated previously, including the AUTHENTICATION_STATE]
-	if (cleanup_required)
-	{
-		if (auth_state->credential.data.deviceKey != NULL)
-			STRING_delete(auth_state->credential.data.deviceKey);
-		if (auth_state->iot_hub_host_fqdn != NULL)
-			STRING_delete(auth_state->iot_hub_host_fqdn);
-		if (auth_state->credential.data.deviceSasToken != NULL)
-			STRING_delete(auth_state->credential.data.deviceSasToken);
-		if (auth_state->cbs_state.sasTokenKeyName != NULL)
-			STRING_delete(auth_state->cbs_state.sasTokenKeyName);
-		if (auth_state != NULL)
-			free(auth_state);
-	}
-
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_022: [If no failure occurs, authentication_create() shall return a reference to the AUTHENTICATION_STATE handle]
-	return (AUTHENTICATION_STATE_HANDLE)auth_state;
-}
-
-static STRING_HANDLE concatenate_3_strings(const char* prefix, const char* infix, const char* suffix)
-{
-	STRING_HANDLE result = NULL;
-	char* concat;
-	size_t totalLength = strlen(prefix) + strlen(infix) + strlen(suffix) + 1; // One extra for \0.
-
-	if ((concat = (char*)malloc(totalLength)) != NULL)
-	{
-		(void)strcpy(concat, prefix);
-		(void)strcat(concat, infix);
-		(void)strcat(concat, suffix);
-		result = STRING_construct(concat);
-		free(concat);
-	}
-	else
-	{
-		result = NULL;
-	}
-
-	return result;
-}
-
-static STRING_HANDLE create_devices_path(STRING_HANDLE device_id, STRING_HANDLE iothub_host_fqdn)
-{
-	STRING_HANDLE devices_path;
-
-	if ((devices_path = concatenate_3_strings(STRING_c_str(device_id), "/devices/", STRING_c_str(iothub_host_fqdn))) == NULL)
-	{
-		LogError("Could not create the devices_path parameter (concatenate_3_strings failed)");
-	}
-
-	return devices_path;
-}
-
-int authentication_authenticate(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	int result;
-
-	// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_023: [If authentication_state is NULL, authentication_authenticate() shall fail and return an error code]
-	if (authentication_state_handle == NULL)
-	{
-		result = __FAILURE__;
-		LogError("Failed to authenticate device (the authentication_state_handle is NULL)");
-	}
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-
-		switch (auth_state->credential.type)
-		{
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_024: [If the credential type is DEVICE_KEY, authentication_authenticate() shall create a SAS token and put it to CBS]
-			case DEVICE_KEY:
-			{
-				size_t currentTimeInSeconds;
-
-				if ((result = getSecondsSinceEpoch(&currentTimeInSeconds)) != RESULT_OK)
-				{
-					LogError("Failed getting current time to compute the SAS token creation time (%d).", result);
-					result = __FAILURE__;
-				}
-				else
-				{
-					STRING_HANDLE devices_path = NULL;
-					STRING_HANDLE newSASToken = NULL;
-
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_026: [The SAS token expiration time shall be calculated adding `sas_token_lifetime` to the current number of seconds since epoch time UTC]
-					size_t new_expiry_time = currentTimeInSeconds + (auth_state->cbs_connection->sas_token_lifetime / 1000);
-
-					// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_076: [A STRING_HANDLE, referred to as `devices_path`, shall be created from the following parts: iot_hub_host_fqdn + "/devices/" + device_id]
-					if ((devices_path = create_devices_path(auth_state->iot_hub_host_fqdn, auth_state->device_id)) == NULL)
-					{
-						// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_077: [If `devices_path` failed to be created, authentication_authenticate() shall fail and return an error code]
-						LogError("Failed to authenticate device (could not create the devices_path parameter)");
-						result = __FAILURE__;
-					}
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_025: [The SAS token shall be created using SASToken_Create(), passing the deviceKey, device_path, sasTokenKeyName and expiration time as arguments]
-					else if ((newSASToken = SASToken_Create(auth_state->credential.data.deviceKey, devices_path, auth_state->cbs_state.sasTokenKeyName, new_expiry_time)) == NULL)
-					{
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_027: [If SASToken_Create() fails, authentication_authenticate() shall fail and return an error code]
-						LogError("Could not generate a new SAS token for the CBS.");
-						result = __FAILURE__;
-					}
-					else
-					{
-						auth_state->cbs_state.current_sas_token_create_time = currentTimeInSeconds;
-
-						if (handSASTokenToCbs(auth_state, devices_path, newSASToken, currentTimeInSeconds) != 0)
-						{
-							// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_032: [If cbs_put_token() fails, authentication_authenticate() shall fail and return an error code]
-							LogError("unable to send the new SASToken to CBS");
-							result = __FAILURE__;
-						}
-						else
-						{
-							// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_033: [If cbs_put_token() succeeds, authentication_authenticate() shall return success code 0]
-							result = RESULT_OK;
-						}
-
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_031: [authentication_authenticate() shall free the memory allocated for the new SAS token using STRING_delete()]
-						STRING_delete(newSASToken);
-					}
-
-					if (devices_path != NULL)
-						STRING_delete(devices_path);
-				}
-
-				break;
-			}
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_034: [If the credential type is DEVICE_SAS_TOKEN, authentication_authenticate() shall put the SAS token provided to CBS]
-			case DEVICE_SAS_TOKEN:
-			{
-				size_t currentTimeInSeconds;
-
-				if ((result = getSecondsSinceEpoch(&currentTimeInSeconds)) != RESULT_OK)
-				{
-					LogError("Failed getting current time to compute the SAS token creation time (%d).", result);
-					result = __FAILURE__;
-				}
-				else
-				{
-					STRING_HANDLE devices_path = NULL;
-
-					// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_078: [A STRING_HANDLE, referred to as `devices_path`, shall be created from the following parts: iot_hub_host_fqdn + "/devices/" + device_id]
-					if ((devices_path = create_devices_path(auth_state->iot_hub_host_fqdn, auth_state->device_id)) == NULL)
-					{
-						// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_079: [If `devices_path` failed to be created, authentication_authenticate() shall fail and return an error code]
-						LogError("Failed to authenticate device (could not create the devices_path parameter)");
-						result = __FAILURE__;
-					}
-					else if (handSASTokenToCbs(auth_state, devices_path, auth_state->credential.data.deviceSasToken, currentTimeInSeconds) != 0)
-					{
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_036: [If cbs_put_token() fails, authentication_authenticate() shall fail and return an error code]
-						LogError("unable to send the new SASToken to CBS");
-						result = __FAILURE__;
-					}
-					else
-					{
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_040: [If cbs_put_token() succeeds, authentication_authenticate() shall return success code 0]
-						result = RESULT_OK;
-					}
-
-					if (devices_path != NULL)
-						STRING_delete(devices_path);
-				}
-				break;
-			}
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_043: [If the credential type is X509, authentication_authenticate() shall not perform any authentication and return a success code]
-			case X509:
-				if (auth_state->status != AUTHENTICATION_STATUS_IDLE)
-				{
-					LogError("Failed to authenticate device [X509] (authentication status %d is invalid)", auth_state->status);
-					result = __FAILURE__;
-				}
-				else
-				{
-					auth_state->status = AUTHENTICATION_STATUS_OK;
-					result = RESULT_OK;
-				}
-				break;
-			default:
-				result = __FAILURE__;
-				LogError("Failed to authenticate the device (unexpected credential type %d)", auth_state->credential.type);
-				break;
-		}
-	}
-
-	return result;
-}
-
-AUTHENTICATION_STATUS authentication_get_status(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	AUTHENTICATION_STATUS auth_status = AUTHENTICATION_STATUS_IDLE;
-	
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_044: [If authentication_state is NULL, authentication_get_status() shall fail and return AUTHENTICATION_STATUS_NONE]
-	if (authentication_state_handle == NULL)
-	{
-		LogError("Failed retrieving the authentication status (the authentication_state_handle is NULL)");
-		auth_status = AUTHENTICATION_STATUS_NONE;
-	}
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-
-		switch (auth_state->credential.type)
-		{
-			case DEVICE_KEY:
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_045: [If the credential type is DEVICE_KEY and current status is AUTHENTICATION_STATUS_IN_PROGRESS, authentication_get_status() shall check for authentication timeout]
-				if (auth_state->status == AUTHENTICATION_STATUS_IN_PROGRESS)
-				{
-					bool timeout_reached;
-
-					if (verifyAuthenticationTimeout(auth_state, &timeout_reached) != RESULT_OK)
-					{
-						LogError("Failed retrieving the status of the authentication (failed verifying if the authentication is expired)");
-						auth_state->status = AUTHENTICATION_STATUS_FAILURE;
-					}
-					else if (timeout_reached)
-					{
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_047: [If authentication has timed out, authentication_get_status() shall set the status of the state to AUTHENTICATION_STATUS_TIMEOUT]
-						auth_state->status = AUTHENTICATION_STATUS_TIMEOUT;
-					}
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_048: [If the credential type is DEVICE_KEY and current status is AUTHENTICATION_STATUS_OK, authentication_get_status() shall check if SAS token must be refreshed]
-				else if (auth_state->status == AUTHENTICATION_STATUS_OK && isSasTokenRefreshRequired(auth_state))
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_050: [If the SAS token must be refreshed, authentication_get_status() shall set the status of the state to AUTHENTICATION_STATUS_REFRESH_REQUIRED]
-					auth_state->status = AUTHENTICATION_STATUS_REFRESH_REQUIRED;
-				}
-				break;
-			case DEVICE_SAS_TOKEN:
-				if (auth_state->status == AUTHENTICATION_STATUS_IN_PROGRESS)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_051: [If the credential type is DEVICE_SAS_TOKEN and current status is AUTHENTICATION_STATUS_IN_PROGRESS, authentication_get_status() shall check for authentication timeout]
-					bool timeout_reached;
-					if (verifyAuthenticationTimeout(auth_state, &timeout_reached) != RESULT_OK)
-					{
-						LogError("Failed retrieving the status of the authentication (failed verifying if the authentication is expired)");
-						auth_state->status = AUTHENTICATION_STATUS_FAILURE;
-					}
-					else if (timeout_reached)
-					{
-						// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_053: [If authentication has timed out, authentication_get_status() shall set the status of the state to AUTHENTICATION_STATUS_TIMEOUT]
-						auth_state->status = AUTHENTICATION_STATUS_TIMEOUT;
-					}
-				}
-				break;
-			case X509:
-				break;
-			default:
-				LogError("Failed to retrieve the authentication status (unexpected credential type %d)", auth_state->credential.type);
-				break;
-		}
-
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_054: [After checks and updates, authentication_get_status() shall return the status saved on the AUTHENTICATION_STATE]
-		auth_status = auth_state->status;
-	}
-
-	return auth_status;
-}
-
-AMQP_TRANSPORT_CREDENTIAL* authentication_get_credential(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	AMQP_TRANSPORT_CREDENTIAL* credential;
-
-	// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_056: [If authentication_state is NULL, authentication_get_credential() shall fail and return NULL]
-	if (authentication_state_handle == NULL)
-	{
-		LogError("authentication_get_credential() failed (authentication_state_handle is NULL)");
-		credential = NULL;
-	}
-	// Codes_SRS_IOTHUBTRANSPORTAMQP_AUTH_09_055: [If authentication_state is not NULL, authentication_get_credential() shall return a reference to credentials saved in the AUTHENTICATION_STATE]
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-		credential = &auth_state->credential;
-	}
-
-	return credential;
-}
-
-int authentication_refresh(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	int result;
-
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_057: [If authentication_state is NULL, authentication_refresh() shall fail and return error code]
-	if (authentication_state_handle == NULL)
-	{
-		LogError("authentication_refresh() failed (authentication_state_handle is NULL)");
-		result = __FAILURE__;
-	}
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_058: [If the credential type is X509, authentication_refresh() shall return with success code 0]
-		if (auth_state->credential.type == X509)
-		{
-			result = RESULT_OK;
-		}
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_059: [authentication_refresh() shall invoke authentication_authenticate(), passing the authentication_state handle]
-		else if ((result = authentication_authenticate(authentication_state_handle)) != RESULT_OK)
-		{
-			// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_060: [if authentication_authenticate() fails, authentication_refresh() shall fail and return an error code]
-			LogError("Failed refreshing authentication (%d).", result);
-			result = __FAILURE__;
-		}
-	}
-
-	return result;
-}
-
-int authentication_reset(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	int result;
-
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_061: [If authentication_state is NULL, authentication_reset() shall fail and return an error code]
-	if (authentication_state_handle == NULL)
-	{
-		LogError("Failed to reset the authentication state (authentication_state is NULL)");
-		result = __FAILURE__;
-	}
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-
-		switch (auth_state->credential.type)
-		{
-			case DEVICE_KEY:
-			case DEVICE_SAS_TOKEN:
-			{
-				STRING_HANDLE devices_path = NULL;
-
-				if (auth_state->status == AUTHENTICATION_STATUS_FAILURE || auth_state->status == AUTHENTICATION_STATUS_REFRESH_REQUIRED || auth_state->status == AUTHENTICATION_STATUS_TIMEOUT)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_063: [If the authentication_state status is AUTHENTICATION_STATUS_FAILURE or AUTHENTICATION_STATUS_REFRESH_REQUIRED, authentication_reset() shall set the status to AUTHENTICATION_STATUS_IDLE]
-					auth_state->status = AUTHENTICATION_STATUS_IDLE;
-					result = RESULT_OK;
-				}
-				else if (auth_state->status != AUTHENTICATION_STATUS_OK && auth_state->status != AUTHENTICATION_STATUS_IN_PROGRESS)
-				{
-					LogError("Failed to reset the authentication state (authentication status is invalid: %i)", auth_state->status);
-					result = __FAILURE__;
-				}
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_064: [If the authentication_state status is AUTHENTICATION_STATUS_OK or AUTHENTICATION_STATUS_IN_PROGRESS, authentication_reset() delete the previous token using cbs_delete_token()]
-				else if ((devices_path = create_devices_path(auth_state->iot_hub_host_fqdn, auth_state->device_id)) == NULL)
-				{
-					LogError("Failed to reset the authenticate state (could not create the devices_path parameter for cbs_delete_token)");
-					result = __FAILURE__;
-				}
-				else if (cbs_delete_token(auth_state->cbs_connection->cbs_handle, STRING_c_str(devices_path), SAS_TOKEN_TYPE, on_delete_token_complete, auth_state) != RESULT_OK)
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_065: [If cbs_delete_token fails, authentication_reset() shall fail and return an error code]
-					LogError("Failed to reset the authentication state (failed deleting the current SAS token from CBS)");
-					result = __FAILURE__;
-				}
-				else
-				{
-					// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_067: [If no error occurs, authentication_reset() shall return 0]
-					auth_state->status = AUTHENTICATION_STATUS_IDLE;
-					auth_state->cbs_state.current_sas_token_create_time = 0;
-					result = RESULT_OK;
-				}
-				break;
-			}
-			case X509:
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_062: [If the credential type is X509, authentication_reset() shall set the status to AUTHENTICATION_STATUS_IDLE and return with success code 0]
-				auth_state->status = AUTHENTICATION_STATUS_IDLE;
-				result = RESULT_OK;
-				break;
-			default:
-				result = __FAILURE__;
-				LogError("Failed to reset the authentication state (unexpected credential type %d)", auth_state->credential.type);
-				break;
-		}
-	}
-
-	return result;
-}
-
-void authentication_destroy(AUTHENTICATION_STATE_HANDLE authentication_state_handle)
-{
-	// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_068: [If authentication_state is NULL, authentication_destroy() shall fail and return]
-	if (authentication_state_handle == NULL)
-	{
-		LogError("Failed to destroy the authentication state (authentication_state is NULL)");
-	}
-	else
-	{
-		AUTHENTICATION_STATE* auth_state = (AUTHENTICATION_STATE*)authentication_state_handle;
-
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_069: [authentication_destroy() shall destroy the AUTHENTICATION_STATE->device_id using STRING_delete()]
-		STRING_delete(auth_state->device_id);
-		// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_070: [authentication_destroy() shall destroy the AUTHENTICATION_STATE->iot_hub_host_fqdn using STRING_delete()]
-		STRING_delete(auth_state->iot_hub_host_fqdn);
-
-		switch (auth_state->credential.type)
-		{
-			case (CREDENTIAL_NOT_BUILD):
-			case(X509):
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_075: [authentication_destroy() shall free the AUTHENTICATION_STATE]
-				free(auth_state);
-				break;
-			case(DEVICE_KEY):
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_071: [If the credential type is DEVICE_KEY, authentication_destroy() shall destroy `deviceKey` in AUTHENTICATION_STATE using STRING_delete()]
-				STRING_delete(auth_state->credential.data.deviceKey);
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_072: [If the credential type is DEVICE_KEY, authentication_destroy() shall destroy `sasTokenKeyName` in AUTHENTICATION_STATE using STRING_delete()]
-				STRING_delete(auth_state->cbs_state.sasTokenKeyName);
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_075: [authentication_destroy() shall free the AUTHENTICATION_STATE]
-				free(auth_state);
-				break;
-			case(DEVICE_SAS_TOKEN):
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_073: [If the credential type is DEVICE_SAS_TOKEN, authentication_destroy() shall destroy `deviceSasToken` in AUTHENTICATION_STATE using STRING_delete()]
-				STRING_delete(auth_state->credential.data.deviceSasToken);
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_074: [If the credential type is DEVICE_SAS_TOKEN, authentication_destroy() shall destroy `sasTokenKeyName` in AUTHENTICATION_STATE using STRING_delete()]
-				STRING_delete(auth_state->cbs_state.sasTokenKeyName);
-				// Codes_IOTHUBTRANSPORTAMQP_AUTH_09_075: [authentication_destroy() shall free the AUTHENTICATION_STATE]
-				free(auth_state);
-				break;
-			default:
-				LogError("Failed to destroy the authentication state (unexpected credential type %d)", auth_state->credential.type);
-				break;
-		}
-	}
-}
--- a/iothubtransportamqp_auth.h	Fri Feb 24 14:00:00 2017 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved. 
-// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
-
-#ifndef IOTHUBTRANSPORTAMQP_AUTH_H 
-#define IOTHUBTRANSPORTAMQP_AUTH_H 
-
-#include <stdlib.h>
-#include <stdint.h>
-#include "azure_c_shared_utility/strings.h" 
-#include "azure_c_shared_utility/sastoken.h"
-#include "azure_c_shared_utility/xio.h"
-#include "azure_uamqp_c/cbs.h"
-#include "azure_uamqp_c/sasl_mechanism.h"
-#include "iothub_transport_ll.h" 
-#include "azure_c_shared_utility/umock_c_prod.h"
-
-#ifdef __cplusplus 
-extern "C" 
-{ 
-#endif 
-
-typedef struct AMQP_TRANSPORT_CBS_CONNECTION_TAG
-{
-	// How long a SAS token created by the transport is valid, in milliseconds.
-	size_t sas_token_lifetime;
-	// Maximum period of time for the transport to wait before refreshing the SAS token it created previously, in milliseconds.
-	size_t sas_token_refresh_time;
-	// Maximum time the transport waits for  uAMQP cbs_put_token() to complete before marking it a failure, in milliseconds.
-	size_t cbs_request_timeout;
-
-	// AMQP SASL I/O transport created on top of the TLS I/O layer.
-	XIO_HANDLE sasl_io;
-	// AMQP SASL I/O mechanism to be used.
-	SASL_MECHANISM_HANDLE sasl_mechanism;
-	// Connection instance with the Azure IoT CBS.
-	CBS_HANDLE cbs_handle;
-} AMQP_TRANSPORT_CBS_CONNECTION;
-
-typedef struct AUTHENTICATION_CONFIG_TAG
-{
-	const char* device_id;
-	const char* device_key;
-	const char* device_sas_token;
-	const char* iot_hub_host_fqdn;
-	const AMQP_TRANSPORT_CBS_CONNECTION* cbs_connection;
-
-} AUTHENTICATION_CONFIG;
-
-typedef enum AUTHENTICATION_STATUS_TAG 
-{
-	AUTHENTICATION_STATUS_IDLE, 
-	AUTHENTICATION_STATUS_IN_PROGRESS, 
-	AUTHENTICATION_STATUS_TIMEOUT,
-	AUTHENTICATION_STATUS_REFRESH_REQUIRED,
-	AUTHENTICATION_STATUS_FAILURE, 
-	AUTHENTICATION_STATUS_OK,
-	AUTHENTICATION_STATUS_NONE
-} AUTHENTICATION_STATUS; 
- 
-typedef enum AMQP_TRANSPORT_CREDENTIAL_TYPE_TAG 
-{ 
-	CREDENTIAL_NOT_BUILD, 
-	X509, 
-	DEVICE_KEY, 
-	DEVICE_SAS_TOKEN, 
-} AMQP_TRANSPORT_CREDENTIAL_TYPE; 
- 
-typedef struct X509_CREDENTIAL_TAG 
-{ 
-	const char* x509certificate; 
-	const char* x509privatekey; 
-} X509_CREDENTIAL; 
- 
-typedef union AMQP_TRANSPORT_CREDENTIAL_DATA_TAG 
-{ 
-	// Key associated to the device to be used. 
-	STRING_HANDLE deviceKey; 
- 
-	// SAS associated to the device to be used. 
-	STRING_HANDLE deviceSasToken; 
- 
-	// X509  
-	X509_CREDENTIAL x509credential; 
-} AMQP_TRANSPORT_CREDENTIAL_DATA; 
- 
-typedef struct AMQP_TRANSPORT_CREDENTIAL_TAG 
-{ 
-	AMQP_TRANSPORT_CREDENTIAL_TYPE type; 
-	AMQP_TRANSPORT_CREDENTIAL_DATA data; 
-} AMQP_TRANSPORT_CREDENTIAL; 
-
-struct AUTHENTICATION_STATE;
-typedef struct AUTHENTICATION_STATE* AUTHENTICATION_STATE_HANDLE;
- 
-/** @brief Creates a state holder for all authentication-related information and connections. 
-* 
-*   @returns an instance of the AUTHENTICATION_STATE_HANDLE if succeeds, NULL if any failure occurs. 
-*/ 
-MOCKABLE_FUNCTION(, AUTHENTICATION_STATE_HANDLE, authentication_create, const AUTHENTICATION_CONFIG*, config);
- 
-/** @brief Establishes the first authentication for the device in the transport it is registered to. 
-* 
-* @details If SAS token or key are used, creates a cbs instance for the transport if it does not have one,  
-*            and puts a SAS token in (creates one if key is used, or applies the SAS token if provided by user). 
-*            If certificates are used, they are set on the tls_io instance of the transport. 
-* 
-*   @returns 0 if it succeeds, non-zero if it fails. 
-*/ 
-MOCKABLE_FUNCTION(, int, authentication_authenticate, AUTHENTICATION_STATE_HANDLE, authentication_state);
- 
-/** @brief Indicates if the device is authenticated successfuly, if authentication is in progress or completed with failure. 
-* 
-*   @returns A flag indicating the current authentication status of the device. 
-*/ 
-MOCKABLE_FUNCTION(, AUTHENTICATION_STATUS, authentication_get_status, AUTHENTICATION_STATE_HANDLE, authentication_state);
-
-/** @brief Gets the credential stored by the handle for authenticating the device.
-*
-*   @returns A AMQP_TRANSPORT_CREDENTIAL with the credentials type and data.
-*/
-MOCKABLE_FUNCTION(, AMQP_TRANSPORT_CREDENTIAL*, authentication_get_credential, AUTHENTICATION_STATE_HANDLE, authentication_state);
-
-/** @brief Refreshes the authentication if needed. 
-* 
-* @details If SAS key is used, a new token is generated and put to cbs if the previous generated token is expired. 
-* 
-*   @returns 0 if it succeeds, non-zero if it fails. 
-*/ 
-MOCKABLE_FUNCTION(, int, authentication_refresh, AUTHENTICATION_STATE_HANDLE, authentication_state);
-
-/** @brief Resets the state of the authentication.
-*
-* @details Causes the status of the authentication state to be reset to IDLE (similar to when the state is created).
-*
-*   @returns 0 if it succeeds, non-zero if it fails.
-*/
-MOCKABLE_FUNCTION(, int, authentication_reset, AUTHENTICATION_STATE_HANDLE, authentication_state);
-
-/** @brief De-authenticates the device and destroy the state instance. 
-* 
-* @details Closes the subscription to cbs if in use, destroys the cbs instance if it is the last device registered. 
-*            No action is taken if certificate-based authentication if used. 
-* 
-*   @returns Nothing. 
-*/ 
-MOCKABLE_FUNCTION(, void, authentication_destroy, AUTHENTICATION_STATE_HANDLE, authentication_state);
- 
-#ifdef __cplusplus 
-} 
-#endif 
- 
-#endif /*IOTHUBTRANSPORTAMQP_AUTH_H*/ 
\ No newline at end of file