src/adv_monitor: add monitor with rssi support for mgmt
authorArchie Pusaka <apusaka@chromium.org>
Fri, 15 Jan 2021 11:50:39 +0000 (19:50 +0800)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 11 Mar 2022 13:38:34 +0000 (19:08 +0530)
Using the new opcode MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI to
monitor advertisement according to some RSSI criteria.

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
src/adv_monitor.c

index f695e51..a7c1dad 100644 (file)
 #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 */
+#define ADV_MONITOR_UNSET_TIMEOUT      0       /* second */
+#define ADV_MONITOR_MIN_TIMEOUT                1       /* second */
+#define ADV_MONITOR_MAX_TIMEOUT                300     /* second */
+#define ADV_MONITOR_DEFAULT_LOW_TIMEOUT        5       /* second */
+#define ADV_MONITOR_DEFAULT_HIGH_TIMEOUT 10    /* second */
+#define ADV_MONITOR_UNSET_SAMPLING_PERIOD 256  /* 100 ms */
+#define ADV_MONITOR_MAX_SAMPLING_PERIOD        255     /* 100 ms */
+#define ADV_MONITOR_DEFAULT_SAMPLING_PERIOD 0  /* 100 ms */
 
 struct btd_adv_monitor_manager {
        struct btd_adapter *adapter;
@@ -94,6 +99,10 @@ struct adv_monitor {
        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 */
+       uint16_t sampling_period;       /* Merge packets in the same timeslot.
+                                        * Currenly unimplemented in user space.
+                                        * Used only to pass data to kernel.
+                                        */
        struct queue *devices;          /* List of adv_monitor_device objects */
 
        enum monitor_type type;         /* MONITOR_TYPE_* */
@@ -359,9 +368,10 @@ static struct adv_monitor *monitor_new(struct adv_monitor_app *app,
        monitor->state = MONITOR_STATE_NEW;
 
        monitor->high_rssi = ADV_MONITOR_UNSET_RSSI;
-       monitor->high_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
+       monitor->high_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT;
        monitor->low_rssi = ADV_MONITOR_UNSET_RSSI;
-       monitor->low_rssi_timeout = ADV_MONITOR_UNSET_TIMER;
+       monitor->low_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT;
+       monitor->sampling_period = ADV_MONITOR_UNSET_SAMPLING_PERIOD;
        monitor->devices = queue_new();
 
        monitor->type = MONITOR_TYPE_NONE;
@@ -422,103 +432,119 @@ failed:
        return false;
 }
 
-/* Retrieves RSSIThresholdsAndTimers from the remote Adv Monitor object,
+/* Retrieves RSSI thresholds and timeouts 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;
+       DBusMessageIter iter;
+       GDBusProxy *proxy = monitor->proxy;
+       int16_t h_rssi = ADV_MONITOR_UNSET_RSSI;
+       int16_t l_rssi = ADV_MONITOR_UNSET_RSSI;
+       uint16_t h_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT;
+       uint16_t l_rssi_timeout = ADV_MONITOR_UNSET_TIMEOUT;
+       int16_t sampling_period = ADV_MONITOR_UNSET_SAMPLING_PERIOD;
        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;
+       /* Extract RSSIHighThreshold */
+       if (g_dbus_proxy_get_property(proxy, "RSSIHighThreshold", &iter)) {
+               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_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 RSSIHighTimeout */
+       if (g_dbus_proxy_get_property(proxy, "RSSIHighTimeout", &iter)) {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
+                       goto failed;
+               dbus_message_iter_get_basic(&iter, &h_rssi_timeout);
+       }
 
-       /* 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 RSSILowThreshold */
+       if (g_dbus_proxy_get_property(proxy, "RSSILowThreshold", &iter)) {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT16)
+                       goto failed;
+               dbus_message_iter_get_basic(&iter, &l_rssi);
+       }
 
-       /* 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 RSSILowTimeout */
+       if (g_dbus_proxy_get_property(proxy, "RSSILowTimeout", &iter)) {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
+                       goto failed;
+               dbus_message_iter_get_basic(&iter, &l_rssi_timeout);
+       }
 
-       /* Extract LowRSSIThresholdTimer */
-       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
-               goto failed;
-       dbus_message_iter_get_basic(&iter, &l_rssi_timer);
+       /* Extract RSSISamplingPeriod */
+       if (g_dbus_proxy_get_property(proxy, "RSSISamplingPeriod", &iter)) {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16)
+                       goto failed;
+               dbus_message_iter_get_basic(&iter, &sampling_period);
+       }
 
-       /* 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.
+       /* Verify the values of RSSIs and their timeouts. All fields should be
+        * either set to the unset values or are set within valid ranges.
+        * If the fields are only partially set, we would try our best to fill
+        * in with some sane values.
         */
        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) {
+               h_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT &&
+               l_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT &&
+               sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD) {
                goto done;
        }
 
