From d7089cf5537db0196142eb0c2538f47209dc77db Mon Sep 17 00:00:00 2001 From: Daniel Adam Date: Fri, 12 Apr 2024 12:26:46 +0200 Subject: [PATCH] cloud: add oc_cloud_manager_stop_v1 function The function enables the caller to stop the cloud manager, but without clearing the cloud configuration. --- api/cloud/oc_cloud.c | 50 ++++++++++++++------ api/cloud/oc_cloud_context.c | 24 ++++++++++ api/cloud/oc_cloud_context_internal.h | 14 ++++++ api/cloud/unittest/cloud_manager_test.cpp | 57 +++++++++++++++++++++++ api/cloud/unittest/cloud_test.cpp | 37 +++++++++++++++ include/oc_cloud.h | 19 +++++++- swig/swig_interfaces/oc_cloud.i | 21 +++++++++ 7 files changed, 206 insertions(+), 16 deletions(-) diff --git a/api/cloud/oc_cloud.c b/api/cloud/oc_cloud.c index 04345e8ac..7db7f44c0 100644 --- a/api/cloud/oc_cloud.c +++ b/api/cloud/oc_cloud.c @@ -89,10 +89,7 @@ start_manager(void *user_data) static void cloud_manager_restart(oc_cloud_context_t *ctx) { - if (!ctx->cloud_manager) { - OC_CLOUD_ERR("cloud manager is not running"); - return; - } + assert(ctx->cloud_manager); cloud_manager_stop(ctx); oc_cloud_deregister_stop(ctx); oc_reset_delayed_callback(ctx, start_manager, 0); @@ -102,7 +99,9 @@ static oc_event_callback_retval_t restart_manager(void *user_data) { oc_cloud_context_t *ctx = (oc_cloud_context_t *)user_data; - cloud_manager_restart(ctx); + if (ctx->cloud_manager) { + cloud_manager_restart(ctx); + } return OC_EVENT_DONE; } @@ -449,8 +448,11 @@ oc_cloud_manager_start(oc_cloud_context_t *ctx, oc_cloud_cb_t cb, void *data) cloud_manager_start(ctx); ctx->cloud_manager = true; - oc_cloud_registration_context_init(&ctx->registration_ctx, - &ctx->store.ci_servers); + // in case we stopped the manager, but kept the configuration by calling + // oc_cloud_manager_stop_v1(ctx, true), we also need to keep the registration + // context + oc_cloud_registration_context_init_if_not_set(&ctx->registration_ctx, + &ctx->store.ci_servers); #ifdef OC_SESSION_EVENTS oc_remove_session_event_callback_v1(cloud_ep_session_event_handler, ctx, false); @@ -464,13 +466,16 @@ oc_cloud_manager_start(oc_cloud_context_t *ctx, oc_cloud_cb_t cb, void *data) return 0; } -int -oc_cloud_manager_stop(oc_cloud_context_t *ctx) +static bool +cloud_has_configuration(const oc_cloud_context_t *ctx) { - if (ctx == NULL) { - return -1; - } + return oc_cloud_get_server_uri(ctx) != NULL && + cloud_context_has_access_token(ctx); +} +void +oc_cloud_manager_stop_v1(oc_cloud_context_t *ctx, bool resetConfiguration) +{ #ifdef OC_SESSION_EVENTS oc_remove_session_event_callback_v1(cloud_ep_session_event_handler, ctx, false); @@ -484,11 +489,28 @@ oc_cloud_manager_stop(oc_cloud_context_t *ctx) oc_remove_delayed_callback(ctx, start_manager); cloud_rd_reset_context(ctx); cloud_manager_stop(ctx); - oc_cloud_store_reinitialize(&ctx->store); oc_cloud_reset_endpoint(ctx); - oc_cloud_registration_context_deinit(&ctx->registration_ctx); + if (resetConfiguration || !cloud_has_configuration(ctx)) { + oc_cloud_store_reinitialize(&ctx->store); + oc_cloud_registration_context_deinit(&ctx->registration_ctx); + } else { + ctx->store.status &= ~OC_CLOUD_LOGGED_IN; + ctx->store.cps = (ctx->store.status & OC_CLOUD_REGISTERED) != 0 + ? OC_CPS_REGISTERED + : OC_CPS_READYTOREGISTER; + oc_cloud_registration_context_reset(&ctx->registration_ctx); + oc_cloud_store_dump_async(&ctx->store); + } ctx->cloud_manager = false; +} +int +oc_cloud_manager_stop(oc_cloud_context_t *ctx) +{ + if (ctx == NULL) { + return -1; + } + oc_cloud_manager_stop_v1(ctx, true); return 0; } diff --git a/api/cloud/oc_cloud_context.c b/api/cloud/oc_cloud_context.c index 5557c076d..2afe42372 100644 --- a/api/cloud/oc_cloud_context.c +++ b/api/cloud/oc_cloud_context.c @@ -300,6 +300,24 @@ oc_cloud_get_identity_cert_chain(const oc_cloud_context_t *ctx) return ctx->selected_identity_cred_id; } +bool +oc_cloud_registration_context_is_initialized( + const oc_cloud_registration_context_t *regctx) +{ + return oc_string(regctx->initial_server) != NULL; +} + +void +oc_cloud_registration_context_init_if_not_set( + oc_cloud_registration_context_t *regctx, + const oc_endpoint_addresses_t *servers) +{ + if (oc_cloud_registration_context_is_initialized(regctx)) { + return; + } + oc_cloud_registration_context_init(regctx, servers); +} + void oc_cloud_registration_context_init(oc_cloud_registration_context_t *regctx, const oc_endpoint_addresses_t *servers) @@ -325,6 +343,12 @@ oc_cloud_registration_context_deinit(oc_cloud_registration_context_t *regctx) regctx->server_changed = false; } +void +oc_cloud_registration_context_reset(oc_cloud_registration_context_t *regctx) +{ + regctx->server_changed = false; +} + void oc_cloud_set_keepalive( oc_cloud_context_t *ctx, diff --git a/api/cloud/oc_cloud_context_internal.h b/api/cloud/oc_cloud_context_internal.h index 0de854fbb..18963cdac 100644 --- a/api/cloud/oc_cloud_context_internal.h +++ b/api/cloud/oc_cloud_context_internal.h @@ -184,6 +184,20 @@ void oc_cloud_registration_context_init(oc_cloud_registration_context_t *regctx, void oc_cloud_registration_context_deinit( oc_cloud_registration_context_t *regctx) OC_NONNULL(); +/** @brief Check if the registration context is initialized */ +bool oc_cloud_registration_context_is_initialized( + const oc_cloud_registration_context_t *regctx) OC_NONNULL(); + +/** @brief Initialize the registration context if it hasn't been previously + * initialized */ +void oc_cloud_registration_context_init_if_not_set( + oc_cloud_registration_context_t *regctx, + const oc_endpoint_addresses_t *servers) OC_NONNULL(); + +/** @brief Reset temporary data */ +void oc_cloud_registration_context_reset( + oc_cloud_registration_context_t *regctx) OC_NONNULL(); + #ifdef __cplusplus } #endif diff --git a/api/cloud/unittest/cloud_manager_test.cpp b/api/cloud/unittest/cloud_manager_test.cpp index f2c3f763b..51eba9643 100644 --- a/api/cloud/unittest/cloud_manager_test.cpp +++ b/api/cloud/unittest/cloud_manager_test.cpp @@ -120,6 +120,63 @@ TEST_F(TestCloudManager, oc_cloud_manager_stop_fail) EXPECT_EQ(-1, oc_cloud_manager_stop(nullptr)); } +TEST_F(TestCloudManager, oc_cloud_manager_stop_v1) +{ + ASSERT_EQ(0, oc_cloud_manager_start(&m_context, nullptr, nullptr)); + ASSERT_TRUE(oc_cloud_manager_is_started(&m_context)); + + // if cloud is registered and logged in, then the login flag should be + // removed, so on restart will attempt to login again + m_context.store.status = + OC_CLOUD_INITIALIZED | OC_CLOUD_REGISTERED | OC_CLOUD_LOGGED_IN; + m_context.store.cps = OC_CPS_REGISTERED; + oc_cloud_manager_stop_v1(&m_context, false); + EXPECT_FALSE(oc_cloud_manager_is_started(&m_context)); + EXPECT_EQ(OC_CLOUD_INITIALIZED | OC_CLOUD_REGISTERED, m_context.store.status); + EXPECT_EQ(OC_CPS_REGISTERED, m_context.store.cps); + + // if cloud is stopped during registration, then it should be in the ready to + // register state + ASSERT_EQ(0, oc_cloud_manager_start(&m_context, nullptr, nullptr)); + ASSERT_TRUE(oc_cloud_manager_is_started(&m_context)); + m_context.store.status = OC_CLOUD_INITIALIZED; + m_context.store.cps = OC_CPS_REGISTERING; + oc_cloud_manager_stop_v1(&m_context, false); + EXPECT_FALSE(oc_cloud_manager_is_started(&m_context)); + EXPECT_EQ(OC_CLOUD_INITIALIZED, m_context.store.status); + EXPECT_EQ(OC_CPS_READYTOREGISTER, m_context.store.cps); + + // if the cloud is not configured, then the configuration should be reset + ASSERT_EQ(0, oc_cloud_manager_start(&m_context, nullptr, nullptr)); + ASSERT_TRUE(oc_cloud_manager_is_started(&m_context)); + m_context.store.status = + OC_CLOUD_INITIALIZED | OC_CLOUD_REGISTERED | OC_CLOUD_LOGGED_IN; + m_context.store.cps = OC_CPS_REGISTERED; + // this sets the default cloud server, but access token will not be set + ASSERT_EQ(0, oc_cloud_provision_conf_resource(&m_context, "", "", "", "")); + ASSERT_EQ(nullptr, oc_string(*oc_cloud_get_access_token(&m_context))); + oc_cloud_manager_stop_v1(&m_context, false); + EXPECT_FALSE(oc_cloud_manager_is_started(&m_context)); + EXPECT_EQ(OC_CLOUD_INITIALIZED, m_context.store.status); + EXPECT_EQ(OC_CPS_UNINITIALIZED, m_context.store.cps); + + ASSERT_EQ(0, oc_cloud_manager_start(&m_context, nullptr, nullptr)); + ASSERT_TRUE(oc_cloud_manager_is_started(&m_context)); + m_context.store.status = + OC_CLOUD_INITIALIZED | OC_CLOUD_REGISTERED | OC_CLOUD_LOGGED_IN; + m_context.store.cps = OC_CPS_REGISTERED; + // access token set + ASSERT_EQ(0, oc_cloud_provision_conf_resource(&m_context, "", "access_token", + "", "")); + ASSERT_NE(nullptr, oc_string(*oc_cloud_get_access_token(&m_context))); + // but no cloud server set + oc_endpoint_addresses_clear(&m_context.store.ci_servers); + oc_cloud_manager_stop_v1(&m_context, false); + EXPECT_FALSE(oc_cloud_manager_is_started(&m_context)); + EXPECT_EQ(OC_CLOUD_INITIALIZED, m_context.store.status); + EXPECT_EQ(OC_CPS_UNINITIALIZED, m_context.store.cps); +} + TEST_F(TestCloudManager, oc_cloud_manager_is_started) { EXPECT_FALSE(oc_cloud_manager_is_started(&m_context)); diff --git a/api/cloud/unittest/cloud_test.cpp b/api/cloud/unittest/cloud_test.cpp index 4500317b0..b91c6b209 100644 --- a/api/cloud/unittest/cloud_test.cpp +++ b/api/cloud/unittest/cloud_test.cpp @@ -363,6 +363,43 @@ TEST_F(TestCloudWithServer, oc_cloud_provision_conf_resource) EXPECT_EQ(OC_CLOUD_INITIALIZED, ctx->store.status); } +TEST_F(TestCloudWithServer, oc_cloud_provision_conf_resource_with_started_cloud) +{ + oc_cloud_context_t *ctx = oc_cloud_get_context(kDeviceID); + ASSERT_NE(nullptr, ctx); + ASSERT_FALSE( + oc_cloud_registration_context_is_initialized(&ctx->registration_ctx)); + ctx->store.status = OC_CLOUD_INITIALIZED; + ASSERT_EQ(0, oc_cloud_manager_start(ctx, nullptr, nullptr)); + ASSERT_TRUE( + oc_cloud_registration_context_is_initialized(&ctx->registration_ctx)); + auto defaulServer = OC_STRING_LOCAL(OCF_COAPCLOUDCONF_DEFAULT_CIS); + EXPECT_TRUE( + oc_string_is_equal(&ctx->registration_ctx.initial_server, &defaulServer)); + + std::string_view ci_server = "ci_server"; + std::string_view access_token = "access_token"; + std::string_view sid = "12345678-1234-5678-1234-567812345678"; + oc_uuid_t sid_uuid; + oc_str_to_uuid(sid.data(), &sid_uuid); + std::string_view auth_provider = "auth_provider"; + ASSERT_EQ(0, oc_cloud_provision_conf_resource(ctx, ci_server.data(), + access_token.data(), sid.data(), + auth_provider.data())); + EXPECT_STREQ(access_token.data(), oc_string(*oc_cloud_get_access_token(ctx))); + EXPECT_STREQ(auth_provider.data(), + oc_string(*oc_cloud_get_authorization_provider_name(ctx))); + const auto *ctx_cis = oc_cloud_get_server_uri(ctx); + ASSERT_NE(nullptr, ctx_cis); + EXPECT_STREQ(ci_server.data(), oc_string(*ctx_cis)); + EXPECT_TRUE(oc_uuid_is_equal(sid_uuid, *oc_cloud_get_server_id(ctx))); + EXPECT_EQ(OC_CLOUD_INITIALIZED, ctx->store.status); + EXPECT_TRUE(oc_cloud_manager_is_started(ctx)); + EXPECT_TRUE(oc_string_view_is_equal( + oc_string_view2(&ctx->registration_ctx.initial_server), + oc_string_view(ci_server.data(), ci_server.length()))); +} + TEST_F(TestCloudWithServer, oc_cloud_action_to_str) { std::string v; diff --git a/include/oc_cloud.h b/include/oc_cloud.h index 63f9a5d71..766d3f979 100644 --- a/include/oc_cloud.h +++ b/include/oc_cloud.h @@ -305,7 +305,7 @@ oc_cps_t oc_cloud_get_provisioning_status(const oc_cloud_context_t *ctx) /** * @brief Start cloud registration process. * - * @param ctx cloud context (cannot be NULL) + * @param ctx cloud context * @param cb callback function invoked on status change * @param data user data provided to the status change function * @return int 0 on success @@ -319,13 +319,28 @@ int oc_cloud_manager_start(oc_cloud_context_t *ctx, oc_cloud_cb_t cb, * @brief Stop cloud registration process, remove related pending delayed * callbacks and clean-up data. * - * @param ctx cloud context (cannot be NULL) + * @param ctx cloud context * @return int 0 on success * @return int -1 on error */ OC_API int oc_cloud_manager_stop(oc_cloud_context_t *ctx); +/** + * @brief Stop cloud registration process, remove related pending delayed + * callbacks and clean-up data. + * + * @param ctx cloud context (cannot be NULL) + * @param resetConfiguration if true, reset cloud configuration to default + * (cloud must be reconfigured by oc_cloud_provision_conf_resource or by + * updating the cloud resource); if false the previous cloud configuration is + * kept, but the cloud is reset to OC_CPS_REGISTERED state if it was registered + * or to OC_CPS_READYTOREGISTER otherwise + */ +OC_API +void oc_cloud_manager_stop_v1(oc_cloud_context_t *ctx, bool resetConfiguration) + OC_NONNULL(); + /** * @brief Restart cloud registration process with the current configuration. * diff --git a/swig/swig_interfaces/oc_cloud.i b/swig/swig_interfaces/oc_cloud.i index 30241111f..b991d180a 100644 --- a/swig/swig_interfaces/oc_cloud.i +++ b/swig/swig_interfaces/oc_cloud.i @@ -377,6 +377,23 @@ int jni_cloud_manager_stop(oc_cloud_context_t *ctx) } %} +%ignore oc_cloud_manager_stop_v1; +%rename (managerStopV1) jni_cloud_manager_stop_v1; +%inline %{ +void jni_cloud_manager_stop_v1(oc_cloud_context_t *ctx, bool resetConfiguration) +{ +#ifdef OC_CLOUD + oc_cloud_manager_stop_v1(ctx, resetConfiguration); + jni_callback_data *item = jni_list_get_item_by_callback_valid(OC_CALLBACK_VALID_TILL_CLOUD_MANAGER_STOP); + jni_list_remove(item); +#else /* OC_CLOUD*/ + OC_DBG("JNI: %s - Must build with OC_CLOUD defined to use this function.\n", __func__); + (void)ctx; + (void)resetConfiguration; +#endif /* !OC_CLOUD */ +} +%} + %ignore oc_cloud_manager_restart; %rename (managerRestart) jni_cloud_manager_restart; %inline %{ @@ -712,10 +729,14 @@ void jni_cloud_context_clear(oc_cloud_context_t *ctx, bool dump_async) %ignore cloud_context_has_permanent_access_token; %ignore cloud_context_clear_access_token; %ignore cloud_context_has_refresh_token; +%ignore cloud_context_on_server_change; %ignore oc_cloud_registration_context_t; %ignore oc_cloud_registration_context_init; %ignore oc_cloud_registration_context_deinit; +%ignore oc_cloud_registration_context_is_initialized; +%ignore oc_cloud_registration_context_init_if_not_set; +%ignore oc_cloud_registration_context_reset; %ignore oc_cloud_context_t::registration_ctx; %include "api/cloud/oc_cloud_context_internal.h"