Fix race condition in bluez discovery operations

This guards StartDiscovery from happening when StopDiscovery is still in
progress and guards StopDiscovery from happening when StartDiscovery is
still in progress.

BUG=chromium:765392,chromium:738237
TEST=Add intentional delay in kernel's MGMT_OP_START_DISCOVERY so race
condition can be reproduced and check that bluez returns error Busy
instead of letting the race condition happen.
CQ-DEPEND=CL:677466

Change-Id: I29da033aa9139312f7ef09727f31154a25650d49
Reviewed-on: https://chromium-review.googlesource.com/677467
Commit-Ready: Sonny Sasaka <sonnysasaka@chromium.org>
Tested-by: Sonny Sasaka <sonnysasaka@chromium.org>
Reviewed-by: Miao-chen Chou <mcchou@chromium.org>
Reviewed-by: Dmitry Grinberg <dmitrygr@google.com>
(cherry picked from commit 4baf6005b854802bfea07c8142179dbf31ea7561)
Reviewed-on: https://chromium-review.googlesource.com/679114
Reviewed-by: Sonny Sasaka <sonnysasaka@chromium.org>
Commit-Queue: Sonny Sasaka <sonnysasaka@chromium.org>
diff --git a/src/adapter.c b/src/adapter.c
index 83f38dd..b89d3fe 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -215,6 +215,10 @@
 	bool filtered_discovery;	/* we are doing filtered discovery */
 	bool no_scan_restart_delay;	/* when this flag is set, restart scan
 					 * without delay */
+	/* indicates whether there is start discovery operation in progress */
+	bool start_discovery_in_progress;
+	/* indicates whether there is stop discovery operation in progress */
+	bool stop_discovery_in_progress;
 	uint8_t discovery_type;		/* current active discovery type */
 	uint8_t discovery_enable;	/* discovery enabled/disabled */
 	bool discovery_suspended;	/* discovery has been suspended */
@@ -1450,6 +1454,8 @@
 
 	DBG("status 0x%02x", status);
 
+	adapter->start_discovery_in_progress = false;
+
 	if (length < sizeof(*rp)) {
 		btd_error(adapter->dev_id,
 			"Wrong size of start discovery return parameters");
@@ -1551,6 +1557,7 @@
 		struct mgmt_cp_start_discovery cp;
 
 		cp.type = new_type;
+		adapter->start_discovery_in_progress = true;
 		mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
 				adapter->dev_id, sizeof(cp), &cp,
 				start_discovery_complete, adapter, NULL);
@@ -1563,6 +1570,7 @@
 	DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d",
 				sd_cp->rssi, sd_cp->type, sd_cp->uuid_count);
 
+	adapter->start_discovery_in_progress = true;
 	mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
 		  adapter->dev_id, sizeof(*sd_cp) + sd_cp->uuid_count * 16,
 		  sd_cp, start_discovery_complete, adapter, NULL);
@@ -1727,6 +1735,8 @@
 
 	DBG("status 0x%02x", status);
 
+	adapter->stop_discovery_in_progress = false;
+
 	if (status == MGMT_STATUS_SUCCESS) {
 		adapter->discovery_type = 0x00;
 		adapter->discovery_enable = 0x00;
@@ -2082,6 +2092,7 @@
 
 	cp.type = adapter->discovery_type;
 
+	adapter->stop_discovery_in_progress = true;
 	mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
 				adapter->dev_id, sizeof(cp), &cp,
 				stop_discovery_complete, adapter, NULL);
@@ -2136,6 +2147,15 @@
 		return btd_error_busy(msg);
 
 	/*
+	 * We are still waiting for mgmt response from kernel about the
+	 * previous stop discovery request.
+	 */
+	if (adapter->stop_discovery_in_progress) {
+		DBG("error: stop discovery in progress");
+		return btd_error_busy(msg);
+	}
+
+	/*
 	 * If there was pre-set filter, just reconnect it to discovery_list,
 	 * and trigger scan.
 	 */
@@ -2420,6 +2440,15 @@
 	if (!list)
 		return btd_error_failed(msg, "No discovery started");
 
+	/*
+	 * We are still waiting for mgmt response from kernel about the
+	 * previous start discovery request.
+	 */
+	if (adapter->start_discovery_in_progress) {
+		DBG("error: start discovery in progress");
+		return btd_error_busy(msg);
+	}
+
 	client = list->data;
 
 	cp.type = adapter->discovery_type;
@@ -2449,6 +2478,7 @@
 		return dbus_message_new_method_return(msg);
 	}
 
+	adapter->stop_discovery_in_progress = true;
 	mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
 				adapter->dev_id, sizeof(cp), &cp,
 				stop_discovery_complete, adapter, NULL);