Re: [BlueZ PATCH v1 6/7] adv_monitor: Handle D-Bus proxy event of an ADV monitor

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Miao,

On Tue, Aug 18, 2020 at 3:31 PM Miao-chen Chou <mcchou@xxxxxxxxxxxx> wrote:
>
> This adds two handlers, one for adding and one for removing, of D-Bus proxy
> events. The handler of property changes is set to NULL as intended,
> since for simplicity no further changes on monitor's properties would
> affect the ongoing monitoring.
>
> The following test steps were performed with bluetoothctl.
> - After registering the root path, expose two monitors and verify that
> the proxy added event are received.
> - Have two monitors added, unexpose the monitors, and verify that the
> proxy removed events are received for those two monitors.
> - Have two monitors added, unregister the monitors and verify that the
> proxy removed events are received for those two monitors.
>
> Reviewed-by: Yun-Hao Chung <howardchung@xxxxxxxxxx>
> Reviewed-by: Manish Mandlik <mmandlik@xxxxxxxxxxxx>
> ---
>
>  src/adv_monitor.c | 492 ++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 479 insertions(+), 13 deletions(-)
>
> diff --git a/src/adv_monitor.c b/src/adv_monitor.c
> index b5ea5ee99..23fbc2b45 100644
> --- a/src/adv_monitor.c
> +++ b/src/adv_monitor.c
> @@ -37,14 +37,23 @@
>  #include "dbus-common.h"
>  #include "log.h"
>  #include "src/error.h"
> +#include "src/shared/ad.h"
>  #include "src/shared/mgmt.h"
>  #include "src/shared/queue.h"
>  #include "src/shared/util.h"
>
>  #include "adv_monitor.h"
>
> +#define ADV_MONITOR_INTERFACE          "org.bluez.AdvertisementMonitor1"
>  #define ADV_MONITOR_MGR_INTERFACE      "org.bluez.AdvertisementMonitorManager1"
>
> +#define ADV_MONITOR_UNSET_RSSI         127     /* dBm */
> +#define ADV_MONITOR_MAX_RSSI           20      /* dBm */
> +#define ADV_MONITOR_MIN_RSSI           -127    /* dBm */
> +#define ADV_MONITOR_UNSET_TIMER                0       /* second */
> +#define ADV_MONITOR_MIN_TIMER          1       /* second */
> +#define ADV_MONITOR_MAX_TIMER          300     /* second */
> +
>  struct btd_adv_monitor_manager {
>         struct btd_adapter *adapter;
>         struct mgmt *mgmt;
> @@ -66,6 +75,43 @@ struct adv_monitor_app {
>
>         DBusMessage *reg;
>         GDBusClient *client;
> +
> +       struct queue *monitors;
> +};
> +
> +enum monitor_type {
> +       MONITOR_TYPE_NONE,
> +       MONITOR_TYPE_OR_PATTERNS,
> +};
> +
> +enum monitor_state {
> +       MONITOR_STATE_NEW,      /* New but not yet init'ed with actual values */
> +       MONITOR_STATE_FAILED,   /* Failed to be init'ed */
> +       MONITOR_STATE_INITED,   /* Init'ed but not yet sent to kernel */
> +       MONITOR_STATE_HONORED,  /* Accepted by kernel */
> +};
> +
> +struct pattern {
> +       uint8_t ad_type;
> +       uint8_t offset;
> +       uint8_t length;
> +       uint8_t value[BT_AD_MAX_DATA_LEN];
> +};
> +
> +struct adv_monitor {
> +       struct adv_monitor_app *app;
> +       GDBusProxy *proxy;
> +       char *path;
> +
> +       enum monitor_state state;       /* MONITOR_STATE_* */
> +
> +       int8_t high_rssi;               /* high RSSI threshold */
> +       uint16_t high_rssi_timeout;     /* high RSSI threshold timeout */
> +       int8_t low_rssi;                /* low RSSI threshold */
> +       uint16_t low_rssi_timeout;      /* low RSSI threshold timeout */
> +
> +       enum monitor_type type;         /* MONITOR_TYPE_* */
> +       struct queue *patterns;
>  };
>
>  struct app_match_data {
> @@ -73,6 +119,14 @@ struct app_match_data {
>         const char *path;
>  };
>
> +const struct adv_monitor_type {
> +       enum monitor_type type;
> +       const char *name;
> +} supported_types[] = {
> +       { MONITOR_TYPE_OR_PATTERNS, "or_patterns" },
> +       { },
> +};
> +
>  /* Replies to an app's D-Bus message and unref it */
>  static void app_reply_msg(struct adv_monitor_app *app, DBusMessage *reply)
>  {
> @@ -84,6 +138,52 @@ static void app_reply_msg(struct adv_monitor_app *app, DBusMessage *reply)
>         app->reg = NULL;
>  }
>
> +/* Frees a pattern */
> +static void pattern_free(void *data)
> +{
> +       struct pattern *pattern = data;
> +
> +       if (!pattern)
> +               return;
> +
> +       g_free(pattern);
> +}
> +
> +/* Frees a monitor object */
> +static void monitor_free(void *data)
> +{
> +       struct adv_monitor *monitor = data;
> +
> +       if (!monitor)
> +               return;
> +
> +       monitor->app = NULL;
> +       g_dbus_proxy_unref(monitor->proxy);
> +       monitor->proxy = NULL;
> +       g_free(monitor->path);
> +       monitor->path = NULL;
> +
> +       queue_destroy(monitor->patterns, pattern_free);
> +       monitor->patterns = NULL;
> +
> +       g_free(monitor);
> +}
> +
> +/* Calls Release() method of the remote Adv Monitor */
> +static void monitor_release(void *data, void *user_data)
> +{
> +       struct adv_monitor *monitor = data;
> +
> +       if (!monitor)
> +               return;
> +
> +       DBG("Calling Release() on Adv Monitor of owner %s at path %s",
> +               monitor->app->owner, monitor->path);
> +
> +       g_dbus_proxy_method_call(monitor->proxy, "Release", NULL, NULL, NULL,
> +                                       NULL);
> +}
> +
>  /* Destroys an app object along with related D-Bus handlers */
>  static void app_destroy(void *data)
>  {
> @@ -94,6 +194,9 @@ static void app_destroy(void *data)
>
>         DBG("Destroy Adv Monitor app %s at path %s", app->owner, app->path);
>
> +       queue_foreach(app->monitors, monitor_release, NULL);
> +       queue_destroy(app->monitors, monitor_free);
> +
>         if (app->reg) {
>                 app_reply_msg(app, btd_error_failed(app->reg,
>                                                 "Adv Monitor app destroyed"));
> @@ -139,6 +242,372 @@ static void app_ready_cb(GDBusClient *client, void *user_data)
>         app_reply_msg(app, dbus_message_new_method_return(app->reg));
>  }
>
> +/* Allocates an Adv Monitor */
> +static struct adv_monitor *monitor_new(struct adv_monitor_app *app,
> +                                               GDBusProxy *proxy)
> +{
> +       struct adv_monitor *monitor;
> +
> +       if (!app || !proxy)
> +               return NULL;
> +
> +       monitor = g_new0(struct adv_monitor, 1);

new0

> +       if (!monitor)
> +               return NULL;
> +
> +       monitor->app = app;
> +       monitor->proxy = g_dbus_proxy_ref(proxy);
> +       monitor->path = g_strdup(g_dbus_proxy_get_path(proxy));
> +
> +       monitor->state = MONITOR_STATE_NEW;
> +
> +       monitor->high_rssi = ADV_MONITOR_UNSET_RSSI;
> +       monitor->high_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
> +       monitor->low_rssi = ADV_MONITOR_UNSET_RSSI;
> +       monitor->low_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
> +
> +       monitor->type = MONITOR_TYPE_NONE;
> +       monitor->patterns = NULL;
> +
> +       return monitor;
> +}
> +
> +/* Matches a monitor based on its D-Bus path */
> +static bool monitor_match(const void *a, const void *b)
> +{
> +       const GDBusProxy *proxy = b;
> +       const struct adv_monitor *monitor = a;
> +
> +       if (!proxy || !monitor)
> +               return false;
> +
> +       if (strcmp(g_dbus_proxy_get_path(proxy), monitor->path) != 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +/* Retrieves Type from the remote Adv Monitor object, verifies the value and
> + * update the local Adv Monitor
> + */
> +static bool parse_monitor_type(struct adv_monitor *monitor, const char *path)
> +{
> +       DBusMessageIter iter;
> +       const struct adv_monitor_type *t;
> +       const char *type_str;
> +       uint16_t adapter_id = monitor->app->manager->adapter_id;
> +
> +       if (!g_dbus_proxy_get_property(monitor->proxy, "Type", &iter)) {
> +               btd_error(adapter_id, "Failed to retrieve property Type from "
> +                       "the Adv Monitor at path %s", path);
> +               return false;
> +       }
> +
> +       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
> +               goto failed;
> +
> +       dbus_message_iter_get_basic(&iter, &type_str);
> +
> +       for (t = supported_types; t->name; t++) {
> +               if (strcmp(t->name, type_str) == 0) {
> +                       monitor->type = t->type;
> +                       return true;
> +               }
> +       }
> +
> +failed:
> +       btd_error(adapter_id, "Invalid argument of property Type of the Adv "
> +                       "Monitor at path %s", path);
> +
> +       return false;
> +}
> +
> +/* Retrieves RSSIThresholdsAndTimers from the remote Adv Monitor object,
> + * verifies the values and update the local Adv Monitor
> + */
> +static bool parse_rssi_and_timeout(struct adv_monitor *monitor,
> +                                       const char *path)
> +{
> +       DBusMessageIter prop_struct, iter;
> +       int16_t h_rssi, l_rssi;
> +       uint16_t h_rssi_timer, l_rssi_timer;
> +       uint16_t adapter_id = monitor->app->manager->adapter_id;
> +
> +       /* Property RSSIThresholdsAndTimers is optional */
> +       if (!g_dbus_proxy_get_property(monitor->proxy,
> +                                       "RSSIThresholdsAndTimers",
> +                                       &prop_struct)) {
> +               DBG("Adv Monitor at path %s provides no RSSI thresholds and "
> +                       "timeouts", path);
> +               return true;
> +       }
> +
> +       if (dbus_message_iter_get_arg_type(&prop_struct) != DBUS_TYPE_STRUCT)
> +               goto failed;
> +
> +       dbus_message_iter_recurse(&prop_struct, &iter);
> +
> +       /* Extract HighRSSIThreshold */
> +       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT16)
> +               goto failed;
> +       dbus_message_iter_get_basic(&iter, &h_rssi);
> +       if (!dbus_message_iter_next(&iter))
> +               goto failed;
> +
> +       /* Extract HighRSSIThresholdTimer */
> +       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
> +               goto failed;
> +       dbus_message_iter_get_basic(&iter, &h_rssi_timer);
> +       if (!dbus_message_iter_next(&iter))
> +               goto failed;
> +
> +       /* Extract LowRSSIThreshold */
> +       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT16)
> +               goto failed;
> +       dbus_message_iter_get_basic(&iter, &l_rssi);
> +       if (!dbus_message_iter_next(&iter))
> +               goto failed;
> +
> +       /* Extract LowRSSIThresholdTimer */
> +       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
> +               goto failed;
> +       dbus_message_iter_get_basic(&iter, &l_rssi_timer);
> +
> +       /* Verify the values of RSSIs and their timers. For simplicity, we
> +        * enforce the all-or-none rule to these fields. In other words, either
> +        * all are set to the unset values or all are set within valid ranges.
> +        */
> +       if (h_rssi == ADV_MONITOR_UNSET_RSSI &&
> +               l_rssi == ADV_MONITOR_UNSET_RSSI &&
> +               h_rssi_timer == ADV_MONITOR_UNSET_TIMER &&
> +               l_rssi_timer == ADV_MONITOR_UNSET_TIMER) {
> +               goto done;
> +       }
> +
> +       if (h_rssi < ADV_MONITOR_MIN_RSSI || h_rssi > ADV_MONITOR_MAX_RSSI ||
> +               l_rssi < ADV_MONITOR_MIN_RSSI ||
> +               l_rssi > ADV_MONITOR_MAX_RSSI || h_rssi <= l_rssi) {
> +               goto failed;
> +       }
> +
> +       if (h_rssi_timer < ADV_MONITOR_MIN_TIMER ||
> +               h_rssi_timer > ADV_MONITOR_MAX_TIMER ||
> +               l_rssi_timer < ADV_MONITOR_MIN_TIMER ||
> +               l_rssi_timer > ADV_MONITOR_MAX_TIMER) {
> +               goto failed;
> +       }
> +
> +       monitor->high_rssi = h_rssi;
> +       monitor->low_rssi = l_rssi;
> +       monitor->high_rssi_timeout = h_rssi_timer;
> +       monitor->low_rssi_timeout = l_rssi_timer;
> +
> +done:
> +       DBG("Adv Monitor at %s initiated with high RSSI threshold %d, high "
> +               "RSSI threshold timeout %d, low RSSI threshold %d, low RSSI "
> +               "threshold timeout %d", path, monitor->high_rssi,
> +               monitor->high_rssi_timeout, monitor->low_rssi,
> +               monitor->low_rssi_timeout);
> +
> +       return true;
> +
> +failed:
> +       monitor->high_rssi = ADV_MONITOR_UNSET_RSSI;
> +       monitor->low_rssi = ADV_MONITOR_UNSET_RSSI;
> +       monitor->high_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
> +       monitor->low_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
> +
> +       btd_error(adapter_id, "Invalid argument of property "
> +                       "RSSIThresholdsAndTimers of the Adv Monitor at path %s",
> +                       path);
> +
> +       return false;
> +}
> +
> +/* Retrieves Patterns from the remote Adv Monitor object, verifies the values
> + * and update the local Adv Monitor
> + */
> +static bool parse_patterns(struct adv_monitor *monitor, const char *path)
> +{
> +       DBusMessageIter array, array_iter;
> +       uint16_t num_patterns = 0;
> +       uint16_t adapter_id = monitor->app->manager->adapter_id;
> +
> +       if (!g_dbus_proxy_get_property(monitor->proxy, "Patterns", &array)) {
> +               btd_error(adapter_id, "Failed to retrieve property Patterns "
> +                               "from the Adv Monitor at path %s", path);
> +               return false;
> +       }
> +
> +       monitor->patterns = queue_new();
> +
> +       if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY ||
> +               dbus_message_iter_get_element_type(&array) !=
> +               DBUS_TYPE_STRUCT) {
> +               goto failed;
> +       }
> +
> +       dbus_message_iter_recurse(&array, &array_iter);
> +
> +       while (dbus_message_iter_get_arg_type(&array_iter) ==
> +               DBUS_TYPE_STRUCT) {
> +               int value_len;
> +               uint8_t *value;
> +               uint8_t offset, ad_type;
> +               struct pattern *pattern;
> +               DBusMessageIter struct_iter, value_iter;
> +
> +               dbus_message_iter_recurse(&array_iter, &struct_iter);
> +
> +               // Extract start position
> +               if (dbus_message_iter_get_arg_type(&struct_iter) !=
> +                       DBUS_TYPE_BYTE) {
> +                       goto failed;
> +               }
> +               dbus_message_iter_get_basic(&struct_iter, &offset);
> +               if (!dbus_message_iter_next(&struct_iter))
> +                       goto failed;
> +
> +               // Extract AD data type
> +               if (dbus_message_iter_get_arg_type(&struct_iter) !=
> +                       DBUS_TYPE_BYTE) {
> +                       goto failed;
> +               }
> +               dbus_message_iter_get_basic(&struct_iter, &ad_type);
> +               if (!dbus_message_iter_next(&struct_iter))
> +                       goto failed;
> +
> +               // Extract value of a pattern
> +               if (dbus_message_iter_get_arg_type(&struct_iter) !=
> +                       DBUS_TYPE_ARRAY) {
> +                       goto failed;
> +               }
> +               dbus_message_iter_recurse(&struct_iter, &value_iter);
> +               dbus_message_iter_get_fixed_array(&value_iter, &value,
> +                                                       &value_len);
> +
> +               // Verify the values
> +               if (offset > BT_AD_MAX_DATA_LEN - 1)
> +                       goto failed;
> +
> +               if (ad_type > BT_AD_3D_INFO_DATA &&
> +                       ad_type != BT_AD_MANUFACTURER_DATA
> +                       || ad_type < BT_AD_FLAGS) {
> +                       goto failed;
> +               }
> +
> +               if (!value || value_len <= 0 || value_len > BT_AD_MAX_DATA_LEN)
> +                       goto failed;
> +
> +               pattern = g_new0(struct pattern, 1);

new0

> +               if (!pattern)
> +                       goto failed;
> +
> +               pattern->ad_type = ad_type;
> +               pattern->offset = offset;
> +               pattern->length = value_len;
> +               g_memmove(pattern->value, value, pattern->length);

This looks wrong, we shouldn't be changing the memory value points to,
this might be equivalent to memcpy so Id just use that instead.

> +
> +               queue_push_tail(monitor->patterns, pattern);
> +
> +               dbus_message_iter_next(&array_iter);
> +       }
> +
> +       /* There must be at least one pattern. */
> +       if (queue_isempty(monitor->patterns))
> +               goto failed;
> +
> +       return true;
> +
> +failed:
> +       queue_destroy(monitor->patterns, pattern_free);
> +       monitor->patterns = NULL;
> +
> +       btd_error(adapter_id, "Invalid argument of property Patterns of the "
> +                       "Adv Monitor at path %s", path);
> +
> +       return false;
> +}
> +
> +/* Processes the content of the remote Adv Monitor */
> +static bool monitor_process(struct adv_monitor *monitor,
> +                               struct adv_monitor_app *app)
> +{
> +       const char *path = g_dbus_proxy_get_path(monitor->proxy);
> +
> +       monitor->state = MONITOR_STATE_FAILED;
> +
> +       if (!parse_monitor_type(monitor, path))
> +               goto done;
> +
> +       if (!parse_rssi_and_timeout(monitor, path))
> +               goto done;
> +
> +       if (monitor->type == MONITOR_TYPE_OR_PATTERNS &&
> +               parse_patterns(monitor, path)) {
> +               monitor->state = MONITOR_STATE_INITED;
> +       }
> +
> +done:
> +       return monitor->state != MONITOR_STATE_FAILED;
> +}
> +
> +/* Handles an Adv Monitor D-Bus proxy added event */
> +static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data)
> +{
> +       struct adv_monitor *monitor;
> +       struct adv_monitor_app *app = user_data;
> +       uint16_t adapter_id = app->manager->adapter_id;
> +       const char *path = g_dbus_proxy_get_path(proxy);
> +       const char *iface = g_dbus_proxy_get_interface(proxy);
> +
> +       if (strcmp(iface, ADV_MONITOR_INTERFACE) != 0 ||
> +               !g_str_has_prefix(path, app->path)) {
> +               return;
> +       }
> +
> +       if (queue_find(app->monitors, monitor_match, proxy)) {
> +               btd_error(adapter_id, "Adv Monitor proxy already exists with "
> +                               "path %s", path);
> +               return;
> +       }
> +
> +       monitor = monitor_new(app, proxy);
> +       if (!monitor) {
> +               btd_error(adapter_id, "Failed to allocate an Adv Monitor for "
> +                               "the object at %s", path);
> +               return;
> +       }
> +
> +       if (!monitor_process(monitor, app)) {
> +               monitor_release(monitor, NULL);
> +               monitor_free(monitor);
> +               DBG("Adv Monitor at path %s released due to invalid content",
> +                       path);
> +               return;
> +       }
> +
> +       queue_push_tail(app->monitors, monitor);
> +
> +       DBG("Adv Monitor allocated for the object at path %s", path);
> +}
> +
> +/* Handles the removal of an Adv Monitor D-Bus proxy */
> +static void monitor_proxy_removed_cb(GDBusProxy *proxy, void *user_data)
> +{
> +       struct adv_monitor *monitor;
> +       struct adv_monitor_app *app = user_data;
> +
> +       monitor = queue_remove_if(app->monitors, monitor_match, proxy);
> +       if (monitor) {
> +               DBG("Adv Monitor removed for the object at path %s",
> +                       monitor->path);
> +
> +               /* The object was gone, so we don't need to call Release() */
> +               monitor_free(monitor);
> +       }
> +}
> +
>  /* Creates an app object, initiates it and sets D-Bus event handlers */
>  static struct adv_monitor_app *app_create(DBusConnection *conn,
>                                         DBusMessage *msg, const char *sender,
> @@ -165,8 +634,17 @@ static struct adv_monitor_app *app_create(DBusConnection *conn,
>                 return NULL;
>         }
>
> +       app->monitors = queue_new();
> +
>         g_dbus_client_set_disconnect_watch(app->client, app_disconnect_cb, app);
> -       g_dbus_client_set_proxy_handlers(app->client, NULL, NULL, NULL, NULL);
> +
> +       /* Note that any property changes on a monitor object would not affect
> +        * the content of the corresponding monitor.
> +        */
> +       g_dbus_client_set_proxy_handlers(app->client, monitor_proxy_added_cb,
> +                                               monitor_proxy_removed_cb, NULL,
> +                                               app);
> +
>         g_dbus_client_set_ready_watch(app->client, app_ready_cb, app);
>
>         app->reg = dbus_message_ref(msg);
> @@ -273,18 +751,6 @@ static const GDBusMethodTable adv_monitor_methods[] = {
>         { }
>  };
>
> -enum monitor_type {
> -       MONITOR_TYPE_OR_PATTERNS,
> -};
> -
> -const struct adv_monitor_type {
> -       enum monitor_type type;
> -       const char *name;
> -} supported_types[] = {
> -       { MONITOR_TYPE_OR_PATTERNS, "or_patterns" },
> -       { },
> -};
> -
>  /* Gets SupportedMonitorTypes property */
>  static gboolean get_supported_monitor_types(const GDBusPropertyTable *property,
>                                                 DBusMessageIter *iter,
> --
> 2.26.2
>


-- 
Luiz Augusto von Dentz



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux