device: Support marking a device with wake allowed
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Mon, 6 Jul 2020 20:25:15 +0000 (13:25 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Mon, 12 Apr 2021 09:00:49 +0000 (14:30 +0530)
If a device is allowed to wake the host system from suspend, it should
be marked as wake allowed. We add support for a new property that is
sent to the kernel via set device flags mgmt op. We also add the dbus
endpoint to allow the wake allowed setting to be controlled.

In order for wake allowed to be set, the profile must also support wake.
This setting isn't exposed to the user but must be set by profiles that
intend to support wake from suspend.

If a device is connecting for the first time, it will be marked
WakeAllowed if the profile supports it. On subsequent reloads of bluez,
the stored setting "WakeAllowed" will be used to override any other
setting.

Signed-off-by: Anuj Jain <anuj01.jain@samsung.com>
Signed-off-by: Ayush Garg <ayush.garg@samsung.com>
lib/mgmt.h
src/adapter.c
src/adapter.h
src/device.c
src/device.h

index 4f00a85..0e7d827 100644 (file)
@@ -675,6 +675,8 @@ struct mgmt_rp_get_device_flags {
        uint32_t current_flags;
 } __packed;
 
+#define DEVICE_FLAG_REMOTE_WAKEUP      (1 << 0)
+
 #define MGMT_OP_SET_DEVICE_FLAGS       0x0050
 #define MGMT_SET_DEVICE_FLAGS_SIZE     11
 struct mgmt_cp_set_device_flags {
index 26a5a1f..84994b7 100644 (file)
@@ -9957,6 +9957,94 @@ void adapter_auto_connect_add(struct btd_adapter *adapter,
        adapter->connect_list = g_slist_append(adapter->connect_list, device);
 }
 
+static void set_device_wakeable_complete(uint8_t status, uint16_t length,
+                                        const void *param, void *user_data)
+{
+       const struct mgmt_rp_set_device_flags *rp = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *dev;
+       char addr[18];
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               btd_error(adapter->dev_id, "Set device flags return status: %s",
+                         mgmt_errstr(status));
+               return;
+       }
+
+       if (length < sizeof(*rp)) {
+               btd_error(adapter->dev_id,
+                         "Too small Set Device Flags complete event: %d",
+                         length);
+               return;
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       dev = btd_adapter_find_device(adapter, &rp->addr.bdaddr, rp->addr.type);
+       if (!dev) {
+               btd_error(adapter->dev_id,
+                         "Set Device Flags complete for unknown device %s",
+                         addr);
+               return;
+       }
+
+       device_set_wake_allowed_complete(dev);
+}
+
+void adapter_set_device_wakeable(struct btd_adapter *adapter,
+                                struct btd_device *device, bool wakeable)
+{
+       struct mgmt_cp_set_device_flags cp;
+       const bdaddr_t *bdaddr;
+       uint8_t bdaddr_type;
+
+       if (!kernel_conn_control)
+               return;
+
+       bdaddr = device_get_address(device);
+       bdaddr_type = btd_device_get_bdaddr_type(device);
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+       cp.addr.type = bdaddr_type;
+       cp.current_flags = btd_device_get_current_flags(device);
+       if (wakeable)
+               cp.current_flags |= DEVICE_FLAG_REMOTE_WAKEUP;
+       else
+               cp.current_flags &= ~DEVICE_FLAG_REMOTE_WAKEUP;
+
+       mgmt_send(adapter->mgmt, MGMT_OP_SET_DEVICE_FLAGS, adapter->dev_id,
+                 sizeof(cp), &cp, set_device_wakeable_complete, adapter, NULL);
+}
+
+static void device_flags_changed_callback(uint16_t index, uint16_t length,
+                                         const void *param, void *user_data)
+{
+       const struct mgmt_ev_device_flags_changed *ev = param;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *dev;
+       char addr[18];
+
+       if (length < sizeof(*ev)) {
+               btd_error(adapter->dev_id,
+                         "Too small Device Flags Changed event: %d",
+                         length);
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       dev = btd_adapter_find_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       if (!dev) {
+               btd_error(adapter->dev_id,
+                       "Device Flags Changed for unknown device %s", addr);
+               return;
+       }
+
+       btd_device_flags_changed(dev, ev->supported_flags, ev->current_flags);
+}
+
+
 static void remove_device_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -14512,6 +14600,11 @@ static int adapter_register(struct btd_adapter *adapter)
 #endif
 
 load:
+       mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FLAGS_CHANGED,
+                                               adapter->dev_id,
+                                               device_flags_changed_callback,
+                                               adapter, NULL);
+
        load_config(adapter);
        fix_storage(adapter);
        load_drivers(adapter);
index 0b7c1f6..f4b2393 100644 (file)
@@ -306,6 +306,8 @@ int adapter_connect_list_add(struct btd_adapter *adapter,
                                        struct btd_device *device);
 void adapter_connect_list_remove(struct btd_adapter *adapter,
                                                struct btd_device *device);
+void adapter_set_device_wakeable(struct btd_adapter *adapter,
+                                struct btd_device *dev, bool wakeable);
 void adapter_auto_connect_add(struct btd_adapter *adapter,
                                        struct btd_device *device);
 void adapter_auto_connect_remove(struct btd_adapter *adapter,
index 2022219..e04ee24 100644 (file)
@@ -195,6 +195,13 @@ struct csrk_info {
        uint32_t counter;
 };
 
+enum {
+       WAKE_FLAG_DEFAULT = 0,
+       WAKE_FLAG_ENABLED,
+       WAKE_FLAG_DISABLED,
+};
+
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 typedef enum {
        DEV_PAIRED_NONE = 0,
@@ -231,6 +238,21 @@ struct btd_device {
        bool            le;
        bool            pending_paired;         /* "Paired" waiting for SDP */
        bool            svc_refreshed;
+
+       /* Manage whether this device can wake the system from suspend.
+        * - wake_support: Requires a profile that supports wake (i.e. HID)
+        * - wake_allowed: Is wake currently allowed?
+        * - pending_wake_allowed - Wake flag sent via set_device_flags
+        * - wake_override - User configured wake setting
+        */
+       bool            wake_support;
+       bool            wake_allowed;
+       bool            pending_wake_allowed;
+       uint8_t         wake_override;
+       GDBusPendingPropertySet wake_id;
+
+       uint32_t        supported_flags;
+       uint32_t        current_flags;
        GSList          *svc_callbacks;
        GSList          *eir_uuids;
        struct bt_ad    *ad;
@@ -593,6 +615,12 @@ static gboolean store_device_info_cb(gpointer user_data)
        g_key_file_set_boolean(key_file, "General", "Blocked",
                                                        device->blocked);
 
+       if (device->wake_override != WAKE_FLAG_DEFAULT) {
+               g_key_file_set_boolean(key_file, "General", "WakeAllowed",
+                                      device->wake_override ==
+                                              WAKE_FLAG_ENABLED);
+       }
+
        if (device->uuids) {
                GSList *l;
                int i;
@@ -1802,6 +1830,130 @@ dev_property_advertising_data_exist(const GDBusPropertyTable *property,
        return bt_ad_has_data(device->ad, NULL);
 }
 
+static bool device_get_wake_support(struct btd_device *device)
+{
+       return device->wake_support;
+}
+
+void device_set_wake_support(struct btd_device *device, bool wake_support)
+{
+       device->wake_support = wake_support;
+
+       /* If wake configuration has not been made yet, set the initial
+        * configuration.
+        */
+       if (device->wake_override == WAKE_FLAG_DEFAULT) {
+               device_set_wake_override(device, wake_support);
+               device_set_wake_allowed(device, wake_support, -1U);
+       }
+}
+
+static bool device_get_wake_allowed(struct btd_device *device)
+{
+       return device->wake_allowed;
+}
+
+void device_set_wake_override(struct btd_device *device, bool wake_override)
+{
+       if (wake_override) {
+               device->wake_override = WAKE_FLAG_ENABLED;
+               device->current_flags |= DEVICE_FLAG_REMOTE_WAKEUP;
+       } else {
+               device->wake_override = WAKE_FLAG_DISABLED;
+               device->current_flags &= ~DEVICE_FLAG_REMOTE_WAKEUP;
+       }
+}
+
+void device_set_wake_allowed(struct btd_device *device, bool wake_allowed,
+                            GDBusPendingPropertySet id)
+{
+       /* Pending and current value are the same unless there is a change in
+        * progress. Only update wake allowed if pending value doesn't match the
+        * new value.
+        */
+       if (wake_allowed == device->pending_wake_allowed)
+               return;
+
+       device->wake_id = id;
+       device->pending_wake_allowed = wake_allowed;
+       adapter_set_device_wakeable(device_get_adapter(device), device,
+                                   wake_allowed);
+}
+
+void device_set_wake_allowed_complete(struct btd_device *device)
+{
+       if (device->wake_id != -1U) {
+               g_dbus_pending_property_success(device->wake_id);
+               device->wake_id = -1U;
+       }
+
+       device->wake_allowed = device->pending_wake_allowed;
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                       DEVICE_INTERFACE, "WakeAllowed");
+
+       store_device_info(device);
+}
+
+
+static gboolean
+dev_property_get_wake_allowed(const GDBusPropertyTable *property,
+                            DBusMessageIter *iter, void *data)
+{
+       struct btd_device *device = data;
+       dbus_bool_t wake_allowed = device_get_wake_allowed(device);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &wake_allowed);
+
+       return TRUE;
+}
+
+static void dev_property_set_wake_allowed(const GDBusPropertyTable *property,
+                                        DBusMessageIter *value,
+                                        GDBusPendingPropertySet id, void *data)
+{
+       struct btd_device *device = data;
+       dbus_bool_t b;
+
+       if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       if (device->temporary) {
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".Unsupported",
+                                       "Cannot set property while temporary");
+               return;
+       }
+
+       /* Emit busy or success depending on current value. */
+       if (b == device->pending_wake_allowed) {
+               if (device->wake_allowed == device->pending_wake_allowed)
+                       g_dbus_pending_property_success(id);
+               else
+                       g_dbus_pending_property_error(
+                               id, ERROR_INTERFACE ".Busy",
+                               "Property change in progress");
+
+               return;
+       }
+
+       dbus_message_iter_get_basic(value, &b);
+       device_set_wake_override(device, b);
+       device_set_wake_allowed(device, b, id);
+}
+
+static gboolean dev_property_wake_allowed_exist(
+               const GDBusPropertyTable *property, void *data)
+{
+       struct btd_device *device = data;
+
+       return device_get_wake_support(device);
+}
+
+
 static gboolean disconnect_all(gpointer user_data)
 {
        struct btd_device *device = user_data;
@@ -4490,6 +4642,9 @@ static const GDBusPropertyTable device_properties[] = {
        { "AdvertisingData", "a{yv}", dev_property_get_advertising_data,
                                NULL, dev_property_advertising_data_exist,
                                G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
+       { "WakeAllowed", "b", dev_property_get_wake_allowed,
+                               dev_property_set_wake_allowed,
+                               dev_property_wake_allowed_exist },
        { }
 };
 
@@ -4807,9 +4962,11 @@ static void convert_info(struct btd_device *device, GKeyFile *key_file)
 static void load_info(struct btd_device *device, const char *local,
                        const char *peer, GKeyFile *key_file)
 {
+       GError *gerr = NULL;
        char *str;
        gboolean store_needed = FALSE;
        gboolean blocked;
+       gboolean wake_allowed;
        char **uuids;
        int source, vendor, product, version;
        char **techno, **t;
@@ -4981,6 +5138,18 @@ next:
                btd_device_set_pnpid(device, source, vendor, product, version);
        }
 
+       /* Wake allowed is only configured and stored if user changed it.
+        * Otherwise, we enable if profile supports it.
+        */
+       wake_allowed = g_key_file_get_boolean(key_file, "General",
+                                             "WakeAllowed", &gerr);
+       if (!gerr) {
+               device_set_wake_override(device, wake_allowed);
+       } else {
+               g_error_free(gerr);
+               gerr = NULL;
+       }
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
        /* Load Service changed Registered flag */
        svc_change_regd = g_key_file_get_boolean(key_file, "Att",
@@ -9307,6 +9476,48 @@ void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
        store_device_info(device);
 }
 
+uint32_t btd_device_get_current_flags(struct btd_device *dev)
+{
+       return dev->current_flags;
+}
+
+/* This event is sent immediately after add device on all mgmt sockets.
+ * Afterwards, it is only sent to mgmt sockets other than the one which called
+ * set_device_flags.
+ */
+void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags,
+                             uint32_t current_flags)
+{
+       const uint32_t changed_flags = dev->current_flags ^ current_flags;
+       bool flag_value;
+
+       dev->supported_flags = supported_flags;
+       dev->current_flags = current_flags;
+
+       if (!changed_flags)
+               return;
+
+       if (changed_flags & DEVICE_FLAG_REMOTE_WAKEUP) {
+               flag_value = !!(current_flags & DEVICE_FLAG_REMOTE_WAKEUP);
+               dev->pending_wake_allowed = flag_value;
+
+               /* If an override exists and doesn't match the current state,
+                * apply it. This logic will run after Add Device only and will
+                * enable wake for previously paired devices.
+                */
+               if (dev->wake_override != WAKE_FLAG_DEFAULT) {
+                       bool wake_allowed =
+                               dev->wake_override == WAKE_FLAG_ENABLED;
+                       if (flag_value != wake_allowed)
+                               device_set_wake_allowed(dev, wake_allowed, -1U);
+                       else
+                               device_set_wake_allowed_complete(dev);
+               } else {
+                       device_set_wake_allowed_complete(dev);
+               }
+       }
+}
+
 static void service_state_changed(struct btd_service *service,
                                                btd_service_state_t old_state,
                                                btd_service_state_t new_state,
index 517f7d9..909e7f1 100644 (file)
@@ -177,6 +177,11 @@ void device_store_svc_chng_ccc(struct btd_device *device, uint8_t bdaddr_type,
 void device_load_svc_chng_ccc(struct btd_device *device, uint16_t *ccc_le,
                                                        uint16_t *ccc_bredr);
 
+void device_set_wake_support(struct btd_device *device, bool wake_support);
+void device_set_wake_override(struct btd_device *device, bool wake_override);
+void device_set_wake_allowed(struct btd_device *device, bool wake_allowed,
+                            guint32 id);
+void device_set_wake_allowed_complete(struct btd_device *device);
 typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
                                        void *user_data);
 
@@ -249,5 +254,9 @@ void device_change_pkt_type(gpointer data, gpointer user_data);
 int device_discover_services(struct btd_device *device);
 int btd_device_connect_services(struct btd_device *dev, GSList *services);
 
+uint32_t btd_device_get_current_flags(struct btd_device *dev);
+void btd_device_flags_changed(struct btd_device *dev, uint32_t supported_flags,
+                             uint32_t current_flags);
+
 void btd_device_init(void);
 void btd_device_cleanup(void);