Ram Gandikota
/
metronome
FRDM K64F Metronome
Diff: mbed-client/source/m2minterfaceimpl.cpp
- Revision:
- 0:a2cb7295a1f7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-client/source/m2minterfaceimpl.cpp Sun May 14 18:35:07 2017 +0000 @@ -0,0 +1,1036 @@ +/* + * 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 <stdlib.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" + +#include <stdlib.h> + +#define TRACE_GROUP "mClt" + +#define RESOLVE_SEC_MODE(mode) ((mode == M2MInterface::TCP || mode == M2MInterface::TCP_QUEUE) ? M2MConnectionSecurity::TLS : M2MConnectionSecurity::DTLS) + +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) +: _event_data(NULL), + _bootstrap_timer(NULL), + _server_port(0), + _listen_port(listen_port), + _endpoint_type(ep_type), + _domain( dmn), + _life_time(l_time), + _context_address(con_addr), + _register_server(NULL), + _queue_sleep_timer(*this), + _retry_timer(*this), + _callback_handler(NULL), + _max_states( STATE_MAX_STATES ), + _event_ignored(false), + _event_generated(false), + _reconnecting(false), + _retry_timer_expired(false), + _bootstrapped(true), // True as default to get it working with connector only configuration + _current_state(0), + _retry_count(0), + _binding_mode(mode), + _observer(observer), + _security_connection( new M2MConnectionSecurity( RESOLVE_SEC_MODE(mode) )), + _connection_handler(*this, _security_connection, mode, stack), + _nsdl_interface(*this), + _security(NULL) +{ + tr_debug("M2MInterfaceImpl::M2MInterfaceImpl() -IN"); + _nsdl_interface.create_endpoint(ep_name, + _endpoint_type, + _life_time, + _domain, + (uint8_t)_binding_mode & 0x07, // nsdl binding mode is only 3 least significant bits + _context_address); + + //Here we must use TCP still + __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"); + _connection_handler.stop_listening(); + 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"); + if(!security) { + _observer.error(M2MInterface::InvalidParameters); + return; + } + // 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_bootstrap_wait + 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_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"); + if(!security) { + _observer.error(M2MInterface::InvalidParameters); + return; + } + // Transition to a new state based upon + // the current state of the state machine + //TODO: manage register object in a list. + _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 (EVENT_IGNORED) // state_bootstrap_wait + 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 (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); + } + tr_debug("M2MInterfaceImpl::register_object - OUT"); +} + +void M2MInterfaceImpl::update_registration(M2MSecurity *security_object, const uint32_t lifetime) +{ + tr_debug("M2MInterfaceImpl::update_registration(M2MSecurity *security_object, const uint32_t lifetime)"); + M2MUpdateRegisterData *data = new M2MUpdateRegisterData(); + data->_object = security_object; + data->_lifetime = lifetime; + start_register_update(data); +} + +void M2MInterfaceImpl::update_registration(M2MSecurity *security_object, + const M2MObjectList &object_list, + const uint32_t lifetime) +{ + tr_debug("M2MInterfaceImpl::update_registration(M2MSecurity *security_object, const M2MObjectList &object_list, const uint32_t lifetime)"); + M2MUpdateRegisterData *data = new M2MUpdateRegisterData(); + data->_object = security_object; + data->_lifetime = lifetime; + data->_object_list = object_list; + start_register_update(data); +} + +void M2MInterfaceImpl::unregister_object(M2MSecurity* /*security*/) +{ + tr_debug("M2MInterfaceImpl::unregister_object - IN"); + tr_debug("M2MInterfaceImpl::unregister_object - current state %d", _current_state); + // 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_bootstrap_wait + 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 (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()"); + _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"); + if (_current_state != STATE_IDLE) { + 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"); + _retry_count = 0; + _reconnecting = false; + _bootstrapped = true; + _bootstrap_timer->stop_timer(); + internal_event(STATE_BOOTSTRAPPED); + _observer.bootstrap_done(security_object); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::bootstrap_wait(M2MSecurity *security_object) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::bootstrap_wait"); + _security = security_object; + internal_event(STATE_BOOTSTRAP_WAIT); +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MInterfaceImpl::bootstrap_error() +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::bootstrap_error()"); + _bootstrapped = false; + _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"); + 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 MBED_CLIENT_RECONNECTION_LOOP < 1 + if (!_retry_timer_expired && _reconnecting) { + tr_debug("M2MInterfaceImpl::socket_error - retry timer running - return"); + return; + } +#endif + + 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) { + _retry_count++; + } +#if MBED_CLIENT_RECONNECTION_LOOP > 0 + else { + tr_debug("M2MInterfaceImpl::socket_error - start again"); + _retry_count = 1; + _observer.error(error); + } +#else + else { + tr_debug("M2MInterfaceImpl::socket_error - no more retries"); + _connection_handler.stop_listening(); + _retry_timer.stop_timer(); + retry = false; + } +#endif + if (retry) { + internal_event(STATE_IDLE); + _reconnecting = true; + _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); + } + } + // 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); + } + } + if (_current_state == STATE_BOOTSTRAP_WAIT) { + // For bootstrap we need to call bootstrap_done callback ONLY after we have + // sent the last ACK and ended in STATE_BOOTSTRAP_WAIT + M2MSecurity *sec = _security; + _security = NULL; + bootstrap_done(sec); + } + else { + 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; + if (_bootstrapped) { + internal_event(STATE_REGISTER); + } else { + internal_event(STATE_BOOTSTRAP); + } + } + 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(); +} + +void M2MInterfaceImpl::state_bootstrap(EventData *data) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MInterfaceImpl::state_bootstrap"); + // Start with bootstrapping preparation + _bootstrapped = false; + M2MSecurityData *event = static_cast<M2MSecurityData *> (data); + if(!_security) { + M2MInterface::Error error = M2MInterface::InvalidParameters; + if (event) { + _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 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_bootstrap - IP address %s , Port %d", _server_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(!_server_ip_address.empty()) { + error = M2MInterface::ErrorNone; + _connection_handler.resolve_server_address(_server_ip_address, + _server_port, + M2MConnectionObserver::Bootstrap, + _security); + } + } + } + } + } + if (error != M2MInterface::ErrorNone) { + tr_error("M2MInterfaceImpl::state_bootstrap - set error as M2MInterface::InvalidParameters"); + internal_event(STATE_IDLE); + _observer.error(error); + } + } else { + _listen_port = rand() % 64511 + 1024; + _connection_handler.stop_listening(); + _connection_handler.bind_connection(_listen_port); + _connection_handler.resolve_server_address(_server_ip_address, + _server_port, + M2MConnectionObserver::Bootstrap, + _security); + } +#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"); + if (data) { + 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; + } 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.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 + StringBuffer<M2MBase::MAX_PATH_SIZE> new_ep_name; + + if(!new_ep_name.ensure_space(_nsdl_interface.endpoint_name().size() + 1)) + { + tr_error("MM2MInterfaceImpl::state_bootstrap_address_resolved : name too long"); + _observer.error(M2MInterface::InvalidParameters); + return; + } + + new_ep_name.append(_nsdl_interface.endpoint_name().c_str()); + + if (!_domain.empty()) { + + if(!new_ep_name.ensure_space(_nsdl_interface.endpoint_name().size() + 1 + _domain.size() + 1)) + { + tr_error("MM2MInterfaceImpl::state_bootstrap_address_resolved : name + domain too long"); + _observer.error(M2MInterface::InvalidParameters); + return; + } + + new_ep_name.append('@'); + new_ep_name.append(_domain.c_str()); + + } + if(_nsdl_interface.create_bootstrap_resource(&address, new_ep_name.c_str())) { + 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"); + M2MRegisterData *event = static_cast<M2MRegisterData *> (data); + if (!_security) { + M2MInterface::Error error = M2MInterface::InvalidParameters; + // Start with registration preparation + if(event) { + _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 { + _listen_port = rand() % 64511 + 1024; + _connection_handler.stop_listening(); + if (event) { + _nsdl_interface.create_nsdl_list_structure(event->_object_list); + } + _connection_handler.bind_connection(_listen_port); + _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->_address->_length, + event->_port, address_type)) { + // 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_registered( EventData */*data*/) +{ + tr_debug("M2MInterfaceImpl::state_registered"); + _retry_count = 0; + _reconnecting = false; +} + +void M2MInterfaceImpl::state_update_registration(EventData *data) +{ + tr_debug("M2MInterfaceImpl::state_update_registration"); + if(data) { + M2MUpdateRegisterData *event = static_cast<M2MUpdateRegisterData *> (data); + // Create new resources if any + if (!event->_object_list.empty()) { + _nsdl_interface.create_nsdl_list_structure(event->_object_list); + } + _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_BOOTSTRAP_WAIT: + #ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + // Do nothing, we're just waiting for data_sent callback + #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_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; + } +} + +void M2MInterfaceImpl::start_register_update(M2MUpdateRegisterData *data) { + tr_debug("M2MInterfaceImpl::start_register_update - IN"); + if(!data || (data->_lifetime != 0 && (data->_lifetime < MINIMUM_REGISTRATION_TIME))) { + _observer.error(M2MInterface::InvalidParameters); + } + 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_bootstrap_wait + 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 (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); + } +}