diff options
author | Hsin-Yu Chao <hychao@chromium.org> | 2020-04-06 06:18:47 +0000 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-04-08 06:17:37 +0000 |
commit | f9b957a8379b6b97cac005c8ce405393413266da (patch) | |
tree | 666de22059d07ef2ea53800432f5e094b6b97efb | |
parent | 256d755d96856d34b77f3a9c858a4357e9257165 (diff) | |
download | adhd-f9b957a8379b6b97cac005c8ce405393413266da.tar.gz |
CRAS: bt_device - Fix leaked suspend timer
When BT headset connection was interrupted by unexpected error a suspend
timer is scheduled in 5 seconds. However this timer doesn't get cleaned up
correctly so there's a chance when connection resumed within 5 seconds, headset
will disconnect immediately.
This change fixes the issue in two ways:
- When BlueZ reports headset disconnected, reset the suspend timer.
- When unexpected error occurs when headset is in disconnected status,
don't schedule suspend timer.
BUG=b:152977539
TEST=unittest
Change-Id: Ic0d07dd7b363f56998e055b4da17d61f75f331ab
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/2136575
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
Tested-by: Hsinyu Chao <hychao@chromium.org>
Commit-Queue: Hsinyu Chao <hychao@chromium.org>
-rw-r--r-- | cras/src/server/cras_bt_device.c | 10 | ||||
-rw-r--r-- | cras/src/tests/bt_device_unittest.cc | 95 |
2 files changed, 94 insertions, 11 deletions
diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c index bc6b43d9..90bd8755 100644 --- a/cras/src/server/cras_bt_device.c +++ b/cras/src/server/cras_bt_device.c @@ -626,6 +626,8 @@ cras_bt_device_start_new_conn_watch_timer(struct cras_bt_device *device) tm, CONN_WATCH_PERIOD_MS, bt_device_conn_watch_cb, device); } +static void bt_device_cancel_suspend(struct cras_bt_device *device); + void cras_bt_device_set_connected(struct cras_bt_device *device, int value) { struct cras_tm *tm = cras_system_state_get_tm(); @@ -634,8 +636,10 @@ void cras_bt_device_set_connected(struct cras_bt_device *device, int value) if (device->connected && !value) { cras_bt_profile_on_device_disconnected(device); - /* Device is disconnected, resets connected profiles. */ + /* Device is disconnected, resets connected profiles and the + * suspend timer which scheduled earlier. */ device->connected_profiles = 0; + bt_device_cancel_suspend(device); } device->connected = value; @@ -651,6 +655,10 @@ void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device, { device->connected_profiles &= !profile; + /* Do nothing if device already disconnected. */ + if (!device->connected) + return; + /* If any profile, a2dp or hfp/hsp, has dropped for some reason, * we shall make sure this device is fully disconnected within * given time so that user does not see a headset stay connected diff --git a/cras/src/tests/bt_device_unittest.cc b/cras/src/tests/bt_device_unittest.cc index d2e9a13f..c6e6dcad 100644 --- a/cras/src/tests/bt_device_unittest.cc +++ b/cras/src/tests/bt_device_unittest.cc @@ -30,6 +30,7 @@ static cras_main_message* cras_main_message_send_msg; static cras_message_callback cras_main_message_add_handler_callback; static void* cras_main_message_add_handler_callback_data; static int cras_tm_create_timer_called; +static int cras_tm_cancel_timer_called; static int cras_a2dp_start_called; static int cras_a2dp_suspend_connected_device_called; static int cras_hfp_ag_remove_conflict_called; @@ -41,6 +42,8 @@ static int dbus_message_new_method_call_called; static const char* dbus_message_new_method_call_method; static struct cras_bt_device* cras_a2dp_connected_device_ret; static struct cras_bt_device* cras_a2dp_suspend_connected_device_dev; +static struct cras_timer* cras_tm_cancel_timer_arg; +static struct cras_timer* cras_tm_create_timer_ret; struct MockDBusMessage { int type; @@ -58,6 +61,7 @@ void ResetStubData() { cras_bt_io_try_remove_ret = 0; cras_main_message_send_msg = NULL; cras_tm_create_timer_called = 0; + cras_tm_cancel_timer_called = 0; cras_a2dp_start_called = 0; cras_a2dp_suspend_connected_device_called = 0; cras_hfp_ag_remove_conflict_called = 0; @@ -78,11 +82,12 @@ static void FreeMockDBusMessage(MockDBusMessage* head) { delete head; } -static struct MockDBusMessage* NewMockDBusConnectedMessage() { +static struct MockDBusMessage* NewMockDBusConnectedMessage(long connected) { MockDBusMessage* msg = new MockDBusMessage{DBUS_TYPE_ARRAY, NULL}; MockDBusMessage* dict = new MockDBusMessage{DBUS_TYPE_STRING, (void*)strdup("Connected")}; - MockDBusMessage* variant = new MockDBusMessage{DBUS_TYPE_BOOLEAN, (void*)1}; + MockDBusMessage* variant = + new MockDBusMessage{DBUS_TYPE_BOOLEAN, (void*)connected}; msg->recurse = dict; dict->next = new MockDBusMessage{DBUS_TYPE_INVALID, NULL}; @@ -216,7 +221,7 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpOnly) { cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); @@ -249,7 +254,7 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedHfpHspOnly) { cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); @@ -284,7 +289,7 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpHfpHsp) { cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); @@ -327,7 +332,7 @@ TEST_F(BtDeviceTestSuite, DevConnectedConflictCheck) { cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); cras_bt_device_audio_gateway_initialized(device); cras_bt_device_a2dp_configured(device); @@ -365,7 +370,7 @@ TEST_F(BtDeviceTestSuite, A2dpDropped) { cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); @@ -396,6 +401,73 @@ TEST_F(BtDeviceTestSuite, A2dpDropped) { FreeMockDBusMessage(msg_root); } +TEST_F(BtDeviceTestSuite, DevConnectDisconnectBackToBack) { + struct cras_bt_device* device; + struct MockDBusMessage *msg_root, *cur; + + ResetStubData(); + + device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); + EXPECT_NE((void*)NULL, device); + + cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); + cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); + cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + + cur = msg_root = NewMockDBusConnectedMessage(1); + cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); + EXPECT_EQ(1, cras_tm_create_timer_called); + EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); + FreeMockDBusMessage(msg_root); + + cras_bt_device_a2dp_configured(device); + cras_bt_device_audio_gateway_initialized(device); + + /* Expect suspend timer is scheduled. */ + cras_tm_create_timer_ret = reinterpret_cast<struct cras_timer*>(0x101); + cras_bt_device_notify_profile_dropped(device, + CRAS_BT_DEVICE_PROFILE_A2DP_SINK); + EXPECT_EQ(2, cras_tm_create_timer_called); + /* Another profile drop won't schedule another timer because one is + * already armed. */ + EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); + cras_bt_device_notify_profile_dropped(device, + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); + EXPECT_EQ(2, cras_tm_create_timer_called); + + cur = msg_root = NewMockDBusConnectedMessage(0); + cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); + + /* When BlueZ reports headset disconnection, cancel the pending timer. */ + EXPECT_EQ(cras_tm_cancel_timer_called, 1); + EXPECT_EQ(cras_tm_cancel_timer_arg, (void*)0x101); + FreeMockDBusMessage(msg_root); + + /* Headset connects again. */ + cur = msg_root = NewMockDBusConnectedMessage(1); + cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); + EXPECT_EQ(3, cras_tm_create_timer_called); + EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); + FreeMockDBusMessage(msg_root); + + /* Headset disconnects, later profile drop events shouldn't trigger + * suspend timer because headset is already in disconnected stats. + */ + cur = msg_root = NewMockDBusConnectedMessage(0); + cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); + FreeMockDBusMessage(msg_root); + + cras_tm_create_timer_called = 0; + cras_bt_device_notify_profile_dropped(device, + CRAS_BT_DEVICE_PROFILE_A2DP_SINK); + EXPECT_EQ(0, cras_tm_create_timer_called); + cras_bt_device_notify_profile_dropped(device, + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); + EXPECT_EQ(0, cras_tm_create_timer_called); + + cras_bt_device_remove(device); +} + TEST_F(BtDeviceTestSuite, ConnectionWatchTimeout) { struct cras_bt_device* device; struct MockDBusMessage *msg_root, *cur; @@ -409,7 +481,7 @@ TEST_F(BtDeviceTestSuite, ConnectionWatchTimeout) { cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); - cur = msg_root = NewMockDBusConnectedMessage(); + cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); @@ -594,10 +666,13 @@ struct cras_timer* cras_tm_create_timer(struct cras_tm* tm, cras_tm_create_timer_called++; cras_tm_create_timer_cb = cb; cras_tm_create_timer_cb_data = cb_data; - return NULL; + return cras_tm_create_timer_ret; } -void cras_tm_cancel_timer(struct cras_tm* tm, struct cras_timer* t) {} +void cras_tm_cancel_timer(struct cras_tm* tm, struct cras_timer* t) { + cras_tm_cancel_timer_called++; + cras_tm_cancel_timer_arg = t; +} DBusMessage* dbus_message_new_method_call(const char* destination, const char* path, |