Preliminary main mbed library for nexpaq development
Diff: features/FEATURE_CLIENT/mbed-client/source/m2minterfaceimpl.cpp
- Revision:
- 0:6c56fb4bc5f0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/FEATURE_CLIENT/mbed-client/source/m2minterfaceimpl.cpp Fri Nov 04 20:27:58 2016 +0000 @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <assert.h> +#include "include/m2minterfaceimpl.h" +#include "include/eventdata.h" +#include "mbed-client/m2minterfaceobserver.h" +#include "mbed-client/m2mconnectionhandler.h" +#include "mbed-client/m2mconnectionsecurity.h" +#include "include/m2mnsdlinterface.h" +#include "include/nsdlaccesshelper.h" +#include "mbed-client/m2msecurity.h" +#include "mbed-client/m2mconstants.h" +#include "mbed-client/m2mtimer.h" +#include "mbed-trace/mbed_trace.h" + +#define TRACE_GROUP "mClt" + +M2MInterfaceImpl::M2MInterfaceImpl(M2MInterfaceObserver& observer, + const String &ep_name, + const String &ep_type, + const int32_t l_time, + const uint16_t listen_port, + const String &dmn, + M2MInterface::BindingMode mode, + M2MInterface::NetworkStack stack, + const String &con_addr) +: _observer(observer), + _nsdl_interface(new M2MNsdlInterface(*this)), + _current_state(0), + _max_states( STATE_MAX_STATES ), + _event_generated(false), + _event_data(NULL), + _endpoint_type(ep_type), + _domain( dmn), + _life_time(l_time), + _binding_mode(mode), + _context_address(con_addr), + _listen_port(listen_port), + _server_port(0), + _register_server(NULL), + _event_ignored(false), + _register_ongoing(false), + _update_register_ongoing(false), + _queue_sleep_timer(new M2MTimer(*this)), + _retry_timer(new M2MTimer(*this)), + _bootstrap_timer(NULL), + _callback_handler(NULL), + _security(NULL), + _retry_count(0), + _reconnecting(false), + _retry_timer_expired(false) +{ + M2MConnectionSecurity::SecurityMode sec_mode = M2MConnectionSecurity::DTLS; + //Hack for now + if( _binding_mode == M2MInterface::TCP ){ + _binding_mode = M2MInterface::UDP; + sec_mode = M2MConnectionSecurity::TLS; + }else if( _binding_mode == M2MInterface::TCP_QUEUE ){ + _binding_mode = M2MInterface::UDP_QUEUE; + sec_mode = M2MConnectionSecurity::TLS; + } + tr_debug("M2MInterfaceImpl::M2MInterfaceImpl() -IN"); + _nsdl_interface->create_endpoint(ep_name, + _endpoint_type, + _life_time, + _domain, + (uint8_t)_binding_mode, + _context_address); + + //Doesn't own, ownership is passed to ConnectionHandler class + _security_connection = new M2MConnectionSecurity(sec_mode); + //Here we must use TCP still + _connection_handler = new M2MConnectionHandler(*this, _security_connection, mode, stack); + __connection_handler = _connection_handler; + _connection_handler->bind_connection(_listen_port); +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + _bootstrap_timer = new M2MTimer(*this); +#endif + tr_debug("M2MInterfaceImpl::M2MInterfaceImpl() -OUT"); +} + + +M2MInterfaceImpl::~M2MInterfaceImpl() +{ + tr_debug("M2MInterfaceImpl::~M2MInterfaceImpl() - IN"); + delete _queue_sleep_timer; + delete _nsdl_interface; + _connection_handler->stop_listening(); + __connection_handler = NULL; + delete _connection_handler; + delete _retry_timer; + delete _bootstrap_timer; + _security_connection = NULL; + tr_debug("M2MInterfaceImpl::~M2MInterfaceImpl() - OUT"); +} + +void M2MInterfaceImpl::bootstrap(M2MSecurity *security) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::bootstrap(M2MSecurity *security) - IN"); + // Transition to a new state based upon + // the current state of the state machine + M2MSecurityData* data = new M2MSecurityData(); + data->_object = security; + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (STATE_BOOTSTRAP) // state_idle + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state__bootstrap_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap_resource_created + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrapped + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_resource_created + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_registered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_update_registration + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregister + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregistered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_sending_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_sent + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_received + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_processing_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_processed + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_waiting + END_TRANSITION_MAP(data) + if(_event_ignored) { + _event_ignored = false; + _observer.error(M2MInterface::NotAllowed); + } + tr_debug("M2MInterfaceImpl::bootstrap(M2MSecurity *security) - OUT"); +#else + _observer.error(M2MInterface::NotAllowed); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::cancel_bootstrap() +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +//TODO: Do we need this ? +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::register_object(M2MSecurity *security, const M2MObjectList &object_list) +{ + tr_debug("M2MInterfaceImpl::register_object - IN"); + // Transition to a new state based upon + // the current state of the state machine + //TODO: manage register object in a list. + if(!_register_ongoing) { + _register_ongoing = true; + _register_server = security; + M2MRegisterData *data = new M2MRegisterData(); + data->_object = security; + data->_object_list = object_list; + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (STATE_REGISTER) // state_idle + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state__bootstrap_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap_resource_created + TRANSITION_MAP_ENTRY (STATE_REGISTER) // state_bootstrapped + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_resource_created + TRANSITION_MAP_ENTRY (STATE_REGISTER) // state_registered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_update_registration + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregister + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregistered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_sending_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_sent + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_received + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_processing_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_processed + TRANSITION_MAP_ENTRY (STATE_REGISTER) // state_waiting + END_TRANSITION_MAP(data) + if(_event_ignored) { + _event_ignored = false; + _observer.error(M2MInterface::NotAllowed); + } + } else { + tr_debug("M2MInterfaceImpl::register_object - NOT ALLOWED"); + _observer.error(M2MInterface::NotAllowed); + } + tr_debug("M2MInterfaceImpl::register_object - OUT"); +} + +void M2MInterfaceImpl::update_registration(M2MSecurity *security_object, const uint32_t lifetime) +{ + tr_debug("M2MInterfaceImpl::update_registration - IN"); + // Transition to a new state based upon + // the current state of the state machine + if(lifetime != 0 && (lifetime < MINIMUM_REGISTRATION_TIME)) { + _observer.error(M2MInterface::InvalidParameters); + } else if(!_update_register_ongoing){ + tr_debug("M2MInterfaceImpl::update_registration - already ongoing"); + _update_register_ongoing = true; + M2MUpdateRegisterData *data = new M2MUpdateRegisterData(); + data->_object = security_object; + data->_lifetime = lifetime; + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_idle + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state__bootstrap_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap_resource_created + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrapped + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_resource_created + TRANSITION_MAP_ENTRY (STATE_UPDATE_REGISTRATION) // state_registered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_update_registration + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregister + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregistered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_sending_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_sent + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_received + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_processing_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_processed + TRANSITION_MAP_ENTRY (STATE_UPDATE_REGISTRATION) // state_waiting + END_TRANSITION_MAP(data) + if(_event_ignored) { + _event_ignored = false; + if (!_reconnecting) + _observer.error(M2MInterface::NotAllowed); + } + } else if(!_reconnecting) { + tr_debug("M2MInterfaceImpl::update_registration - NOT ALLOWED"); + _observer.error(M2MInterface::NotAllowed); + } else { + tr_debug("M2MInterfaceImpl::update_registration - reconnecting"); + } + tr_debug("M2MInterfaceImpl::update_registration - OUT"); +} + +void M2MInterfaceImpl::unregister_object(M2MSecurity* /*security*/) +{ + tr_debug("M2MInterfaceImpl::unregister_object - IN"); + // Transition to a new state based upon + // the current state of the state machine + BEGIN_TRANSITION_MAP // - Current State - + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_idle + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state__bootstrap_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrap_resource_created + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_bootstrapped + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_address_resolved + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_register_resource_created + TRANSITION_MAP_ENTRY (STATE_UNREGISTER) // state_registered + TRANSITION_MAP_ENTRY (STATE_UNREGISTER) // state_update_registration + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregister + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_unregistered + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_sending_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_sent + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_received + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_processing_coap_data + TRANSITION_MAP_ENTRY (EVENT_IGNORED) // state_coap_data_processed + TRANSITION_MAP_ENTRY (STATE_UNREGISTER) // state_waiting + END_TRANSITION_MAP(NULL) + if(_event_ignored) { + _event_ignored = false; + _observer.error(M2MInterface::NotAllowed); + } + tr_debug("M2MInterfaceImpl::unregister_object - OUT"); +} + +void M2MInterfaceImpl::set_queue_sleep_handler(callback_handler handler) +{ + tr_debug("M2MInterfaceImpl::set_queue_sleep_handler()"); + _callback_handler = handler; +} + +void M2MInterfaceImpl::set_random_number_callback(random_number_cb callback) +{ + if(_security_connection) { + _security_connection->set_random_number_callback(callback); + } +} + +void M2MInterfaceImpl::set_entropy_callback(entropy_cb callback) +{ + if(_security_connection) { + _security_connection->set_entropy_callback(callback); + } +} + +void M2MInterfaceImpl::set_platform_network_handler(void *handler) +{ + tr_debug("M2MInterfaceImpl::set_platform_network_handler()"); + if(_connection_handler) { + _connection_handler->set_platform_network_handler(handler); + } +} + +void M2MInterfaceImpl::coap_message_ready(uint8_t *data_ptr, + uint16_t data_len, + sn_nsdl_addr_s *address_ptr) +{ + tr_debug("M2MInterfaceImpl::coap_message_ready"); + internal_event(STATE_SENDING_COAP_DATA); + if(!_connection_handler->send_data(data_ptr,data_len,address_ptr)) { + internal_event( STATE_IDLE); + tr_error("M2MInterfaceImpl::coap_message_ready() - M2MInterface::NetworkError"); + if (!_reconnecting) { + _observer.error(M2MInterface::NetworkError); + } + } +} + +void M2MInterfaceImpl::client_registered(M2MServer *server_object) +{ + tr_debug("M2MInterfaceImpl::client_registered"); + _retry_count = 0; + internal_event(STATE_REGISTERED); + //Inform client is registered. + //TODO: manage register object in a list. + _observer.object_registered(_register_server,*server_object); +} + +void M2MInterfaceImpl::registration_updated(const M2MServer &server_object) +{ + tr_debug("M2MInterfaceImpl::registration_updated"); + internal_event(STATE_REGISTERED); + _observer.registration_updated(_register_server,server_object); +} + +void M2MInterfaceImpl::registration_error(uint8_t error_code, bool retry) +{ + tr_debug("M2MInterfaceImpl::registration_error code [%d]", error_code); + // Try to register again + if (retry) { + socket_error(M2MConnectionHandler::SOCKET_SEND_ERROR); + } else { + internal_event(STATE_IDLE); + _observer.error((M2MInterface::Error)error_code); + } +} + +void M2MInterfaceImpl::client_unregistered() +{ + tr_debug("M2MInterfaceImpl::client_unregistered()"); + internal_event(STATE_UNREGISTERED); + //TODO: manage register object in a list. + _observer.object_unregistered(_register_server); +} + +void M2MInterfaceImpl::bootstrap_done(M2MSecurity *security_object) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::bootstrap_done"); + _bootstrap_timer->stop_timer(); + internal_event(STATE_BOOTSTRAPPED); + _observer.bootstrap_done(security_object); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::bootstrap_error() +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::bootstrap_error()"); + _bootstrap_timer->stop_timer(); + internal_event(STATE_IDLE); + _observer.error(M2MInterface::BootstrapFailed); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::coap_data_processed() +{ + tr_debug("M2MInterfaceImpl::coap_data_processed()"); + internal_event(STATE_COAP_DATA_PROCESSED); +} + +void M2MInterfaceImpl::value_updated(M2MBase *base) +{ + tr_debug("M2MInterfaceImpl::value_updated(M2MBase *base)"); + if(base) { + M2MBase::BaseType type = base->base_type(); + _observer.value_updated(base, type); + } +} + +void M2MInterfaceImpl::data_available(uint8_t* data, + uint16_t data_size, + const M2MConnectionObserver::SocketAddress &address) +{ + tr_debug("M2MInterfaceImpl::data_available"); + ReceivedData *event = new ReceivedData(); + event->_data = data; + event->_size = data_size; + event->_address = &address; + internal_event(STATE_COAP_DATA_RECEIVED, event); +} + +void M2MInterfaceImpl::socket_error(uint8_t error_code, bool retry) +{ + tr_debug("M2MInterfaceImpl::socket_error: (%d), retry (%d), reconnecting (%d)", error_code, retry, _reconnecting); + if (!_retry_timer_expired && _reconnecting) { + tr_debug("M2MInterfaceImpl::socket_error - retry timer running - return"); + return; + } + M2MInterface::Error error = M2MInterface::ErrorNone; + switch (error_code) { + case M2MConnectionHandler::SSL_CONNECTION_ERROR: + error = M2MInterface::SecureConnectionFailed; + break; + case M2MConnectionHandler::DNS_RESOLVING_ERROR: + error = M2MInterface::DnsResolvingFailed; + break; + case M2MConnectionHandler::SOCKET_READ_ERROR: + error = M2MInterface::NetworkError; + break; + case M2MConnectionHandler::SOCKET_SEND_ERROR: + error = M2MInterface::NetworkError; + break; + case M2MConnectionHandler::SSL_HANDSHAKE_ERROR: + error = M2MInterface::SecureConnectionFailed; + break; + case M2MConnectionHandler::SOCKET_ABORT: + error = M2MInterface::NetworkError; + break; + default: + break; + } + + // Try to do reconnecting + if (retry) { + if (_retry_count < MBED_CLIENT_RECONNECTION_COUNT) { + internal_event(STATE_IDLE); + _reconnecting = true; + _retry_count++; + _connection_handler->stop_listening(); + int retry_time = MBED_CLIENT_RECONNECTION_INTERVAL * + MBED_CLIENT_RECONNECTION_COUNT * _retry_count * 1000; + _retry_timer_expired = false; + _retry_timer->start_timer(retry_time, + M2MTimerObserver::RetryTimer); + tr_debug("M2MInterfaceImpl::socket_error - reconnecting in %d(s), count %d/%d", retry_time / 1000, + _retry_count, MBED_CLIENT_RECONNECTION_COUNT); + } else { + tr_debug("M2MInterfaceImpl::socket_error - no more retries"); + _connection_handler->stop_listening(); + _retry_timer->stop_timer(); + retry = false; + } + } + // Inform application + if (!retry && M2MInterface::ErrorNone != error) { + tr_debug("M2MInterfaceImpl::socket_error - send error to application"); + _connection_handler->stop_listening(); + _retry_timer->stop_timer(); + _retry_count = 0; + _reconnecting = false; + _observer.error(error); + internal_event(STATE_IDLE); + } +} + +void M2MInterfaceImpl::address_ready(const M2MConnectionObserver::SocketAddress &address, + M2MConnectionObserver::ServerType server_type, + const uint16_t server_port) +{ + tr_debug("M2MInterfaceImpl::address_ready"); + ResolvedAddressData *data = new ResolvedAddressData(); + data->_address = &address; + data->_port = server_port; + if( M2MConnectionObserver::Bootstrap == server_type) { + tr_debug("M2MInterfaceImpl::address_ready() Server Type Bootstrap"); + internal_event(STATE_BOOTSTRAP_ADDRESS_RESOLVED, data); + } else { + tr_debug("M2MInterfaceImpl::address_ready() Server Type LWM2M"); + internal_event(STATE_REGISTER_ADDRESS_RESOLVED, data); + } +} + +void M2MInterfaceImpl::data_sent() +{ + tr_debug("M2MInterfaceImpl::data_sent()"); + if(_binding_mode == M2MInterface::UDP_QUEUE || + _binding_mode == M2MInterface::TCP_QUEUE || + _binding_mode == M2MInterface::SMS_QUEUE || + _binding_mode == M2MInterface::UDP_SMS_QUEUE) { + if(_callback_handler) { + _queue_sleep_timer->stop_timer(); + _queue_sleep_timer->start_timer(MBED_CLIENT_RECONNECTION_COUNT*MBED_CLIENT_RECONNECTION_INTERVAL*1000, + M2MTimerObserver::QueueSleep); + } + } + internal_event(STATE_COAP_DATA_SENT); +} + +void M2MInterfaceImpl::timer_expired(M2MTimerObserver::Type type) +{ + tr_debug("M2MInterfaceImpl::timer_expired()"); + if(M2MTimerObserver::QueueSleep == type) { + if(_callback_handler) { + _callback_handler(); + } + } + else if (M2MTimerObserver::RetryTimer == type) { + _retry_timer_expired = true; + _listen_port = rand() % 64511 + 1024; + tr_debug("M2MInterfaceImpl::timer_expired() - new port: %d", _listen_port); + _connection_handler->bind_connection(_listen_port); + internal_event(STATE_REGISTER); + } + else if (M2MTimerObserver::BootstrapTimer == type) { + bootstrap_error(); + } +} + +// state machine sits here. +void M2MInterfaceImpl::state_idle(EventData* /*data*/) +{ + tr_debug("M2MInterfaceImpl::state_idle"); + _nsdl_interface->stop_timers(); + _register_ongoing = false; + _update_register_ongoing = false; +} + +void M2MInterfaceImpl::state_bootstrap( EventData *data) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::state_bootstrap"); + // Start with bootstrapping preparation + bool success = false; + if(data) { + M2MSecurityData *event = static_cast<M2MSecurityData *> (data); + M2MSecurity *security = event->_object; + if(security) { + if(M2MSecurity::Bootstrap == security->server_type()) { + tr_debug("M2MInterfaceImpl::state_bootstrap - server_type : M2MSecurity::Bootstrap"); + String server_address = security->resource_value_string(M2MSecurity::M2MServerUri); + tr_debug("M2MInterfaceImpl::state_bootstrap - server_address %s", server_address.c_str()); + _bootstrap_timer->start_timer(MBED_CLIENT_RECONNECTION_COUNT * MBED_CLIENT_RECONNECTION_INTERVAL * 8 * 1000, + M2MTimerObserver::BootstrapTimer); + String ip_address; + String coap; + if(server_address.compare(0,sizeof(COAP)-1,COAP) == 0) { + coap = COAP; + } + else if(server_address.compare(0,sizeof(COAPS)-1,COAPS) == 0) { + security->resource_value_int(M2MSecurity::SecurityMode) != M2MSecurity::NoSecurity ? coap = COAPS: coap = ""; + } + if(!coap.empty()) { + server_address = server_address.substr(coap.size(), + server_address.size()-coap.size()); + + process_address(server_address, ip_address, _server_port); + + tr_debug("M2MInterfaceImpl::state_bootstrap - IP address %s , Port %d", ip_address.c_str(), _server_port); + // If bind and resolving server address succeed then proceed else + // return error to the application and go to Idle state. + if(ip_address.empty()) { + tr_error("M2MInterfaceImpl::state_bootstrap - set error as M2MInterface::InvalidParameters"); + success = false; + } else if(_connection_handler->resolve_server_address(ip_address, + _server_port, + M2MConnectionObserver::Bootstrap, + security)) { + tr_debug("M2MInterfaceImpl::state_bootstrap - resolve_server_address - success"); + success = true; + } + } + } + } + } + if(!success) { + tr_error("M2MInterfaceImpl::state_bootstrap - M2MInterface::InvalidParameters"); + _observer.error(M2MInterface::InvalidParameters); + internal_event(STATE_IDLE); + } +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::state_bootstrap_address_resolved( EventData *data) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::state_bootstrap_address_resolved"); + ResolvedAddressData *event = static_cast<ResolvedAddressData *> (data); + sn_nsdl_addr_s address; + + M2MInterface::NetworkStack stack = event->_address->_stack; + + if(M2MInterface::LwIP_IPv4 == stack) { + tr_debug("M2MInterfaceImpl::state_bootstrap_address_resolved : IPv4 address"); + address.type = SN_NSDL_ADDRESS_TYPE_IPV4; + address.addr_len = 4; + } else if((M2MInterface::LwIP_IPv6 == stack) || + (M2MInterface::Nanostack_IPv6 == stack)) { + tr_debug("M2MInterfaceImpl::state_bootstrap_address_resolved : IPv6 address"); + address.type = SN_NSDL_ADDRESS_TYPE_IPV6; + address.addr_len = 16; + } + address.port = event->_port; + address.addr_ptr = (uint8_t*)event->_address->_address; + address.addr_len = event->_address->_length; + _connection_handler->start_listening_for_data(); + + // Include domain id to be part of endpoint name + String new_ep_name; + new_ep_name += _nsdl_interface->endpoint_name(); + if (!_domain.empty()) { + new_ep_name += '@'; + new_ep_name += _domain; + } + if(_nsdl_interface->create_bootstrap_resource(&address, new_ep_name)) { + tr_debug("M2MInterfaceImpl::state_bootstrap_address_resolved : create_bootstrap_resource - success"); + internal_event(STATE_BOOTSTRAP_RESOURCE_CREATED); + } else{ + // If resource creation fails then inform error to application + tr_error("M2MInterfaceImpl::state_bootstrap_address_resolved : M2MInterface::InvalidParameters"); + internal_event(STATE_IDLE); + _observer.error(M2MInterface::InvalidParameters); + } +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::state_bootstrap_resource_created( EventData */*data*/) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::state_bootstrap_resource_created"); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::state_bootstrapped( EventData */*data*/) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::state_bootstrapped"); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::state_register( EventData *data) +{ + tr_debug("M2MInterfaceImpl::state_register"); + if (!_security) { + M2MInterface::Error error = M2MInterface::InvalidParameters; + // Start with registration preparation + if(data) { + M2MRegisterData *event = static_cast<M2MRegisterData *> (data); + _security = event->_object; + if(_security) { + if(M2MSecurity::M2MServer == _security->server_type()) { + tr_debug("M2MInterfaceImpl::state_register - server_type : M2MSecurity::M2MServer"); + if(_nsdl_interface->create_nsdl_list_structure(event->_object_list)) { + tr_debug("M2MInterfaceImpl::state_register - create_nsdl_list_structure - success"); + // If the nsdl resource structure is created successfully + String server_address = _security->resource_value_string(M2MSecurity::M2MServerUri); + tr_debug("M2MInterfaceImpl::state_register - server_address %s", server_address.c_str()); + String coap; + if(server_address.compare(0,sizeof(COAP)-1,COAP) == 0) { + coap = COAP; + } + else if(server_address.compare(0,sizeof(COAPS)-1,COAPS) == 0) { + _security->resource_value_int(M2MSecurity::SecurityMode) != M2MSecurity::NoSecurity ? coap = COAPS: coap = ""; + } + if(!coap.empty()) { + server_address = server_address.substr(coap.size(), + server_address.size() - coap.size()); + process_address(server_address, _server_ip_address, _server_port); + + tr_debug("M2MInterfaceImpl::state_register - IP address %s , Port %d", _server_ip_address.c_str(), _server_port); + if(!_server_ip_address.empty()) { + // Connection related errors are coming through callback + error = M2MInterface::ErrorNone; + _connection_handler->resolve_server_address(_server_ip_address,_server_port, + M2MConnectionObserver::LWM2MServer, + _security); + } + } + } + } + } + } + if (error != M2MInterface::ErrorNone) { + tr_error("M2MInterfaceImpl::state_register - set error as M2MInterface::InvalidParameters"); + internal_event(STATE_IDLE); + _observer.error(error); + } + } else { + _connection_handler->resolve_server_address(_server_ip_address,_server_port, + M2MConnectionObserver::LWM2MServer, + _security); + } +} + +void M2MInterfaceImpl::process_address(const String& server_address, String& ip_address, uint16_t& port) { + + int colonFound = server_address.find_last_of(':'); //10 + if(colonFound != -1) { + ip_address = server_address.substr(0,colonFound); + port = atoi(server_address.substr(colonFound+1, + server_address.size()-ip_address.size()).c_str()); + colonFound = ip_address.find_last_of(']'); + if(ip_address.compare(0,1,"[") == 0) { + if(colonFound == -1) { + ip_address.clear(); + } else { + ip_address = ip_address.substr(1,colonFound-1); + } + } else if(colonFound != -1) { + ip_address.clear(); + } + } +} + +void M2MInterfaceImpl::state_register_address_resolved( EventData *data) +{ + tr_debug("M2MInterfaceImpl::state_register_address_resolved"); + if(data) { + ResolvedAddressData *event = static_cast<ResolvedAddressData *> (data); + + sn_nsdl_addr_type_e address_type = SN_NSDL_ADDRESS_TYPE_IPV6; + + M2MInterface::NetworkStack stack = event->_address->_stack; + + if(M2MInterface::LwIP_IPv4 == stack) { + tr_debug("M2MInterfaceImpl::state_register_address_resolved : IPv4 address"); + address_type = SN_NSDL_ADDRESS_TYPE_IPV4; + } else if((M2MInterface::LwIP_IPv6 == stack) || + (M2MInterface::Nanostack_IPv6 == stack)) { + tr_debug("M2MInterfaceImpl::state_register_address_resolved : IPv6 address"); + address_type = SN_NSDL_ADDRESS_TYPE_IPV6; + } + _connection_handler->start_listening_for_data(); + if(_nsdl_interface->send_register_message((uint8_t*)event->_address->_address,event->_port, address_type)) { + internal_event(STATE_REGISTER_RESOURCE_CREATED); + } else { + // If resource creation fails then inform error to application + tr_error("M2MInterfaceImpl::state_register_address_resolved : M2MInterface::InvalidParameters"); + internal_event(STATE_IDLE); + _observer.error(M2MInterface::InvalidParameters); + } + } +} + +void M2MInterfaceImpl::state_register_resource_created( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_register_resource_created"); +} + +void M2MInterfaceImpl::state_registered( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_registered"); + _retry_count = 0; + _register_ongoing = false; + _update_register_ongoing = false; + _reconnecting = false; +} + +void M2MInterfaceImpl::state_update_registration( EventData *data) +{ + tr_debug("M2MInterfaceImpl::state_update_registration"); + // Start with registration preparation + if(data) { + M2MUpdateRegisterData *event = static_cast<M2MUpdateRegisterData *> (data); + _nsdl_interface->send_update_registration(event->_lifetime); + } +} + +void M2MInterfaceImpl::state_unregister( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_unregister"); + internal_event(STATE_SENDING_COAP_DATA); + if(!_nsdl_interface->send_unregister_message()) { + tr_error("M2MInterfaceImpl::state_unregister : M2MInterface::NotRegistered"); + internal_event(STATE_IDLE); + _observer.error(M2MInterface::NotRegistered); + } +} + +void M2MInterfaceImpl::state_unregistered( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_unregistered"); + internal_event(STATE_IDLE); +} + +void M2MInterfaceImpl::state_sending_coap_data( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_sending_coap_data"); + internal_event(STATE_WAITING); +} + +void M2MInterfaceImpl::state_coap_data_sent( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_coap_data_sent"); + internal_event(STATE_WAITING); +} + +void M2MInterfaceImpl::state_coap_data_received( EventData *data) +{ + tr_debug("M2MInterfaceImpl::state_coap_data_received"); + if(data) { + ReceivedData *event = static_cast<ReceivedData*> (data); + sn_nsdl_addr_s address; + + M2MInterface::NetworkStack stack = event->_address->_stack; + + if(M2MInterface::LwIP_IPv4 == stack) { + tr_debug("M2MInterfaceImpl::state_coap_data_received : IPv4 address"); + address.type = SN_NSDL_ADDRESS_TYPE_IPV4; + address.addr_len = 4; + } else if((M2MInterface::LwIP_IPv6 == stack) || + (M2MInterface::Nanostack_IPv6 == stack)) { + tr_debug("M2MInterfaceImpl::state_coap_data_received : IPv6 address"); + address.type = SN_NSDL_ADDRESS_TYPE_IPV6; + address.addr_len = 16; + } + address.port = event->_address->_port; + address.addr_ptr = (uint8_t*)event->_address->_address; + address.addr_len = event->_address->_length; + + // Process received data + internal_event(STATE_PROCESSING_COAP_DATA); + if(!_nsdl_interface->process_received_data(event->_data, + event->_size, + &address)) { + tr_error("M2MInterfaceImpl::state_coap_data_received : M2MInterface::ResponseParseFailed"); + _observer.error(M2MInterface::ResponseParseFailed); + } + } +} + +void M2MInterfaceImpl::state_processing_coap_data( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_processing_coap_data"); + internal_event(STATE_WAITING); +} + +void M2MInterfaceImpl::state_coap_data_processed( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_coap_data_processed"); + internal_event(STATE_WAITING); +} + +void M2MInterfaceImpl::state_waiting( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_waiting"); +} + +// generates an external event. called once per external event +// to start the state machine executing +void M2MInterfaceImpl::external_event(uint8_t new_state, + EventData* p_data) +{ + tr_debug("M2MInterfaceImpl::external_event : new state %d", new_state); + // if we are supposed to ignore this event + if (new_state == EVENT_IGNORED) { + tr_debug("M2MInterfaceImpl::external_event : new state is EVENT_IGNORED"); + // just delete the event data, if any + if (p_data) { + delete p_data; + p_data = NULL; + } + _event_ignored = true; + } + else { + tr_debug("M2MInterfaceImpl::external_event : handle new state"); + // generate the event and execute the state engine + internal_event(new_state, p_data); + } +} + +// generates an internal event. called from within a state +// function to transition to a new state +void M2MInterfaceImpl::internal_event(uint8_t new_state, + EventData* p_data) +{ + tr_debug("M2MInterfaceImpl::internal_event : new state %d", new_state); + _event_data = p_data; + _event_generated = true; + _current_state = new_state; + state_engine(); +} + +// the state engine executes the state machine states +void M2MInterfaceImpl::state_engine (void) +{ + tr_debug("M2MInterfaceImpl::state_engine"); + EventData* p_data_temp = NULL; + + // while events are being generated keep executing states + while (_event_generated) { + p_data_temp = _event_data; // copy of event data pointer + _event_data = NULL; // event data used up, reset ptr + _event_generated = false; // event used up, reset flag + + assert(_current_state < _max_states); + + state_function( _current_state, p_data_temp ); + + // if event data was used, then delete it + if (p_data_temp) { + delete p_data_temp; + p_data_temp = NULL; + } + } +} + +void M2MInterfaceImpl::state_function( uint8_t current_state, EventData* data ) +{ + switch( current_state ) { + case STATE_IDLE: + M2MInterfaceImpl::state_idle(data); + break; + case STATE_BOOTSTRAP: + #ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + M2MInterfaceImpl::state_bootstrap(data); + #endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + break; + case STATE_BOOTSTRAP_ADDRESS_RESOLVED: + #ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + M2MInterfaceImpl::state_bootstrap_address_resolved(data); + #endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + break; + case STATE_BOOTSTRAP_RESOURCE_CREATED: + #ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + M2MInterfaceImpl::state_bootstrap_resource_created(data); + #endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + break; + case STATE_BOOTSTRAPPED: + #ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + M2MInterfaceImpl::state_bootstrapped(data); + #endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + break; + case STATE_REGISTER: + M2MInterfaceImpl::state_register(data); + break; + case STATE_REGISTER_ADDRESS_RESOLVED: + M2MInterfaceImpl::state_register_address_resolved(data); + break; + case STATE_REGISTER_RESOURCE_CREATED: + M2MInterfaceImpl::state_register_resource_created(data); + break; + case STATE_REGISTERED: + M2MInterfaceImpl::state_registered(data); + break; + case STATE_UPDATE_REGISTRATION: + M2MInterfaceImpl::state_update_registration(data); + break; + case STATE_UNREGISTER: + M2MInterfaceImpl::state_unregister(data); + break; + case STATE_UNREGISTERED: + M2MInterfaceImpl::state_unregistered(data); + break; + case STATE_SENDING_COAP_DATA: + M2MInterfaceImpl::state_sending_coap_data(data); + break; + case STATE_COAP_DATA_SENT: + M2MInterfaceImpl::state_coap_data_sent(data); + break; + case STATE_COAP_DATA_RECEIVED: + M2MInterfaceImpl::state_coap_data_received(data); + break; + case STATE_PROCESSING_COAP_DATA: + M2MInterfaceImpl::state_processing_coap_data(data); + break; + case STATE_COAP_DATA_PROCESSED: + M2MInterfaceImpl::state_coap_data_processed(data); + break; + case STATE_WAITING: + M2MInterfaceImpl::state_waiting(data); + break; + } +}