Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ServiceClient.cpp Source File

ServiceClient.cpp

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 // Note: this macro is needed on armcc to get the the PRI*32 macros
00020 // from inttypes.h in a C++ code.
00021 #ifndef __STDC_FORMAT_MACROS
00022 #define __STDC_FORMAT_MACROS
00023 #endif
00024 #include <inttypes.h>
00025 
00026 #include "include/ServiceClient.h"
00027 #include "include/CloudClientStorage.h"
00028 #include "include/UpdateClientResources.h"
00029 #include "include/UpdateClient.h"
00030 #include "factory_configurator_client.h"
00031 #include "mbed-client/m2mconstants.h"
00032 #include "mbed-trace/mbed_trace.h"
00033 #include "pal.h"
00034 #ifndef MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT
00035 #include "CertificateEnrollmentClient.h"
00036 #endif // MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT
00037 
00038 #if MBED_CLOUD_CLIENT_STL_API
00039 #include <string>
00040 #endif
00041 
00042 #include <assert.h>
00043 
00044 #define TRACE_GROUP "mClt"
00045 
00046 #define CONNECT 0
00047 #define ERROR_UPDATE "Update has failed, check MbedCloudClient::Error"
00048 
00049 /* lookup table for printing hexadecimal values */
00050 const uint8_t ServiceClient::hex_table[16] = {
00051     '0', '1', '2', '3', '4', '5', '6', '7',
00052     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
00053 };
00054 
00055 ServiceClient::ServiceClient(ServiceClientCallback& callback)
00056 : _service_callback(callback),
00057   _service_uri(NULL),
00058   _stack(NULL),
00059   _client_objs(NULL),
00060   _current_state(State_Init),
00061   _event_generated(false),
00062   _state_engine_running(false),
00063 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00064   _setup_update_client(false),
00065 #endif
00066   _connector_client(this)
00067 {
00068 }
00069 
00070 ServiceClient::~ServiceClient()
00071 {
00072 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00073     ARM_UC_HUB_Uninitialize();
00074 #endif
00075 #ifndef MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT
00076     CertificateEnrollmentClient::finalize();
00077 #endif // MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT
00078 }
00079 
00080 void ServiceClient::initialize_and_register(M2MBaseList& reg_objs)
00081 {
00082     tr_debug("ServiceClient::initialize_and_register");
00083     if(_current_state == State_Init ||
00084        _current_state == State_Unregister ||
00085        _current_state == State_Failure) {
00086         _client_objs = &reg_objs;
00087 
00088 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00089         tr_debug("ServiceClient::initialize_and_register: update client supported");
00090 
00091         if(!_setup_update_client) {
00092             _setup_update_client = true;
00093 
00094 #ifdef MBED_CLOUD_DEV_UPDATE_ID
00095             /* Overwrite values stored in KCM. This is for development only
00096                since these IDs should be provisioned in the factory.
00097             */
00098             tr_debug("ServiceClient::initialize_and_register: update IDs defined");
00099 
00100             /* Delete VendorId */
00101             ccs_delete_item("mbed.VendorId", CCS_CONFIG_ITEM);
00102             /* Store Vendor Id to mbed.VendorId. No conversion is performed. */
00103             set_device_resource_value(M2MDevice::Manufacturer,
00104                                       (const char*) arm_uc_vendor_id,
00105                                       arm_uc_vendor_id_size);
00106 
00107             /* Delete ClassId */
00108             ccs_delete_item("mbed.ClassId", CCS_CONFIG_ITEM);
00109             /* Store Class Id to mbed.ClassId. No conversion is performed. */
00110             set_device_resource_value(M2MDevice::ModelNumber,
00111                                       (const char*) arm_uc_class_id,
00112                                       arm_uc_class_id_size);
00113 #endif /* MBED_CLOUD_DEV_UPDATE_ID */
00114 
00115 #ifdef ARM_UPDATE_CLIENT_VERSION
00116             /* Inject Update Client version number if no other software
00117                version is present in the KCM.
00118             */
00119             tr_debug("ServiceClient::initialize_and_register: update version defined");
00120 
00121             const size_t buffer_size = 16;
00122             uint8_t buffer[buffer_size];
00123             size_t size = 0;
00124 
00125             /* check if software version is already set */
00126             ccs_status_e status = ccs_get_item(KEY_DEVICE_SOFTWAREVERSION,
00127                                                buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00128 
00129             if (status == CCS_STATUS_KEY_DOESNT_EXIST) {
00130                 tr_debug("ServiceClient::initialize_and_register: insert update version");
00131 
00132                 /* insert value from Update Client Common */
00133                 ccs_set_item(KEY_DEVICE_SOFTWAREVERSION,
00134                              (const uint8_t*) ARM_UPDATE_CLIENT_VERSION,
00135                              sizeof(ARM_UPDATE_CLIENT_VERSION),
00136                              CCS_CONFIG_ITEM);
00137             }
00138 #endif /* ARM_UPDATE_CLIENT_VERSION */
00139 
00140             /* Update Client adds the OMA LWM2M Firmware Update object */
00141             UpdateClient::populate_object_list(*_client_objs);
00142 
00143             /* Initialize Update Client */
00144             FP1<void, int32_t> callback(this, &ServiceClient::update_error_callback);
00145             UpdateClient::UpdateClient(callback, _connector_client.m2m_interface(), this);
00146         }
00147         // else branch is required for re-initialization.
00148         else {
00149             finish_initialization();
00150         }
00151 #else /* MBED_CLOUD_CLIENT_SUPPORT_UPDATE */
00152         finish_initialization();
00153 #endif /* MBED_CLOUD_CLIENT_SUPPORT_UPDATE */
00154     } else if (_current_state == State_Success) {
00155         state_success();
00156     }
00157 }
00158 
00159 void ServiceClient::finish_initialization(void)
00160 {
00161     /* Device Object is mandatory.
00162        Get instance and add it to object list
00163     */
00164     M2MDevice *device_object = device_object_from_storage();
00165 
00166 #ifndef MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT
00167         // Initialize the certificate enrollment resources and module
00168         if (CertificateEnrollmentClient::init(*_client_objs, &_connector_client.est_client()) != CE_STATUS_SUCCESS) {
00169             _service_callback.error((int)CE_STATUS_INIT_FAILED, "Certificate Enrollment initialization failed");
00170         }
00171 #endif /* !MBED_CONF_MBED_CLOUD_CLIENT_DISABLE_CERTIFICATE_ENROLLMENT */
00172 
00173     if (device_object) {
00174         /* Publish device object resource to mds */
00175         M2MResourceList list = device_object->object_instance()->resources();
00176         if(!list.empty()) {
00177             M2MResourceList::const_iterator it;
00178             it = list.begin();
00179             for ( ; it != list.end(); it++ ) {
00180                 (*it)->set_register_uri(true);
00181             }
00182         }
00183 
00184         /* Add Device Object to object list. */
00185         _client_objs->push_back(device_object);
00186     }
00187 
00188     internal_event(State_Bootstrap);
00189 }
00190 
00191 ConnectorClient &ServiceClient::connector_client()
00192 {
00193     return _connector_client;
00194 }
00195 
00196 const ConnectorClient &ServiceClient::connector_client() const
00197 {
00198     return _connector_client;
00199 }
00200 
00201 // generates an internal event. called from within a state
00202 // function to transition to a new state
00203 void ServiceClient::internal_event(StartupMainState new_state)
00204 {
00205     tr_debug("ServiceClient::internal_event: state: %d -> %d", _current_state, new_state);
00206 
00207     _event_generated = true;
00208     _current_state = new_state;
00209 
00210     if (!_state_engine_running) {
00211         state_engine();
00212     }
00213 }
00214 
00215 // the state engine executes the state machine states
00216 void ServiceClient::state_engine(void)
00217 {
00218     tr_debug("ServiceClient::state_engine");
00219 
00220     // this simple flagging gets rid of recursive calls to this method
00221     _state_engine_running = true;
00222 
00223     // while events are being generated keep executing states
00224     while (_event_generated) {
00225         _event_generated = false;  // event used up, reset flag
00226 
00227         state_function(_current_state);
00228     }
00229 
00230     _state_engine_running = false;
00231 }
00232 
00233 void ServiceClient::state_function(StartupMainState current_state)
00234 {
00235     switch (current_state) {
00236         case State_Init:        // -> Goes to bootstrap state
00237         case State_Bootstrap:    // -> State_Register OR State_Failure
00238             state_bootstrap();
00239             break;
00240         case State_Register:     // -> State_Succes OR State_Failure
00241             state_register();
00242             break;
00243         case State_Success:      // return success to user
00244             state_success();
00245             break;
00246         case State_Failure:      // return error to user
00247             state_failure();
00248             break;
00249         case State_Unregister:   // return error to user
00250             state_unregister();
00251             break;
00252     }
00253 }
00254 
00255 void ServiceClient::state_bootstrap()
00256 {
00257     tr_info("ServiceClient::state_bootstrap()");
00258     bool credentials_ready = _connector_client.connector_credentials_available();
00259     bool bootstrap = _connector_client.use_bootstrap();
00260     tr_info("ServiceClient::state_bootstrap() - lwm2m credentials available: %d", credentials_ready);
00261     tr_info("ServiceClient::state_bootstrap() - use bootstrap: %d", bootstrap);
00262 
00263     bool get_time = false;
00264 #if defined (PAL_USE_SECURE_TIME) && (PAL_USE_SECURE_TIME == 1)
00265     // Strong time is mandatory in bootstrap mode
00266     get_time = pal_osGetTime() == 0 ? true : false;
00267 #endif
00268     // Fallback to rebootstrap if time fetch fails in PAL_USE_SECURE_TIME case
00269     if (credentials_ready && bootstrap && get_time) {
00270         _connector_client.bootstrap_again();
00271     } else if (credentials_ready || !bootstrap) {
00272         internal_event(State_Register);
00273     } else {
00274         _connector_client.start_bootstrap();
00275     }
00276 }
00277 
00278 void ServiceClient::state_register()
00279 {
00280     tr_info("ServiceClient::state_register()");
00281     _connector_client.start_registration(_client_objs);
00282 }
00283 
00284 void ServiceClient::registration_process_result(ConnectorClient::StartupSubStateRegistration status)
00285 {
00286     tr_debug("ServiceClient::registration_process_result(): status: %d", status);
00287     if (status == ConnectorClient::State_Registration_Success) {
00288         internal_event(State_Success);
00289     } else if(status == ConnectorClient::State_Registration_Failure ||
00290               status == ConnectorClient::State_Bootstrap_Failure){
00291         internal_event(State_Failure); // XXX: the status should be saved to eg. event object
00292     }
00293     if(status == ConnectorClient::State_Bootstrap_Success) {
00294         internal_event(State_Register);
00295     }
00296     if(status == ConnectorClient::State_Unregistered) {
00297         internal_event(State_Unregister);
00298     }
00299     if (status == ConnectorClient::State_Registration_Updated) {
00300         _service_callback.complete(ServiceClientCallback::Service_Client_Status_Register_Updated);
00301     }
00302 }
00303 
00304 void ServiceClient::connector_error(M2MInterface::Error error, const char *reason)
00305 {
00306     tr_error("ServiceClient::connector_error() error %d", (int)error);
00307     if (_current_state == State_Register) {
00308         registration_process_result(ConnectorClient::State_Registration_Failure);
00309     }
00310     else if (_current_state == State_Bootstrap) {
00311         registration_process_result(ConnectorClient::State_Bootstrap_Failure);
00312     }
00313     _service_callback.error(int(error),reason);
00314     internal_event(State_Failure);
00315 }
00316 
00317 void ServiceClient::value_updated(M2MBase *base, M2MBase::BaseType type)
00318 {
00319     tr_debug("ServiceClient::value_updated()");
00320     _service_callback.value_updated(base, type);
00321 }
00322 
00323 void ServiceClient::state_success()
00324 {
00325     tr_info("ServiceClient::state_success()");
00326     // this is verified already at client API level, but this might still catch some logic failures
00327     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Registered);
00328 }
00329 
00330 void ServiceClient::state_failure()
00331 {
00332     tr_error("ServiceClient::state_failure()");
00333     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Failure);
00334 }
00335 
00336 void ServiceClient::state_unregister()
00337 {
00338     tr_debug("ServiceClient::state_unregister()");
00339     _service_callback.complete(ServiceClientCallback::Service_Client_Status_Unregistered);
00340 }
00341 
00342 M2MDevice* ServiceClient::device_object_from_storage()
00343 {
00344     M2MDevice *device_object = M2MInterfaceFactory::create_device();
00345     if (device_object == NULL) {
00346         return NULL;
00347     }
00348     M2MObjectInstance* instance = device_object->object_instance(0);
00349     if(instance == NULL) {
00350         return NULL;
00351     }
00352 
00353     const size_t buffer_size = 128;
00354     uint8_t buffer[buffer_size];
00355     size_t size = 0;
00356     M2MResource *res = NULL;
00357 
00358     // Read values to device object
00359     // create_resource() function returns NULL if resource already exists
00360     ccs_status_e status = ccs_get_item(g_fcc_manufacturer_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00361     if (status == CCS_STATUS_SUCCESS) {
00362         const String data((char*)buffer, size);
00363         res = device_object->create_resource(M2MDevice::Manufacturer, data);
00364         if (res == NULL) {
00365             res = instance->resource(DEVICE_MANUFACTURER);
00366             device_object->set_resource_value(M2MDevice::Manufacturer, data);
00367         }
00368         if(res) {
00369             res->publish_value_in_registration_msg(true);
00370             res->set_auto_observable(true);
00371         }
00372     }
00373     status = ccs_get_item(g_fcc_model_number_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00374     if (status == CCS_STATUS_SUCCESS) {
00375         const String data((char*)buffer, size);
00376         res = device_object->create_resource(M2MDevice::ModelNumber, data);
00377         if (res == NULL) {
00378             res = instance->resource(DEVICE_MODEL_NUMBER);
00379             device_object->set_resource_value(M2MDevice::ModelNumber, data);
00380         }
00381         if(res) {
00382             res->publish_value_in_registration_msg(true);
00383             res->set_auto_observable(true);
00384         }
00385     }
00386     status = ccs_get_item(g_fcc_device_serial_number_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00387     if (status == CCS_STATUS_SUCCESS) {
00388         const String data((char*)buffer, size);
00389         res = device_object->create_resource(M2MDevice::SerialNumber, data);
00390         if (res == NULL) {
00391             res = instance->resource(DEVICE_SERIAL_NUMBER);
00392             device_object->set_resource_value(M2MDevice::SerialNumber, data);
00393         }
00394         if(res) {
00395             res->publish_value_in_registration_msg(true);
00396             res->set_auto_observable(true);
00397         }
00398     }
00399 
00400     status = ccs_get_item(g_fcc_device_type_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00401     if (status == CCS_STATUS_SUCCESS) {
00402         const String data((char*)buffer, size);
00403         res = device_object->create_resource(M2MDevice::DeviceType, data);
00404         if (res == NULL) {
00405             res = instance->resource(DEVICE_DEVICE_TYPE);
00406             device_object->set_resource_value(M2MDevice::DeviceType, data);
00407         }
00408         if(res) {
00409             res->publish_value_in_registration_msg(true);
00410             res->set_auto_observable(true);
00411         }
00412     }
00413 
00414     status = ccs_get_item(g_fcc_hardware_version_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00415     if (status == CCS_STATUS_SUCCESS) {
00416         const String data((char*)buffer, size);
00417         res = device_object->create_resource(M2MDevice::HardwareVersion, data);
00418         if (res == NULL) {
00419             res = instance->resource(DEVICE_HARDWARE_VERSION);
00420             device_object->set_resource_value(M2MDevice::HardwareVersion, data);
00421         }
00422         if(res) {
00423             res->publish_value_in_registration_msg(true);
00424             res->set_auto_observable(true);
00425         }
00426     }
00427 
00428     status = ccs_get_item(KEY_DEVICE_SOFTWAREVERSION, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00429     if (status == CCS_STATUS_SUCCESS) {
00430         const String data((char*)buffer, size);
00431         res = device_object->create_resource(M2MDevice::SoftwareVersion, data);
00432         if (res == NULL) {
00433             res = instance->resource(DEVICE_SOFTWARE_VERSION);
00434             device_object->set_resource_value(M2MDevice::SoftwareVersion, data);
00435         }
00436         if(res) {
00437             res->publish_value_in_registration_msg(true);
00438             res->set_auto_observable(true);
00439         }
00440     }
00441 
00442     uint8_t data[4] = {0};
00443     uint32_t value;
00444     status = ccs_get_item(g_fcc_memory_size_parameter_name, data, 4, &size, CCS_CONFIG_ITEM);
00445     if (status == CCS_STATUS_SUCCESS) {
00446         memcpy(&value, data, 4);
00447         res = device_object->create_resource(M2MDevice::MemoryTotal, value);
00448         if (res == NULL) {
00449             res = instance->resource(DEVICE_MEMORY_TOTAL);
00450             device_object->set_resource_value(M2MDevice::MemoryTotal, value);
00451         }
00452         if(res) {
00453             res->publish_value_in_registration_msg(true);
00454             res->set_auto_observable(true);
00455         }
00456         tr_debug("ServiceClient::device_object_from_storage() - setting memory total value %" PRIu32 " (%s)", value, tr_array(data, 4));
00457     }
00458 
00459     status = ccs_get_item(g_fcc_current_time_parameter_name, data, 4, &size, CCS_CONFIG_ITEM);
00460     if (status == CCS_STATUS_SUCCESS) {
00461         memcpy(&value, data, 4);
00462         res = device_object->create_resource(M2MDevice::CurrentTime, value);
00463         if (res == NULL) {
00464             res = instance->resource(DEVICE_CURRENT_TIME);
00465             device_object->set_resource_value(M2MDevice::CurrentTime, value);
00466         }
00467         if(res) {
00468             res->publish_value_in_registration_msg(true);
00469             res->set_auto_observable(true);
00470         }
00471         tr_debug("ServiceClient::device_object_from_storage() - setting current time value %" PRIu32 " (%s)", value, tr_array(data, 4));
00472     }
00473 
00474     status = ccs_get_item(g_fcc_device_time_zone_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00475     if (status == CCS_STATUS_SUCCESS) {
00476         const String data((char*)buffer, size);
00477         res = device_object->create_resource(M2MDevice::Timezone, data);
00478         if ( res == NULL) {
00479             res = instance->resource(DEVICE_TIMEZONE);
00480             device_object->set_resource_value(M2MDevice::Timezone, data);
00481         }
00482         if(res) {
00483             res->publish_value_in_registration_msg(true);
00484             res->set_auto_observable(true);
00485         }
00486     }
00487 
00488     status = ccs_get_item(g_fcc_offset_from_utc_parameter_name, buffer, buffer_size, &size, CCS_CONFIG_ITEM);
00489     if (status == CCS_STATUS_SUCCESS) {
00490         const String data((char*)buffer, size);
00491         res = device_object->create_resource(M2MDevice::UTCOffset, data);
00492         if (res == NULL) {
00493             res = instance->resource(DEVICE_UTC_OFFSET);
00494             device_object->set_resource_value(M2MDevice::UTCOffset, data);
00495         }
00496         if(res) {
00497             res->publish_value_in_registration_msg(true);
00498             res->set_auto_observable(true);
00499         }
00500     }
00501 
00502     return device_object;
00503 }
00504 
00505 /**
00506  * \brief Set resource value in the Device Object
00507  *
00508  * \param resource Device enum to have value set.
00509  * \param value String object.
00510  * \return True if successful, false otherwise.
00511  */
00512 #if MBED_CLOUD_CLIENT_STL_API
00513 bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
00514                                               const std::string& value)
00515 {
00516     return set_device_resource_value(resource,
00517                                      value.c_str(),
00518                                      value.size());
00519 }
00520 #endif
00521 
00522 /**
00523  * \brief Set resource value in the Device Object
00524  *
00525  * \param resource Device enum to have value set.
00526  * \param value Byte buffer.
00527  * \param length Buffer length.
00528  * \return True if successful, false otherwise.
00529  */
00530 bool ServiceClient::set_device_resource_value(M2MDevice::DeviceResource resource,
00531                                               const char* value,
00532                                               uint32_t length)
00533 {
00534     bool retval = false;
00535 
00536     /* sanity check */
00537     if (value && (length < 256) && (length > 0))
00538     {
00539 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00540         /* Pass resource value to Update Client.
00541            Used for validating the manifest.
00542         */
00543         switch (resource) {
00544             case M2MDevice::Manufacturer:
00545                 ARM_UC_SetVendorId((const uint8_t*) value, length);
00546                 break;
00547             case M2MDevice::ModelNumber:
00548                 ARM_UC_SetClassId((const uint8_t*) value, length);
00549                 break;
00550             default:
00551                 break;
00552         }
00553 #endif
00554     }
00555 
00556     return retval;
00557 }
00558 
00559 #ifdef MBED_CLOUD_CLIENT_SUPPORT_UPDATE
00560 void ServiceClient::set_update_authorize_handler(void (*handler)(int32_t request))
00561 {
00562     UpdateClient::set_update_authorize_handler(handler);
00563 }
00564 
00565 void ServiceClient::update_authorize(int32_t request)
00566 {
00567     UpdateClient::update_authorize(request);
00568 }
00569 
00570 void ServiceClient::set_update_progress_handler(void (*handler)(uint32_t progress, uint32_t total))
00571 {
00572     UpdateClient::set_update_progress_handler(handler);
00573 }
00574 
00575 void ServiceClient::update_error_callback(int32_t error)
00576 {
00577     _service_callback.error(error, ERROR_UPDATE);
00578 }
00579 #endif