Chromium Code Reviews| Index: content/renderer/bluetooth/bluetooth_dispatcher.cc |
| diff --git a/content/renderer/bluetooth/bluetooth_dispatcher.cc b/content/renderer/bluetooth/bluetooth_dispatcher.cc |
| index 32323903c772a967d1725d25526dbd5b6a2e42ae..e414f17173ccaf549aaf10cf9bca6fef367d1d6b 100644 |
| --- a/content/renderer/bluetooth/bluetooth_dispatcher.cc |
| +++ b/content/renderer/bluetooth/bluetooth_dispatcher.cc |
| @@ -14,6 +14,7 @@ |
| #include "third_party/WebKit/public/platform/WebPassOwnPtr.h" |
| #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothDevice.h" |
| #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothError.h" |
| +#include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristic.h" |
| #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTCharacteristicInit.h" |
| #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTRemoteServer.h" |
| #include "third_party/WebKit/public/platform/modules/bluetooth/WebBluetoothGATTService.h" |
| @@ -31,6 +32,8 @@ using blink::WebBluetoothScanFilter; |
| using blink::WebRequestDeviceOptions; |
| using blink::WebString; |
| using blink::WebVector; |
| +using NotificationsRequestType = |
| + content::BluetoothDispatcher::NotificationsRequestType; |
| struct BluetoothPrimaryServiceRequest { |
| BluetoothPrimaryServiceRequest( |
| @@ -62,6 +65,31 @@ struct BluetoothCharacteristicRequest { |
| scoped_ptr<blink::WebBluetoothGetCharacteristicCallbacks> callbacks; |
| }; |
| +// Struct that holds a pending Start/StopNotifications request. |
| +struct BluetoothNotificationsRequest { |
| + BluetoothNotificationsRequest( |
| + const std::string characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic, |
| + blink::WebBluetoothNotificationsCallbacks* callbacks, |
| + NotificationsRequestType type) |
| + : characteristic_instance_id(characteristic_instance_id), |
| + characteristic(characteristic), |
| + callbacks(callbacks), |
| + type(type) {} |
| + ~BluetoothNotificationsRequest() {} |
| + |
| + const std::string characteristic_instance_id; |
| + // The characteristic object is owned by the execution context on |
| + // the blink side which can destroy the object at any point. Since the |
| + // object implements ActiveDOMObject, the object calls Stop when is getting |
| + // destroyed, which in turn calls characteristicObjectRemoved. |
| + // characteristicObjectRemoved will null any pointers to the object |
| + // and queue a stop notifications request if necessary. |
| + blink::WebBluetoothGATTCharacteristic* characteristic; |
| + scoped_ptr<blink::WebBluetoothNotificationsCallbacks> callbacks; |
| + NotificationsRequestType type; |
| +}; |
| + |
| namespace content { |
| namespace { |
| @@ -143,6 +171,12 @@ void BluetoothDispatcher::OnMessageReceived(const IPC::Message& msg) { |
| OnWriteValueSuccess); |
| IPC_MESSAGE_HANDLER(BluetoothMsg_WriteCharacteristicValueError, |
| OnWriteValueError); |
| + IPC_MESSAGE_HANDLER(BluetoothMsg_StartNotificationsSuccess, |
| + OnStartNotificationsSuccess) |
| + IPC_MESSAGE_HANDLER(BluetoothMsg_StartNotificationsError, |
| + OnStartNotificationsError) |
| + IPC_MESSAGE_HANDLER(BluetoothMsg_StopNotificationsSuccess, |
| + OnStopNotificationsSuccess) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| DCHECK(handled) << "Unhandled message:" << msg.type(); |
| @@ -225,10 +259,221 @@ void BluetoothDispatcher::writeValue( |
| CurrentWorkerId(), request_id, characteristic_instance_id.utf8(), value)); |
| } |
| +void BluetoothDispatcher::startNotifications( |
| + const blink::WebString& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic, |
| + blink::WebBluetoothNotificationsCallbacks* callbacks) { |
| + int request_id = QueueNotificationRequest(characteristic_instance_id.utf8(), |
| + characteristic, callbacks, |
| + NotificationsRequestType::START); |
| + |
| + // The Notification subscription's state can change after a request |
| + // finishes. To avoid resolving with a soon-to-be-invalid state we queue |
| + // requests. |
| + if (PendingNotificationRequest(characteristic_instance_id.utf8())) { |
| + return; |
| + } |
| + |
| + processStartNotificationsRequest(request_id); |
| +} |
| + |
| +void BluetoothDispatcher::stopNotifications( |
| + const blink::WebString& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic, |
| + blink::WebBluetoothNotificationsCallbacks* callbacks) { |
| + int request_id = QueueNotificationRequest(characteristic_instance_id.utf8(), |
| + characteristic, callbacks, |
| + NotificationsRequestType::STOP); |
| + if (PendingNotificationRequest(characteristic_instance_id.utf8())) { |
| + return; |
| + } |
| + |
| + processStopNotificationsRequest(request_id); |
| +} |
| + |
| +void BluetoothDispatcher::characteristicObjectRemoved( |
| + const blink::WebString& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic) { |
| + // If there is a notification request pending then there are two posibilities: |
| + // 1. The subscription is going to be active. If this is the case, |
| + // we null the characteristic object. startNotificationsSuccess will take |
| + // care of stopping the notifications. |
| + // 2. The subscription is going to be inactive. If this is the case, we |
| + // don't need to to anything since the notification is stopped already. |
| + if (PendingNotificationRequest(characteristic_instance_id.utf8())) { |
| + for (IDMap<BluetoothNotificationsRequest, IDMapOwnPointer>::iterator iter( |
| + &pending_notifications_requests_); |
| + !iter.IsAtEnd(); iter.Advance()) { |
| + iter.GetCurrentValue()->characteristic = nullptr; |
| + } |
| + return; |
| + } |
| + |
| + // If there are no notification requests pending then there are three |
| + // possibilities after removing this characteristic: |
| + // 1. The subscription was inactive already. |
| + // 2. The subscription will become inactive. |
|
scheib
2015/10/01 22:05:23
Case 2 is only if another frame holds the subscrip
ortuno
2015/10/03 04:03:03
Not sure I follow: Case 2 happens when this is the
scheib
2015/10/04 01:35:58
Add detail to the comments somehow explaining case
ortuno
2015/10/06 02:38:43
Done.
|
| + // 3. The subscription will still be active. |
| + |
| + if (!HasActiveNotificationSubscription(characteristic_instance_id.utf8())) { |
| + return; |
| + } |
| + |
| + // For 2 and 3. We simply call processStopNotificationsRequest which will take |
| + // care of stopping the notifications if the subscription becomes inactive. |
|
scheib
2015/10/01 22:05:23
Perhaps:
For 3 calling processStopNotificationsReq
ortuno
2015/10/03 04:03:03
Done.
|
| + processStopNotificationsRequest(QueueNotificationRequest( |
| + characteristic_instance_id.utf8(), characteristic, |
|
scheib
2015/10/01 22:05:23
characteristic should be nullptr
ortuno
2015/10/03 04:03:03
We still need to remove the characteristic from th
scheib
2015/10/04 01:35:58
OK, yeah, this is complicated enough that's not ob
ortuno
2015/10/06 02:38:43
Done.
|
| + nullptr /* callbacks */, NotificationsRequestType::STOP)); |
| +} |
| + |
| void BluetoothDispatcher::WillStopCurrentWorkerThread() { |
| delete this; |
| } |
| +int BluetoothDispatcher::QueueNotificationRequest( |
| + const std::string& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic, |
| + blink::WebBluetoothNotificationsCallbacks* callbacks, |
| + NotificationsRequestType type) { |
| + int request_id = |
| + pending_notifications_requests_.Add(new BluetoothNotificationsRequest( |
| + characteristic_instance_id, characteristic, callbacks, type)); |
| + queued_notification_requests_[characteristic_instance_id].push(request_id); |
| + |
| + return request_id; |
| +} |
| + |
| +void BluetoothDispatcher::ProcessQueuedNotificationRequests(int request_id) { |
| + BluetoothNotificationsRequest* old_request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + |
| + auto queue_iter = queued_notification_requests_.find( |
| + old_request->characteristic_instance_id); |
| + |
| + CHECK(queue_iter != queued_notification_requests_.end()); |
| + |
| + std::queue<int>& request_queue = queue_iter->second; |
| + |
| + DCHECK(request_queue.front() == request_id); |
| + |
| + // Remove old request and clean map if necessary. |
| + request_queue.pop(); |
| + pending_notifications_requests_.Remove(request_id); |
| + if (request_queue.empty()) { |
| + queued_notification_requests_.erase(queue_iter); |
| + return; |
| + } |
| + |
| + int next_request_id = request_queue.front(); |
| + BluetoothNotificationsRequest* next_request = |
| + pending_notifications_requests_.Lookup(next_request_id); |
| + |
| + switch (next_request->type) { |
| + case NotificationsRequestType::START: |
| + processStartNotificationsRequest(next_request_id); |
| + return; |
| + case NotificationsRequestType::STOP: |
| + processStopNotificationsRequest(next_request_id); |
| + return; |
| + } |
| +} |
| + |
| +bool BluetoothDispatcher::PendingNotificationRequest( |
| + const std::string& characteristic_instance_id) { |
| + return queued_notification_requests_.find(characteristic_instance_id) != |
| + queued_notification_requests_.end() && |
| + (queued_notification_requests_[characteristic_instance_id].size() > 1); |
| +} |
| + |
| +bool BluetoothDispatcher::HasActiveNotificationSubscription( |
| + const std::string& characteristic_instance_id) { |
| + return ContainsKey(active_notification_subscriptions_, |
| + characteristic_instance_id); |
| +} |
| + |
| +void BluetoothDispatcher::AddToActiveNotificationSubscriptions( |
| + const std::string& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic) { |
| + active_notification_subscriptions_[characteristic_instance_id].insert( |
| + characteristic); |
| +} |
| + |
| +bool BluetoothDispatcher::RemoveFromActiveNotificationSubscriptions( |
| + const std::string& characteristic_instance_id, |
| + blink::WebBluetoothGATTCharacteristic* characteristic) { |
| + auto active_map = |
| + active_notification_subscriptions_.find(characteristic_instance_id); |
| + |
| + if (active_map == active_notification_subscriptions_.end()) { |
| + return false; |
| + } |
| + |
| + active_map->second.erase(characteristic); |
| + |
| + if (active_map->second.empty()) { |
| + active_notification_subscriptions_.erase(active_map); |
| + DCHECK(!HasActiveNotificationSubscription(characteristic_instance_id)); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +void BluetoothDispatcher::processStartNotificationsRequest(int request_id) { |
| + BluetoothNotificationsRequest* request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + const std::string& characteristic_instance_id = |
| + request->characteristic_instance_id; |
| + blink::WebBluetoothGATTCharacteristic* characteristic = |
|
scheib
2015/10/01 22:05:23
characteristic may be nullptr due to characteristi
ortuno
2015/10/03 04:03:03
That's tricky and seems like a question for Jeffre
scheib
2015/10/04 01:35:58
Can wait for J if you like, but below on Start suc
ortuno
2015/10/06 02:38:43
Chatted offline. We will send the request for now
|
| + request->characteristic; |
| + blink::WebBluetoothNotificationsCallbacks* callbacks = |
| + request->callbacks.get(); |
| + |
| + // If an object is already subscribed to notifications from the characteristic |
| + // no need to send an IPC again. |
| + if (HasActiveNotificationSubscription(characteristic_instance_id)) { |
| + AddToActiveNotificationSubscriptions(characteristic_instance_id, |
| + characteristic); |
| + callbacks->onSuccess(); |
| + |
| + ProcessQueuedNotificationRequests(request_id); |
| + return; |
| + } |
| + Send(new BluetoothHostMsg_StartNotifications(CurrentWorkerId(), request_id, |
| + characteristic_instance_id)); |
| +} |
| + |
| +void BluetoothDispatcher::processStopNotificationsRequest(int request_id) { |
| + // The Notification subscription's state can change after a request |
| + // finishes. To avoid resolving with a soon-to-be-invalid state we queue |
| + // requests. |
| + BluetoothNotificationsRequest* request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + const std::string& characteristic_instance_id = |
| + request->characteristic_instance_id; |
| + blink::WebBluetoothGATTCharacteristic* characteristic = |
| + request->characteristic; |
| + blink::WebBluetoothNotificationsCallbacks* callbacks = |
| + request->callbacks.get(); |
| + |
| + // If removing turns the subscription inactive then stop. |
| + if (RemoveFromActiveNotificationSubscriptions(characteristic_instance_id, |
| + characteristic)) { |
|
scheib
2015/10/01 22:05:23
May be removing from here with characteristic == n
ortuno
2015/10/03 04:03:03
That's why we pass the characteristic and not null
scheib
2015/10/04 01:35:58
We do? I don't see WebBluetoothCharacteristics acc
ortuno
2015/10/06 02:38:43
It's not used yet. WebBluetoothCharacteristics is
|
| + Send(new BluetoothHostMsg_StopNotifications(CurrentWorkerId(), request_id, |
| + characteristic_instance_id)); |
| + return; |
| + } |
| + // An object is destroyed 1) during a start request or 2) after a start |
|
scheib
2015/10/01 22:05:23
Reform comment more like this for readability:
Po
ortuno
2015/10/03 04:03:03
Done.
scheib
2015/10/04 01:35:58
I think you can delete the first 7 comment lines f
ortuno
2015/10/06 02:38:43
Done.
|
| + // request. If 1), then OnStartNotificationsSuccess will queue a stop with |
| + // callback = nullptr and characteristic = nullptr which will eventually call |
| + // processStopNotificationsRequest. If 2), characteristic needs to be removed |
| + // from active_notifications_subscriptions so onCharacteristicObjectRemoved |
| + // will call processStopNotification with callback = nullptr only. |
| + if (callbacks != nullptr) { |
| + callbacks->onSuccess(); |
| + } |
| + ProcessQueuedNotificationRequests(request_id); |
| +} |
| + |
| void BluetoothDispatcher::OnRequestDeviceSuccess( |
| int thread_id, |
| int request_id, |
| @@ -386,4 +631,78 @@ void BluetoothDispatcher::OnWriteValueError(int thread_id, |
| pending_write_value_requests_.Remove(request_id); |
| } |
| +void BluetoothDispatcher::OnStartNotificationsSuccess(int thread_id, |
| + int request_id) { |
| + DCHECK(pending_notifications_requests_.Lookup(request_id)) << request_id; |
| + |
| + BluetoothNotificationsRequest* request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + |
| + DCHECK(queued_notification_requests_[request->characteristic_instance_id] |
| + .front() == request_id); |
| + |
| + // We only send an IPC for inactive notifications. |
| + DCHECK( |
| + !HasActiveNotificationSubscription(request->characteristic_instance_id)); |
| + |
| + AddToActiveNotificationSubscriptions(request->characteristic_instance_id, |
| + request->characteristic); |
| + |
| + // The object requesting the notification could have been destroyed |
| + // while waiting for the subscription. characteristicRemoved |
| + // nulls the characteristic when the corresponding js object gets destroyed. |
| + // Since there could be other characteristics waiting to subscribe, we queue |
|
scheib
2015/10/01 22:05:23
I understand sending a Stop when the object is des
ortuno
2015/10/03 04:03:03
// Frame 1
char.startNotifications()
// char gets
scheib
2015/10/04 01:35:58
Improve comments in code to address the uncertaint
ortuno
2015/10/06 02:38:43
Done.
|
| + // a stop. |
| + if (request->characteristic == nullptr) { |
| + QueueNotificationRequest( |
| + request->characteristic_instance_id, nullptr /* characteristic */, |
| + nullptr /* callbacks */, NotificationsRequestType::STOP); |
| + } |
| + |
| + request->callbacks->onSuccess(); |
| + |
| + ProcessQueuedNotificationRequests(request_id); |
| +} |
| + |
| +void BluetoothDispatcher::OnStartNotificationsError(int thread_id, |
| + int request_id, |
| + WebBluetoothError error) { |
| + DCHECK(pending_notifications_requests_.Lookup(request_id)) << request_id; |
| + |
| + BluetoothNotificationsRequest* request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + |
| + DCHECK(queued_notification_requests_[request->characteristic_instance_id] |
| + .front() == request_id); |
| + |
| + // We only send an IPC for inactive notifications. |
| + DCHECK( |
| + !HasActiveNotificationSubscription(request->characteristic_instance_id)); |
| + |
| + request->callbacks->onError(error); |
| + |
| + ProcessQueuedNotificationRequests(request_id); |
| +} |
| + |
| +void BluetoothDispatcher::OnStopNotificationsSuccess(int thread_id, |
| + int request_id) { |
| + DCHECK(pending_notifications_requests_.Lookup(request_id)) << request_id; |
| + |
| + BluetoothNotificationsRequest* request = |
| + pending_notifications_requests_.Lookup(request_id); |
| + |
| + DCHECK(queued_notification_requests_[request->characteristic_instance_id] |
| + .front() == request_id); |
| + |
| + // We only send an IPC for inactive notifications. |
| + DCHECK( |
| + !HasActiveNotificationSubscription(request->characteristic_instance_id)); |
| + |
| + if (request->callbacks != nullptr) { |
| + request->callbacks->onSuccess(); |
| + } |
| + |
| + ProcessQueuedNotificationRequests(request_id); |
| +} |
| + |
| } // namespace content |