+       if (l_rssi == ADV_MONITOR_UNSET_RSSI)
+               l_rssi = ADV_MONITOR_MIN_RSSI;
+
+       if (h_rssi == ADV_MONITOR_UNSET_RSSI)
+               h_rssi = l_rssi;
+
+       if (l_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT)
+               l_rssi_timeout = ADV_MONITOR_DEFAULT_LOW_TIMEOUT;
+
+       if (h_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT)
+               h_rssi_timeout = ADV_MONITOR_DEFAULT_HIGH_TIMEOUT;
+
+       if (sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD)
+               sampling_period = ADV_MONITOR_DEFAULT_SAMPLING_PERIOD;
+
        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) {
+               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) {
+       if (h_rssi_timeout < ADV_MONITOR_MIN_TIMEOUT ||
+               h_rssi_timeout > ADV_MONITOR_MAX_TIMEOUT ||
+               l_rssi_timeout < ADV_MONITOR_MIN_TIMEOUT ||
+               l_rssi_timeout > ADV_MONITOR_MAX_TIMEOUT) {
                goto failed;
        }
 
+       if (sampling_period > ADV_MONITOR_MAX_SAMPLING_PERIOD)
+               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;
+       monitor->high_rssi_timeout = h_rssi_timeout;
+       monitor->low_rssi_timeout = l_rssi_timeout;
+       monitor->sampling_period = sampling_period;
 
 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);
+               "threshold timeout %d, sampling period %d", path,
+               monitor->high_rssi, monitor->high_rssi_timeout,
+               monitor->low_rssi, monitor->low_rssi_timeout,
+               monitor->sampling_period);
 
        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 "
+                       "Invalid argument of RSSI thresholds and timeouts "
                        "of the Adv Monitor at path %s",
                        path);
 
@@ -672,16 +698,88 @@ static void add_adv_patterns_monitor_cb(uint8_t status, uint16_t length,
        DBG("Adv monitor with handle:0x%04x added", monitor->monitor_handle);
 }
 
-static void monitor_copy_patterns(void *data, void *user_data)
+static bool monitor_rssi_is_unset(struct adv_monitor *monitor)
 {
-       struct bt_ad_pattern *pattern = data;
-       struct mgmt_cp_add_adv_monitor *cp = user_data;
+       return monitor->high_rssi == ADV_MONITOR_UNSET_RSSI &&
+               monitor->low_rssi == ADV_MONITOR_UNSET_RSSI &&
+               monitor->high_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT &&
+               monitor->low_rssi_timeout == ADV_MONITOR_UNSET_TIMEOUT &&
+               monitor->sampling_period == ADV_MONITOR_UNSET_SAMPLING_PERIOD;
+}
 
