void addToAckWaitList(uint8_t indexAckWaitList, const char *pThingName, ShadowActions_t action,
					  const char *pExtractedClientToken, fpActionCallback_t callback, void *pCallbackContext,
					  uint32_t timeout_seconds) {
	AckWaitList[indexAckWaitList].callback = callback;
	strncpy(AckWaitList[indexAckWaitList].clientTokenID, pExtractedClientToken, MAX_SIZE_CLIENT_ID_WITH_SEQUENCE);
	strncpy(AckWaitList[indexAckWaitList].thingName, pThingName, MAX_SIZE_OF_THING_NAME);
	AckWaitList[indexAckWaitList].pCallbackContext = pCallbackContext;
	AckWaitList[indexAckWaitList].action = action;
	init_timer(&(AckWaitList[indexAckWaitList].timer));
	countdown_sec(&(AckWaitList[indexAckWaitList].timer), timeout_seconds);
	AckWaitList[indexAckWaitList].isFree = false;
}
IoT_Error_t subscribeToShadowActionAcks(const char *pThingName, ShadowActions_t action, bool isSticky) {
	IoT_Error_t ret_val = SUCCESS;

	bool clearBothEntriesFromList = true;
	int16_t indexAcceptedSubList = 0;
	int16_t indexRejectedSubList = 0;
	Timer subSettlingtimer;
	indexAcceptedSubList = getNextFreeIndexOfSubscriptionList();
	indexRejectedSubList = getNextFreeIndexOfSubscriptionList();

	if(indexAcceptedSubList >= 0 && indexRejectedSubList >= 0) {
		topicNameFromThingAndAction(SubscriptionList[indexAcceptedSubList].Topic, pThingName, action, SHADOW_ACCEPTED);
		ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic,
										 (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic), QOS0,
										 AckStatusCallback, NULL);
		if(ret_val == SUCCESS) {
			SubscriptionList[indexAcceptedSubList].count = 1;
			SubscriptionList[indexAcceptedSubList].isSticky = isSticky;
			topicNameFromThingAndAction(SubscriptionList[indexRejectedSubList].Topic, pThingName, action,
										SHADOW_REJECTED);
			ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexRejectedSubList].Topic,
											 (uint16_t) strlen(SubscriptionList[indexRejectedSubList].Topic), QOS0,
											 AckStatusCallback, NULL);
			if(ret_val == SUCCESS) {
				SubscriptionList[indexRejectedSubList].count = 1;
				SubscriptionList[indexRejectedSubList].isSticky = isSticky;
				clearBothEntriesFromList = false;

				// wait for SUBSCRIBE_SETTLING_TIME seconds to let the subscription take effect
				init_timer(&subSettlingtimer);
				countdown_sec(&subSettlingtimer, SUBSCRIBE_SETTLING_TIME);
				while(!has_timer_expired(&subSettlingtimer));

			}
		}
	}

	if(clearBothEntriesFromList) {
		if(indexAcceptedSubList >= 0) {
			SubscriptionList[indexAcceptedSubList].isFree = true;
		} else if(indexRejectedSubList >= 0) {
			SubscriptionList[indexRejectedSubList].isFree = true;
		}
		if(SubscriptionList[indexAcceptedSubList].count == 1) {
			aws_iot_mqtt_unsubscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic,
									 (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic));
		}
	}

	return ret_val;
}
/**
 * @brief MQTT Connection Function
 *
 * Called to establish an MQTT connection with the AWS IoT Service
 * This is the internal function which is called by the connect API to perform the operation.
 * Not meant to be called directly as it doesn't do validations or client state changes
 *
 * @param pClient Reference to the IoT Client
 * @param pConnectParams Pointer to MQTT connection parameters
 *
 * @return An IoT Error Type defining successful/failed connection
 */
static IoT_Error_t _aws_iot_mqtt_internal_connect(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pConnectParams) {
	Timer connect_timer;
	IoT_Error_t connack_rc = FAILURE;
	char sessionPresent = 0;
	size_t len = 0;
	IoT_Error_t rc = FAILURE;

	FUNC_ENTRY;

	if(NULL != pConnectParams) {
		/* override default options if new options were supplied */
		rc = aws_iot_mqtt_set_connect_params(pClient, pConnectParams);
		if(SUCCESS != rc) {
			FUNC_EXIT_RC(MQTT_CONNECTION_ERROR);
		}
	}

	rc = pClient->networkStack.connect(&(pClient->networkStack), NULL);
	if(SUCCESS != rc) {
		/* TLS Connect failed, return error */
		FUNC_EXIT_RC(rc);
	}

	init_timer(&connect_timer);
	countdown_ms(&connect_timer, pClient->clientData.commandTimeoutMs);

	pClient->clientData.keepAliveInterval = pClient->clientData.options.keepAliveIntervalInSec;
	rc = _aws_iot_mqtt_serialize_connect(pClient->clientData.writeBuf, pClient->clientData.writeBufSize,
										 &(pClient->clientData.options), &len);
	if(SUCCESS != rc || 0 >= len) {
		FUNC_EXIT_RC(rc);
	}

	/* send the connect packet */
	rc = aws_iot_mqtt_internal_send_packet(pClient, len, &connect_timer);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	/* this will be a blocking call, wait for the CONNACK */
	rc = aws_iot_mqtt_internal_wait_for_read(pClient, CONNACK, &connect_timer);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	/* Received CONNACK, check the return code */
	rc = _aws_iot_mqtt_deserialize_connack((unsigned char *) &sessionPresent, &connack_rc, pClient->clientData.readBuf,
										   pClient->clientData.readBufSize);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	if(MQTT_CONNACK_CONNECTION_ACCEPTED != connack_rc) {
		FUNC_EXIT_RC(connack_rc);
	}

	pClient->clientStatus.isPingOutstanding = false;
	countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval);

	FUNC_EXIT_RC(SUCCESS);
}
IoT_Error_t aws_iot_mqtt_internal_cycle_read(AWS_IoT_Client *pClient, Timer *pTimer, uint8_t *pPacketType) {
	IoT_Error_t rc;

#ifdef _ENABLE_THREAD_SUPPORT_
	IoT_Error_t threadRc;
#endif

	if(NULL == pClient || NULL == pTimer) {
		return NULL_VALUE_ERROR;
	}

#ifdef _ENABLE_THREAD_SUPPORT_
	threadRc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.tls_read_mutex));
	if(SUCCESS != threadRc) {
		FUNC_EXIT_RC(threadRc);
	}
#endif

	/* read the socket, see what work is due */
	rc = _aws_iot_mqtt_internal_read_packet(pClient, pTimer, pPacketType);

#ifdef _ENABLE_THREAD_SUPPORT_
	threadRc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.tls_read_mutex));
	if(SUCCESS != threadRc && (MQTT_NOTHING_TO_READ == rc || SUCCESS == rc)) {
		return threadRc;
	}
#endif

	if(MQTT_NOTHING_TO_READ == rc) {
		/* Nothing to read, not a cycle failure */
		return SUCCESS;
	} else if(SUCCESS != rc) {
		return rc;
	}

	switch(*pPacketType) {
		case CONNACK:
		case PUBACK:
		case SUBACK:
		case UNSUBACK:
			/* SDK is blocking, these responses will be forwarded to calling function to process */
			break;
		case PUBLISH: {
			rc = _aws_iot_mqtt_internal_handle_publish(pClient, pTimer);
			break;
		}
		case PUBREC:
		case PUBCOMP:
			/* QoS2 not supported at this time */
			break;
		case PINGRESP: {
			pClient->clientStatus.isPingOutstanding = 0;
			countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval);
			break;
		}
		default: {
			/* Either unknown packet type or Failure occurred
             * Should not happen */
			rc = MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR;
			break;
		}
	}

	return rc;
}