mesh: Check app model settings of pub/sub support
authorInga Stotland <inga.stotland@intel.com>
Tue, 30 Jun 2020 18:56:15 +0000 (11:56 -0700)
committerAbhay Agarwal <ay.agarwal@samsung.com>
Mon, 28 Dec 2020 06:20:04 +0000 (11:50 +0530)
This adds handling of new options dictionary included with
"Models" and "VendorModels" properties on org.bluez.mesh.Element1
interface.

Supported (optional) dictionary entries:
"Publish" - indicates whether the model supports publication mechanism.
                If not present, publication is enabled.
"Subscribe" - indicates whether the model supports subscription mechanism.
                If not present, subscriptions are enabled.

If a config message related to subscription state is received for a model
that does not support subscription mechanism, an error code 0x08,
("Not A Subscribe Model") is sent in response.

If a config message related to publication state is received for a model
that does not support publication mechanism, an error code 0x07
("Invalid Publish Parameters") is sent in response.

Change-Id: I6365ee91c74bf79cd4950e000ef865c543224136
Signed-off-by: anuj.bhumiya <anuj.bhumiya@samsung.com>
mesh/mesh-config-json.c
mesh/mesh-config.h
mesh/model.c
mesh/model.h
mesh/node.c

index 4cb8cae..160f53f 100644 (file)
@@ -1097,6 +1097,16 @@ static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)
                                goto fail;
                }
 
+               if (json_object_object_get_ex(jmodel, "pubEnabled", &jvalue))
+                       mod->pub_enabled = json_object_get_boolean(jvalue);
+               else
+                       mod->pub_enabled = true;
+
+               if (json_object_object_get_ex(jmodel, "subEnabled", &jvalue))
+                       mod->sub_enabled = json_object_get_boolean(jvalue);
+               else
+                       mod->sub_enabled = true;
+
                if (json_object_object_get_ex(jmodel, "publish", &jvalue)) {
                        mod->pub = parse_model_publication(jvalue);
                        if (!mod->pub)
@@ -1563,7 +1573,7 @@ bool mesh_config_write_iv_index(struct mesh_config *cfg, uint32_t idx,
 static void add_model(void *a, void *b)
 {
        struct mesh_config_model *mod = a;
-       json_object *jmodels = b, *jmodel;
+       json_object *jmodels = b, *jmodel, *jval;
 
        jmodel = json_object_new_object();
        if (!jmodel)
@@ -1575,6 +1585,12 @@ static void add_model(void *a, void *b)
        else
                write_uint32_hex(jmodel, "modelId", mod->id);
 
+       jval = json_object_new_boolean(mod->sub_enabled);
+       json_object_object_add(jmodel, "subEnabled", jval);
+
+       jval = json_object_new_boolean(mod->pub_enabled);
+       json_object_object_add(jmodel, "pubEnabled", jval);
+
        json_object_array_add(jmodels, jmodel);
 }
 
@@ -1975,6 +1991,64 @@ bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t addr,
        return save_config(cfg->jnode, cfg->node_dir_path);
 }
 
+bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr,
+                                               uint32_t mod_id, bool vendor,
+                                               bool enable)
+{
+       json_object *jmodel, *jval;
+       int ele_idx;
+
+       if (!cfg)
+               return false;
+
+       ele_idx = get_element_index(cfg->jnode, ele_addr);
+       if (ele_idx < 0)
+               return false;
+
+       jmodel = get_element_model(cfg->jnode, ele_idx, mod_id, vendor);
+       if (!jmodel)
+               return false;
+
+       json_object_object_del(jmodel, "pubDisabled");
+
+       jval = json_object_new_boolean(!enable);
+       json_object_object_add(jmodel, "pubDisabled", jval);
+
+       if (!enable)
+               json_object_object_del(jmodel, "publish");
+
+       return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
+bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr,
+                                               uint32_t mod_id, bool vendor,
+                                               bool enable)
+{
+       json_object *jmodel, *jval;
+       int ele_idx;
+
+       if (!cfg)
+               return false;
+
+       ele_idx = get_element_index(cfg->jnode, ele_addr);
+       if (ele_idx < 0)
+               return false;
+
+       jmodel = get_element_model(cfg->jnode, ele_idx, mod_id, vendor);
+       if (!jmodel)
+               return false;
+
+       json_object_object_del(jmodel, "subEnabled");
+
+       jval = json_object_new_boolean(enable);
+       json_object_object_add(jmodel, "subEnabled", jval);
+
+       if (!enable)
+               json_object_object_del(jmodel, "subscribe");
+
+       return save_config(cfg->jnode, cfg->node_dir_path);
+}
+
 bool mesh_config_write_seq_number(struct mesh_config *cfg, uint32_t seq,
                                                                bool cache)
 {
index 8ff7b63..9f30e69 100644 (file)
@@ -45,6 +45,8 @@ struct mesh_config_model {
        uint16_t *bindings;
        uint32_t id;
        bool vendor;
+       bool sub_enabled;
+       bool pub_enabled;
        uint32_t num_bindings;
        uint32_t num_subs;
 };
@@ -156,6 +158,12 @@ bool mesh_config_model_sub_del(struct mesh_config *cfg, uint16_t ele_addr,
                                                struct mesh_config_sub *sub);
 bool mesh_config_model_sub_del_all(struct mesh_config *cfg, uint16_t ele_addr,
                                                uint32_t mod_id, bool vendor);
+bool mesh_config_model_pub_enable(struct mesh_config *cfg, uint16_t ele_addr,
+                                               uint32_t mod_id, bool vendor,
+                                               bool enable);
+bool mesh_config_model_sub_enable(struct mesh_config *cfg, uint16_t ele_addr,
+                                               uint32_t mod_id, bool vendor,
+                                               bool enable);
 bool mesh_config_app_key_add(struct mesh_config *cfg, uint16_t net_idx,
                                uint16_t app_idx, const uint8_t key[16]);
 bool mesh_config_app_key_update(struct mesh_config *cfg, uint16_t app_idx,
index 5ed95af..afac6ec 100644 (file)
@@ -51,6 +51,8 @@ struct mesh_model {
        struct l_queue *subs;
        struct l_queue *virtuals;
        struct mesh_model_pub *pub;
+       bool sub_enabled;
+       bool pub_enabled;
        uint32_t id;
        uint8_t ele_idx;
 };
@@ -1097,7 +1099,7 @@ int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
        if (!mod)
                return status;
 
-       if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
+       if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub)))
                return MESH_STATUS_INVALID_PUB_PARAM;
 
        if (!appkey_have_key(node_get_net(node), idx))
