summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>2020-09-11 15:30:37 -0700
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2020-09-14 13:25:37 -0700
commit6611b72600c370ec31795ab48a222594c4afb7ee (patch)
tree2eedfaa697f8f127c25111bb17ff43b98ba72c34 /plugins
parent20275c5a9b22c321853199d85c2b14b06befd0f6 (diff)
downloadbluez-6611b72600c370ec31795ab48a222594c4afb7ee.tar.gz
policy: Reconnect audio on controller resume
During system suspend, all peer devices are disconnected. On resume, HID devices will reconnect but audio devices stay disconnected. As a quality of life improvement, mark audio devices that were disconnected due to suspend and attempt to reconnect them when the controller resumes (after a delay for better co-existence with Wi-Fi).
Diffstat (limited to 'plugins')
-rw-r--r--plugins/policy.c81
1 files changed, 68 insertions, 13 deletions
diff --git a/plugins/policy.c b/plugins/policy.c
index c18ca8d1f..655842942 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -62,6 +62,7 @@ struct reconnect_data {
guint timer;
bool active;
unsigned int attempt;
+ bool on_resume;
};
static const char *default_reconnect[] = {
@@ -76,6 +77,9 @@ static const int default_intervals[] = { 1, 2, 4, 8, 16, 32, 64 };
static int *reconnect_intervals = NULL;
static size_t reconnect_intervals_len = 0;
+static const int default_resume_delay = 2;
+static int resume_delay;
+
static GSList *reconnects = NULL;
static unsigned int service_id = 0;
@@ -712,6 +716,9 @@ static gboolean reconnect_timeout(gpointer data)
/* Mark the GSource as invalid */
reconnect->timer = 0;
+ /* Mark any reconnect on resume as handled */
+ reconnect->on_resume = false;
+
err = btd_device_connect_services(reconnect->dev, reconnect->services);
if (err < 0) {
error("Reconnecting services failed: %s (%d)",
@@ -725,14 +732,17 @@ static gboolean reconnect_timeout(gpointer data)
return FALSE;
}
-static void reconnect_set_timer(struct reconnect_data *reconnect)
+static void reconnect_set_timer(struct reconnect_data *reconnect, int timeout)
{
- static int timeout = 0;
+ static int interval_timeout = 0;
reconnect->active = true;
if (reconnect->attempt < reconnect_intervals_len)
- timeout = reconnect_intervals[reconnect->attempt];
+ interval_timeout = reconnect_intervals[reconnect->attempt];
+
+ if (timeout < 0)
+ timeout = interval_timeout;
DBG("attempt %u/%zu %d seconds", reconnect->attempt + 1,
reconnect_attempts, timeout);
@@ -747,7 +757,9 @@ static void disconnect_cb(struct btd_device *dev, uint8_t reason)
DBG("reason %u", reason);
- if (reason != MGMT_DEV_DISCONN_TIMEOUT)
+ /* Only attempt reconnect for the following reasons */
+ if (reason != MGMT_DEV_DISCONN_TIMEOUT &&
+ reason != MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND)
return;
reconnect = reconnect_find(dev);
@@ -756,10 +768,46 @@ static void disconnect_cb(struct btd_device *dev, uint8_t reason)
reconnect_reset(reconnect);
- DBG("Device %s identified for auto-reconnection",
- device_get_path(dev));
+ DBG("Device %s identified for auto-reconnection", device_get_path(dev));
+
+ switch (reason) {
+ case MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND:
+ if (btd_device_get_service(dev, A2DP_SINK_UUID)) {
+ DBG("%s configured to reconnect on resume",
+ device_get_path(dev));
- reconnect_set_timer(reconnect);
+ reconnect->on_resume = true;
+
+ /* If the kernel supports resume events, it is
+ * preferable to set the reconnect timer there as it is
+ * a more predictable delay.
+ */
+ if (!has_kernel_features(KERNEL_HAS_RESUME_EVT))
+ reconnect_set_timer(reconnect, resume_delay);
+ }
+ break;
+ case MGMT_DEV_DISCONN_TIMEOUT:
+ reconnect_set_timer(reconnect, -1);
+ break;
+ default:
+ DBG("Developer error. Reason = %d", reason);
+ break;
+ }
+}
+
+static void policy_adapter_resume(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ /* Check if devices on this adapter need to be reconnected on resume */
+ for (l = reconnects; l; l = g_slist_next(l)) {
+ struct reconnect_data *reconnect = l->data;
+
+ if (reconnect->on_resume &&
+ device_get_adapter(reconnect->dev) == adapter) {
+ reconnect_set_timer(reconnect, resume_delay);
+ }
+ }
}
static void conn_fail_cb(struct btd_device *dev, uint8_t status)
@@ -787,14 +835,15 @@ static void conn_fail_cb(struct btd_device *dev, uint8_t status)
return;
}
- reconnect_set_timer(reconnect);
+ reconnect_set_timer(reconnect, -1);
}
static int policy_adapter_probe(struct btd_adapter *adapter)
{
DBG("");
- btd_adapter_restore_powered(adapter);
+ if (auto_enable)
+ btd_adapter_restore_powered(adapter);
return 0;
}
@@ -802,6 +851,7 @@ static int policy_adapter_probe(struct btd_adapter *adapter)
static struct btd_adapter_driver policy_driver = {
.name = "policy",
.probe = policy_adapter_probe,
+ .resume = policy_adapter_resume,
};
static int policy_init(void)
@@ -855,14 +905,20 @@ static int policy_init(void)
auto_enable = g_key_file_get_boolean(conf, "Policy", "AutoEnable",
NULL);
+ resume_delay = g_key_file_get_integer(
+ conf, "Policy", "ResumeDelay", &gerr);
+
+ if (gerr) {
+ g_clear_error(&gerr);
+ resume_delay = default_resume_delay;
+ }
done:
if (reconnect_uuids && reconnect_uuids[0] && reconnect_attempts) {
btd_add_disconnect_cb(disconnect_cb);
btd_add_conn_fail_cb(conn_fail_cb);
}
- if (auto_enable)
- btd_register_adapter_driver(&policy_driver);
+ btd_register_adapter_driver(&policy_driver);
return 0;
}
@@ -883,8 +939,7 @@ static void policy_exit(void)
btd_service_remove_state_cb(service_id);
- if (auto_enable)
- btd_unregister_adapter_driver(&policy_driver);
+ btd_unregister_adapter_driver(&policy_driver);
}
BLUETOOTH_PLUGIN_DEFINE(policy, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,