policy: Reconnect audio on controller resume
authorAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Fri, 11 Sep 2020 22:30:37 +0000 (15:30 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 11 Mar 2022 13:38:32 +0000 (19:08 +0530)
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).

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

index ee44409..73d896e 100755 (executable)
@@ -71,6 +71,7 @@ struct reconnect_data {
        guint timer;
        bool active;
        unsigned int attempt;
+       bool on_resume;
 };
 
 #if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(TIZEN_FEATURE_BLUEZ_A2DP_MULTISTREAM)
@@ -90,6 +91,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;
@@ -758,6 +762,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)",
@@ -771,14 +778,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);
@@ -793,7 +803,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);
@@ -802,10 +814,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->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;
 
-       reconnect_set_timer(reconnect);
+       /* 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)
@@ -833,14 +881,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;
 }
@@ -848,6 +897,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)
@@ -901,14 +951,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;
 }
@@ -929,8 +985,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,
index c212486..3a9baf2 100755 (executable)
@@ -135,6 +135,7 @@ static const char *policy_options[] = {
        "ReconnectAttempts",
        "ReconnectIntervals",
        "AutoEnable",
+       "ResumeDelay",
        NULL
 };
 
index 3f3df8d..75573d0 100755 (executable)
 # This includes adapters present on start as well as adapters that are plugged
 # in later on. Defaults to 'false'.
 #AutoEnable=false
+
+# Audio devices that were disconnected due to suspend will be reconnected on
+# resume. ResumeDelay determines the delay between when the controller
+# resumes from suspend and a connection attempt is made. A longer delay is
+# better for better co-existence with Wi-Fi.
+# The value is in seconds.
+# Default: 2
+#ResumeDelay = 2