@@ -1134,9 +1136,11 @@ int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
                /* External model */
                config_update_model_pub_period(node, mod->ele_idx, id,
                                                pub_period_to_ms(period));
-       else
+       else {
                /* Internal model, call registered callbacks */
-               mod->cbs->pub(mod->pub);
+               if (mod->cbs->pub)
+                       mod->cbs->pub(mod->pub);
+       }
 
        return MESH_STATUS_SUCCESS;
 }
@@ -1150,6 +1154,11 @@ struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node, uint16_t addr,
        if (!mod)
                return NULL;
 
+       if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub)))
+               *status = MESH_STATUS_INVALID_PUB_PARAM;
+       else
+               *status = MESH_STATUS_SUCCESS;
+
        return mod->pub;
 }
 
@@ -1171,6 +1180,13 @@ struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id)
        mod->id = id;
        mod->ele_idx = ele_idx;
        mod->virtuals = l_queue_new();
+
+       /*
+        * Unless specifically indicated by an app, subscriptions and
+        * publications are enabled by default
+        */
+       mod->sub_enabled = true;
+       mod->pub_enabled = true;
        return mod;
 }
 
@@ -1318,6 +1334,9 @@ int mesh_model_sub_get(struct mesh_node *node, uint16_t addr, uint32_t id,
        if (!mod)
                return status;
 
+       if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
+               return MESH_STATUS_NOT_SUB_MOD;
+
        entry = l_queue_get_entries(mod->subs);
        *size = 0;
        n = 0;
@@ -1358,6 +1377,9 @@ int mesh_model_sub_add(struct mesh_node *node, uint16_t addr, uint32_t id,
        if (!mod)
                return status;
 
+       if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
+               return MESH_STATUS_NOT_SUB_MOD;
+
        status = add_sub(node_get_net(node), mod, group, is_virt, dst);
 
        if (status != MESH_STATUS_SUCCESS)
@@ -1381,6 +1403,9 @@ int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
        if (!mod)
                return status;
 
+       if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
+               return MESH_STATUS_NOT_SUB_MOD;
+
        subs = mod->subs;
        virtuals = mod->virtuals;
        mod->subs = l_queue_new();
@@ -1430,6 +1455,9 @@ int mesh_model_sub_del(struct mesh_node *node, uint16_t addr, uint32_t id,
        if (!mod)
                return status;
 
+       if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
+               return MESH_STATUS_NOT_SUB_MOD;
+
        if (is_virt) {
                struct mesh_virtual *virt;
 
@@ -1448,27 +1476,22 @@ int mesh_model_sub_del(struct mesh_node *node, uint16_t addr, uint32_t id,
 
        *dst = grp;
 
-       if (l_queue_remove(mod->subs, L_UINT_TO_PTR(grp)))
+       if (l_queue_remove(mod->subs, L_UINT_TO_PTR(grp))) {
                mesh_net_dst_unreg(node_get_net(node), grp);
 
-       if (!mod->cbs)
-               /* External models */
-               config_update_model_subscriptions(node, mod);
+               if (!mod->cbs)
+                       /* External models */
+                       config_update_model_subscriptions(node, mod);
+       }
 
        return MESH_STATUS_SUCCESS;
 }
 
-int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
+static void remove_subs(struct mesh_node *node, struct mesh_model *mod)
 {
-       int status;
-       struct mesh_model *mod;
        const struct l_queue_entry *entry;
        struct mesh_net *net = node_get_net(node);
 
-       mod = find_model(node, addr, id, &status);
-       if (!mod)
-               return status;
-
        entry = l_queue_get_entries(mod->subs);
 
        for (; entry; entry = entry->next)
@@ -1476,6 +1499,21 @@ int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
 
        l_queue_clear(mod->subs, NULL);
        l_queue_clear(mod->virtuals, unref_virt);
+}
+
+int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
+{
+       int status;
+       struct mesh_model *mod;
+
+       mod = find_model(node, addr, id, &status);
+       if (!mod)
+               return status;
+
+       if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
+               return MESH_STATUS_NOT_SUB_MOD;
+
+       remove_subs(node, mod);
 
        if (!mod->cbs)
                /* External models */
@@ -1677,6 +1715,38 @@ void model_build_config(void *model, void *msg_builder)
        l_dbus_message_builder_leave_struct(builder);
 }
 
+void mesh_model_enable_pub(struct mesh_model *mod, bool enable)
+{
+       mod->pub_enabled = enable;
+
+       if (!mod->pub_enabled && mod->pub) {
+               if (mod->pub->virt)
+                       unref_virt(mod->pub->virt);
+
+               l_free(mod->pub);
+               mod->pub = NULL;
+       }
+}
+
+bool mesh_model_is_pub_enabled(struct mesh_model *mod)
+{
+       return mod->pub_enabled;
+}
+
+void mesh_model_enable_sub(struct mesh_node *node, struct mesh_model *mod,
+                                                               bool enable)
+{
+       mod->sub_enabled = enable;
+
+       if (!mod->sub_enabled)
+               remove_subs(node, mod);
+}
+
+bool mesh_model_is_sub_enabled(struct mesh_model *mod)
+{
+       return mod->sub_enabled;
+}
+
 void mesh_model_init(void)
 {
        mesh_virtuals = l_queue_new();
index f8e0f9d..f717fb0 100644 (file)
@@ -112,5 +112,11 @@ bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size, uint32_t *opcode,
                                                                uint16_t *n);
 void model_build_config(void *model, void *msg_builder);
 
+void mesh_model_enable_pub(struct mesh_model *mod, bool enable);
+bool mesh_model_is_pub_enabled(struct mesh_model *mod);
+void mesh_model_enable_sub(struct mesh_node *node, struct mesh_model *mod,
+                                                               bool enable);
+bool mesh_model_is_sub_enabled(struct mesh_model *mod);
+
 void mesh_model_init(void);
 void mesh_model_cleanup(void);
index adff6f0..3f736d6 100644 (file)
@@ -402,6 +402,12 @@ static bool add_models_from_storage(struct mesh_node *node,
                if (!mod)
                        return false;
 
+               if (!db_mod->pub_enabled)
+                       mesh_model_enable_pub(mod, false);
+
+               if (!db_mod->sub_enabled)
+                       mesh_model_enable_sub(node, mod, false);
+
                l_queue_insert(ele->models, mod, compare_model_id, NULL);
        }
 
@@ -1057,65 +1063,99 @@ static void app_disc_cb(struct l_dbus *bus, void *user_data)
        free_node_dbus_resources(node);
 }
 
-static bool get_sig_models_from_properties(struct node_element *ele,
+static bool get_model_options(struct mesh_node *node, struct mesh_model *mod,
+                                       struct l_dbus_message_iter *opts)
+{
+       const char *key;
+       struct l_dbus_message_iter var;
+       bool opt;
+
+       while (l_dbus_message_iter_next_entry(opts, &key, &var)) {
+
+               if (!strcmp(key, "Publish")) {
+                       if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
+                               return false;
+                       mesh_model_enable_pub(mod, opt);
+               } else if (!strcmp(key, "Subscribe")) {
+                       if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
+                               return false;
+                       mesh_model_enable_sub(node, mod, opt);
+               } else
+                       return false;
+       }
+
+       return true;
+}
+
+static bool generate_model(struct mesh_node *node, struct node_element *ele,
+                               uint32_t id, struct l_dbus_message_iter *opts)
+{
+       struct mesh_model *mod;
+
+       /* Disallow duplicates */
+       if (l_queue_find(ele->models, match_model_id,
+                        L_UINT_TO_PTR(id)))
+               return false;
+
+       mod = mesh_model_new(ele->idx, id);
+
+       if (!get_model_options(node, mod, opts)) {
+               l_free(mod);
+               return false;
+       }
+
+       l_queue_insert(ele->models, mod, compare_model_id, NULL);
+
+       return true;
+}
+
+static bool get_sig_models_from_properties(struct mesh_node *node,
+                                       struct node_element *ele,
                                        struct l_dbus_message_iter *property)
 {
-       struct l_dbus_message_iter ids;
-       uint16_t mod_id;
+       struct l_dbus_message_iter mods, var;
+       uint16_t m_id;
 
        if (!ele->models)
                ele->models = l_queue_new();
 
-       if (!l_dbus_message_iter_get_variant(property, "aq", &ids))
+       if (!l_dbus_message_iter_get_variant(property, "a(qa{sv})", &mods))
                return false;
 
        /* Bluetooth SIG defined models */
-       while (l_dbus_message_iter_next_entry(&ids, &mod_id)) {
-               struct mesh_model *mod;
-               uint32_t id = mod_id | VENDOR_ID_MASK;
+       while (l_dbus_message_iter_next_entry(&mods, &m_id, &var)) {
+               uint32_t id = m_id | VENDOR_ID_MASK;
 
                /* Allow Config Server Model only on the primary element */
                if (ele->idx != PRIMARY_ELE_IDX && id == CONFIG_SRV_MODEL)
                        return false;
 
-               /* Disallow duplicates */
-               if (l_queue_find(ele->models, match_model_id,
-                                               L_UINT_TO_PTR(id)))
+               if (!generate_model(node, ele, id, &var))
                        return false;
-
-               mod = mesh_model_new(ele->idx, id);
-
-               l_queue_insert(ele->models, mod, compare_model_id, NULL);
        }
 
        return true;
 }
 
-static bool get_vendor_models_from_properties(struct node_element *ele,
+static bool get_vendor_models_from_properties(struct mesh_node *node,
+                                       struct node_element *ele,
                                        struct l_dbus_message_iter *property)
 {
-       struct l_dbus_message_iter ids;
-       uint16_t mod_id, vendor_id;
+       struct l_dbus_message_iter mods, var;
+       uint16_t m_id, v_id;
 
        if (!ele->models)
                ele->models = l_queue_new();
 
-       if (!l_dbus_message_iter_get_variant(property, "a(qq)", &ids))
+       if (!l_dbus_message_iter_get_variant(property, "a(qqa{sv})", &mods))
                return false;
 
        /* Vendor defined models */
-       while (l_dbus_message_iter_next_entry(&ids, &vendor_id, &mod_id)) {
-               struct mesh_model *mod;
-               uint32_t id = mod_id | (vendor_id << 16);
+       while (l_dbus_message_iter_next_entry(&mods, &v_id, &m_id, &var)) {
+               uint32_t id = m_id | (v_id << 16);
 
-               /* Disallow duplicates */
-               if (l_queue_find(ele->models, match_model_id,
-                                                       L_UINT_TO_PTR(id)))
+               if (!generate_model(node, ele, id, &var))
                        return false;
-
-               mod = mesh_model_new(ele->idx, id);
-
-               l_queue_insert(ele->models, mod, compare_model_id, NULL);
        }
 
        return true;
@@ -1146,14 +1186,19 @@ static bool get_element_properties(struct mesh_node *node, const char *path,
 
                } else if (!strcmp(key, "Models")) {
 
-                       if (mods || !get_sig_models_from_properties(ele, &var))
+                       if (mods)
+                               goto fail;
+
+                       if (!get_sig_models_from_properties(node, ele, &var))
                                goto fail;
 
                        mods = true;
                } else if (!strcmp(key, "VendorModels")) {
 
-                       if (vendor_mods ||
-                               !get_vendor_models_from_properties(ele, &var))
+                       if (vendor_mods)
+                               goto fail;
+
+                       if (!get_vendor_models_from_properties(node, ele, &var))
                                goto fail;
 
                        vendor_mods = true;
@@ -1241,7 +1286,8 @@ static void convert_node_to_storage(struct mesh_node *node,
                        db_mod->id = mod_id;
                        db_mod->vendor = ((mod_id & VENDOR_ID_MASK)
                                                        != VENDOR_ID_MASK);
-
+                       db_mod->pub_enabled = mesh_model_is_pub_enabled(mod);
+                       db_mod->sub_enabled = mesh_model_is_sub_enabled(mod);
                        l_queue_push_tail(db_ele->models, db_mod);
                }
                l_queue_push_tail(db_node->elements, db_ele);
@@ -1397,6 +1443,63 @@ static void update_composition(struct mesh_node *node, struct mesh_node *attach)
        attach->comp = node->comp;
 }
 
+static void update_model_options(struct mesh_node *node,
+                                               struct mesh_node *attach)
+{
+       uint32_t len, i;
+       struct node_element *ele, *ele_attach;
+
+       len = l_queue_length(node->elements);
+
+       for (i = 0; i < len; i++) {
+               const struct l_queue_entry *entry;
+
+               ele = l_queue_find(node->elements, match_element_idx,
+                                                       L_UINT_TO_PTR(i));
+               ele_attach = l_queue_find(attach->elements, match_element_idx,
+                                                       L_UINT_TO_PTR(i));
+               if (!ele || !ele_attach)
+                       continue;
+
+               entry = l_queue_get_entries(ele->models);
+
+               for (; entry; entry = entry->next) {
+                       struct mesh_model *mod, *updated_mod = entry->data;
+                       uint32_t id = mesh_model_get_model_id(updated_mod);
+                       bool opt, updated_opt;
+                       bool vendor = id < VENDOR_ID_MASK;
+
+                       mod = l_queue_find(ele_attach->models, match_model_id,
+                                                       L_UINT_TO_PTR(id));
+                       if (!mod)
+                               continue;
+
+                       if (!vendor)
+                               id &= ~VENDOR_ID_MASK;
+
+                       opt = mesh_model_is_pub_enabled(mod);
+                       updated_opt = mesh_model_is_pub_enabled(updated_mod);
+
+                       if (updated_opt != opt) {
+                               mesh_model_enable_pub(mod, updated_opt);
+                               mesh_config_model_pub_enable(attach->cfg,
+                                                       attach->primary + i, id,
+                                                       vendor, updated_opt);
+                       }
+
+                       opt = mesh_model_is_sub_enabled(mod);
+                       updated_opt = mesh_model_is_sub_enabled(updated_mod);
+
+                       if (updated_opt != opt) {
+                               mesh_model_enable_sub(node, mod, updated_opt);
+                               mesh_config_model_sub_enable(attach->cfg,
+                                                       attach->primary + i, id,
+                                                       vendor, updated_opt);
+                       }
+               }
+       }
+}
+
 static bool check_req_node(struct managed_obj_request *req)
 {
        uint8_t node_comp[MAX_MSG_LEN - 2];
@@ -1468,6 +1571,7 @@ static bool attach_req_node(struct mesh_node *attach, struct mesh_node *node)
        node->owner = NULL;
 
        update_composition(node, attach);
+       update_model_options(node, attach);
 
        node_remove(node);