-       if (!pattern)
-               return;
+/* sends MGMT_OP_ADD_ADV_PATTERNS_MONITOR */
+static bool monitor_send_add_pattern(struct adv_monitor *monitor)
+{
+       struct mgmt_cp_add_adv_monitor *cp = NULL;
+       uint8_t pattern_count, cp_len;
+       const struct queue_entry *e;
+       bool success = true;
+
+       pattern_count = queue_length(monitor->patterns);
+       cp_len = sizeof(*cp) + pattern_count * sizeof(struct mgmt_adv_pattern);
 
-       memcpy(cp->patterns + cp->pattern_count, pattern, sizeof(*pattern));
-       cp->pattern_count++;
+       cp = malloc0(cp_len);
+       if (!cp)
+               return false;
+
+       for (e = queue_get_entries(monitor->patterns); e; e = e->next) {
+               struct bt_ad_pattern *pattern = e->data;
+
+               memcpy(&cp->patterns[cp->pattern_count++], pattern,
+                                                       sizeof(*pattern));
+       }
+
+       if (!mgmt_send(monitor->app->manager->mgmt,
+                       MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+                       monitor->app->manager->adapter_id, cp_len, cp,
+                       add_adv_patterns_monitor_cb, monitor, NULL)) {
+               error("Unable to send Add Adv Patterns Monitor command");
+               success = false;
+       }
+
+       free(cp);
+       return success;
+}
+
+/* sends MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI */
+static bool monitor_send_add_pattern_rssi(struct adv_monitor *monitor)
+{
+       struct mgmt_cp_add_adv_patterns_monitor_rssi *cp = NULL;
+       uint8_t pattern_count, cp_len;
+       const struct queue_entry *e;
+       bool success = true;
+
+       pattern_count = queue_length(monitor->patterns);
+       cp_len = sizeof(*cp) + pattern_count * sizeof(struct mgmt_adv_pattern);
+
+       cp = malloc0(cp_len);
+       if (!cp)
+               return false;
+
+       cp->rssi.high_threshold = monitor->high_rssi;
+       /* High threshold timeout is unsupported in kernel. Value must be 0. */
+       cp->rssi.high_threshold_timeout = 0;
+       cp->rssi.low_threshold = monitor->low_rssi;
+       cp->rssi.low_threshold_timeout = htobs(monitor->low_rssi_timeout);
+       cp->rssi.sampling_period = monitor->sampling_period;
+
+       for (e = queue_get_entries(monitor->patterns); e; e = e->next) {
+               struct bt_ad_pattern *pattern = e->data;
+
+               memcpy(&cp->patterns[cp->pattern_count++], pattern,
+                                                       sizeof(*pattern));
+       }
+
+       if (!mgmt_send(monitor->app->manager->mgmt,
+                       MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI,
+                       monitor->app->manager->adapter_id, cp_len, cp,
+                       add_adv_patterns_monitor_cb, monitor, NULL)) {
+               error("Unable to send Add Adv Patterns Monitor RSSI command");
+               success = false;
+       }
+
+       free(cp);
+       return success;
 }
 
 /* Handles an Adv Monitor D-Bus proxy added event */
@@ -689,8 +787,6 @@ static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data)
 {
        struct adv_monitor *monitor;
        struct adv_monitor_app *app = user_data;
-       struct mgmt_cp_add_adv_monitor *cp = NULL;
-       uint8_t pattern_count, cp_len;
        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);
@@ -724,24 +820,12 @@ static void monitor_proxy_added_cb(GDBusProxy *proxy, void *user_data)
 
        queue_push_tail(app->monitors, monitor);
 
-       pattern_count = queue_length(monitor->patterns);
-       cp_len = sizeof(struct mgmt_cp_add_adv_monitor) +
-                       pattern_count * sizeof(struct mgmt_adv_pattern);
-
-       cp = malloc0(cp_len);
-       queue_foreach(monitor->patterns, monitor_copy_patterns, cp);
-
-       if (!mgmt_send(app->manager->mgmt, MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
-                       adapter_id, cp_len, cp, add_adv_patterns_monitor_cb,
-                       monitor, NULL)) {
-               error("Unable to send Add Adv Patterns Monitor command");
-               goto done;
-       }
+       if (monitor_rssi_is_unset(monitor))
+               monitor_send_add_pattern(monitor);
+       else
+               monitor_send_add_pattern_rssi(monitor);
 
        DBG("Adv Monitor allocated for the object at path %s", path);
-
-done:
-       free(cp);
 }
 
 /* Handles the removal of an Adv Monitor D-Bus proxy */
@@ -1427,10 +1511,7 @@ static void adv_monitor_filter_rssi(struct adv_monitor *monitor,
         * DeviceFound() event without tracking for the RSSI as the Adv has
         * already matched the pattern filter.
         */
-       if (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) {
+       if (monitor_rssi_is_unset(monitor)) {
                DBG("Calling DeviceFound() on Adv Monitor of owner %s "
                    "at path %s", monitor->app->owner, monitor->path);