rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 1 | // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "device/bluetooth/bluetooth_remote_gatt_characteristic.h" |
| 6 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 7 | #include "base/bind.h" |
| 8 | #include "base/location.h" |
| 9 | #include "base/single_thread_task_runner.h" |
| 10 | #include "base/threading/thread_task_runner_handle.h" |
| 11 | #include "device/bluetooth/bluetooth_gatt_notify_session.h" |
rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 12 | #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h" |
| 13 | |
| 14 | namespace device { |
| 15 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 16 | BluetoothRemoteGattCharacteristic::BluetoothRemoteGattCharacteristic() |
| 17 | : weak_ptr_factory_(this) {} |
rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 18 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 19 | BluetoothRemoteGattCharacteristic::~BluetoothRemoteGattCharacteristic() { |
| 20 | while (!pending_notify_commands_.empty()) { |
| 21 | pending_notify_commands_.front()->Cancel(); |
| 22 | } |
| 23 | } |
rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 24 | |
| 25 | std::vector<BluetoothRemoteGattDescriptor*> |
| 26 | BluetoothRemoteGattCharacteristic::GetDescriptorsByUUID( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 27 | const BluetoothUUID& uuid) const { |
rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 28 | std::vector<BluetoothRemoteGattDescriptor*> descriptors; |
| 29 | for (BluetoothRemoteGattDescriptor* descriptor : GetDescriptors()) { |
| 30 | if (descriptor->GetUUID() == uuid) { |
| 31 | descriptors.push_back(descriptor); |
| 32 | } |
| 33 | } |
| 34 | return descriptors; |
| 35 | } |
| 36 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 37 | base::WeakPtr<device::BluetoothRemoteGattCharacteristic> |
| 38 | BluetoothRemoteGattCharacteristic::GetWeakPtr() { |
| 39 | return weak_ptr_factory_.GetWeakPtr(); |
| 40 | } |
| 41 | |
| 42 | bool BluetoothRemoteGattCharacteristic::IsNotifying() const { |
| 43 | return !notify_sessions_.empty(); |
| 44 | } |
| 45 | |
| 46 | BluetoothRemoteGattCharacteristic::NotifySessionCommand::NotifySessionCommand( |
| 47 | const ExecuteCallback& execute_callback, |
| 48 | const base::Closure& cancel_callback) |
| 49 | : execute_callback_(execute_callback), cancel_callback_(cancel_callback) {} |
| 50 | |
| 51 | BluetoothRemoteGattCharacteristic::NotifySessionCommand:: |
Chris Watkins | 52eb8689 | 2017-11-30 06:52:26 | [diff] [blame] | 52 | ~NotifySessionCommand() = default; |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 53 | |
| 54 | void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Execute() { |
| 55 | execute_callback_.Run(COMMAND_NONE, RESULT_SUCCESS, |
| 56 | BluetoothRemoteGattService::GATT_ERROR_UNKNOWN); |
| 57 | } |
| 58 | |
| 59 | void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Execute( |
| 60 | Type previous_command_type, |
| 61 | Result previous_command_result, |
| 62 | BluetoothRemoteGattService::GattErrorCode previous_command_error_code) { |
| 63 | execute_callback_.Run(previous_command_type, previous_command_result, |
| 64 | previous_command_error_code); |
| 65 | } |
| 66 | |
| 67 | void BluetoothRemoteGattCharacteristic::NotifySessionCommand::Cancel() { |
| 68 | cancel_callback_.Run(); |
| 69 | } |
| 70 | |
| 71 | void BluetoothRemoteGattCharacteristic::StartNotifySession( |
| 72 | const NotifySessionCallback& callback, |
| 73 | const ErrorCallback& error_callback) { |
| 74 | NotifySessionCommand* command = new NotifySessionCommand( |
| 75 | base::Bind(&BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession, |
| 76 | GetWeakPtr(), callback, error_callback), |
| 77 | base::Bind(&BluetoothRemoteGattCharacteristic::CancelStartNotifySession, |
| 78 | GetWeakPtr(), |
| 79 | base::Bind(error_callback, |
| 80 | BluetoothRemoteGattService::GATT_ERROR_FAILED))); |
| 81 | |
| 82 | pending_notify_commands_.push(std::unique_ptr<NotifySessionCommand>(command)); |
| 83 | if (pending_notify_commands_.size() == 1) { |
| 84 | command->Execute(); |
| 85 | } |
| 86 | } |
| 87 | |
Jan Wilken Dörrie | a7786fe | 2018-04-12 12:31:05 | [diff] [blame] | 88 | bool BluetoothRemoteGattCharacteristic::WriteWithoutResponse( |
| 89 | base::span<const uint8_t> value) { |
| 90 | NOTIMPLEMENTED(); |
| 91 | return false; |
| 92 | } |
| 93 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 94 | void BluetoothRemoteGattCharacteristic::ExecuteStartNotifySession( |
| 95 | NotifySessionCallback callback, |
| 96 | ErrorCallback error_callback, |
| 97 | NotifySessionCommand::Type previous_command_type, |
| 98 | NotifySessionCommand::Result previous_command_result, |
| 99 | BluetoothRemoteGattService::GattErrorCode previous_command_error_code) { |
| 100 | // If the command that was resolved immediately before this command was run, |
| 101 | // this command should be resolved with the same result. |
| 102 | if (previous_command_type == NotifySessionCommand::COMMAND_START) { |
| 103 | if (previous_command_result == NotifySessionCommand::RESULT_SUCCESS) { |
| 104 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 105 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 106 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 107 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess, |
| 108 | GetWeakPtr(), callback)); |
| 109 | return; |
| 110 | } else { |
| 111 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 112 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 113 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 114 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionError, |
| 115 | GetWeakPtr(), error_callback, previous_command_error_code)); |
| 116 | return; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // Check that the characteristic supports either notifications or |
| 121 | // indications. |
| 122 | Properties properties = GetProperties(); |
| 123 | bool hasNotify = (properties & PROPERTY_NOTIFY) != 0; |
| 124 | bool hasIndicate = (properties & PROPERTY_INDICATE) != 0; |
| 125 | |
| 126 | if (!hasNotify && !hasIndicate) { |
| 127 | LOG(ERROR) << "Characteristic needs NOTIFY or INDICATE"; |
| 128 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 129 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 130 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 131 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionError, |
| 132 | GetWeakPtr(), error_callback, |
| 133 | BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED)); |
| 134 | return; |
| 135 | } |
| 136 | |
| 137 | // If the characteristic is already notifying, then we don't need to |
| 138 | // subscribe again. All we need to do is call the success callback, which |
| 139 | // will create and return a session object to the caller. |
| 140 | if (IsNotifying()) { |
| 141 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 142 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 143 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 144 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess, |
| 145 | GetWeakPtr(), callback)); |
| 146 | return; |
| 147 | } |
| 148 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 149 | // Find the Client Characteristic Configuration descriptor. |
| 150 | std::vector<BluetoothRemoteGattDescriptor*> ccc_descriptor = |
| 151 | GetDescriptorsByUUID(BluetoothRemoteGattDescriptor:: |
| 152 | ClientCharacteristicConfigurationUuid()); |
| 153 | |
| 154 | if (ccc_descriptor.size() != 1u) { |
| 155 | LOG(ERROR) << "Found " << ccc_descriptor.size() |
| 156 | << " client characteristic configuration descriptors."; |
| 157 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 158 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 159 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 160 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionError, |
| 161 | GetWeakPtr(), error_callback, |
| 162 | (ccc_descriptor.size() == 0) |
| 163 | ? BluetoothRemoteGattService::GATT_ERROR_NOT_SUPPORTED |
| 164 | : BluetoothRemoteGattService::GATT_ERROR_FAILED)); |
| 165 | return; |
| 166 | } |
| 167 | |
| 168 | // Pass the Client Characteristic Configuration descriptor to |
| 169 | // SubscribetoNotifications, which will write the correct value to it, and |
| 170 | // do whatever else is needed to get the notifications flowing. |
| 171 | SubscribeToNotifications( |
| 172 | ccc_descriptor[0], |
| 173 | base::Bind( |
| 174 | &BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess, |
| 175 | GetWeakPtr(), callback), |
| 176 | base::Bind(&BluetoothRemoteGattCharacteristic::OnStartNotifySessionError, |
| 177 | GetWeakPtr(), error_callback)); |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | void BluetoothRemoteGattCharacteristic::CancelStartNotifySession( |
| 181 | base::Closure callback) { |
| 182 | std::unique_ptr<NotifySessionCommand> command = |
| 183 | std::move(pending_notify_commands_.front()); |
| 184 | pending_notify_commands_.pop(); |
| 185 | callback.Run(); |
| 186 | } |
| 187 | |
| 188 | void BluetoothRemoteGattCharacteristic::OnStartNotifySessionSuccess( |
| 189 | NotifySessionCallback callback) { |
| 190 | std::unique_ptr<NotifySessionCommand> command = |
| 191 | std::move(pending_notify_commands_.front()); |
| 192 | |
| 193 | std::unique_ptr<device::BluetoothGattNotifySession> notify_session( |
| 194 | new BluetoothGattNotifySession(weak_ptr_factory_.GetWeakPtr())); |
| 195 | notify_sessions_.insert(notify_session.get()); |
| 196 | callback.Run(std::move(notify_session)); |
| 197 | |
| 198 | pending_notify_commands_.pop(); |
| 199 | if (!pending_notify_commands_.empty()) { |
| 200 | pending_notify_commands_.front()->Execute( |
| 201 | NotifySessionCommand::COMMAND_START, |
| 202 | NotifySessionCommand::RESULT_SUCCESS, |
| 203 | BluetoothRemoteGattService::GATT_ERROR_UNKNOWN); |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | void BluetoothRemoteGattCharacteristic::OnStartNotifySessionError( |
| 208 | ErrorCallback error_callback, |
| 209 | BluetoothRemoteGattService::GattErrorCode error) { |
| 210 | std::unique_ptr<NotifySessionCommand> command = |
| 211 | std::move(pending_notify_commands_.front()); |
| 212 | |
| 213 | error_callback.Run(error); |
| 214 | |
| 215 | pending_notify_commands_.pop(); |
| 216 | if (!pending_notify_commands_.empty()) { |
| 217 | pending_notify_commands_.front()->Execute( |
| 218 | NotifySessionCommand::COMMAND_START, NotifySessionCommand::RESULT_ERROR, |
| 219 | error); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | void BluetoothRemoteGattCharacteristic::StopNotifySession( |
| 224 | BluetoothGattNotifySession* session, |
| 225 | const base::Closure& callback) { |
| 226 | NotifySessionCommand* command = new NotifySessionCommand( |
| 227 | base::Bind(&BluetoothRemoteGattCharacteristic::ExecuteStopNotifySession, |
| 228 | GetWeakPtr(), session, callback), |
jlebel | 0b6e266 | 2017-01-19 18:20:47 | [diff] [blame] | 229 | base::Bind(&BluetoothRemoteGattCharacteristic::CancelStopNotifySession, |
| 230 | GetWeakPtr(), callback)); |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 231 | |
| 232 | pending_notify_commands_.push(std::unique_ptr<NotifySessionCommand>(command)); |
| 233 | if (pending_notify_commands_.size() == 1) { |
| 234 | command->Execute(); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | void BluetoothRemoteGattCharacteristic::ExecuteStopNotifySession( |
| 239 | BluetoothGattNotifySession* session, |
| 240 | base::Closure callback, |
| 241 | NotifySessionCommand::Type previous_command_type, |
| 242 | NotifySessionCommand::Result previous_command_result, |
| 243 | BluetoothRemoteGattService::GattErrorCode previous_command_error_code) { |
| 244 | std::set<BluetoothGattNotifySession*>::iterator session_iterator = |
| 245 | notify_sessions_.find(session); |
| 246 | |
| 247 | // If the session does not even belong to this characteristic, we return an |
| 248 | // error right away. |
| 249 | if (session_iterator == notify_sessions_.end()) { |
| 250 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 251 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 252 | base::BindOnce( |
| 253 | &BluetoothRemoteGattCharacteristic::OnStopNotifySessionError, |
| 254 | GetWeakPtr(), session, callback, |
| 255 | BluetoothRemoteGattService::GATT_ERROR_FAILED)); |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 256 | return; |
| 257 | } |
| 258 | |
| 259 | // If there are more active sessions, then we return right away. |
| 260 | if (notify_sessions_.size() > 1) { |
| 261 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 262 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 263 | base::BindOnce( |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 264 | &BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess, |
| 265 | GetWeakPtr(), session, callback)); |
| 266 | return; |
| 267 | } |
| 268 | |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 269 | // Find the Client Characteristic Configuration descriptor. |
| 270 | std::vector<BluetoothRemoteGattDescriptor*> ccc_descriptor = |
| 271 | GetDescriptorsByUUID(BluetoothRemoteGattDescriptor:: |
| 272 | ClientCharacteristicConfigurationUuid()); |
| 273 | |
| 274 | if (ccc_descriptor.size() != 1u) { |
| 275 | LOG(ERROR) << "Found " << ccc_descriptor.size() |
| 276 | << " client characteristic configuration descriptors."; |
| 277 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 278 | FROM_HERE, |
tzik | fa0c131 | 2018-03-07 04:44:52 | [diff] [blame] | 279 | base::BindOnce( |
| 280 | &BluetoothRemoteGattCharacteristic::OnStopNotifySessionError, |
| 281 | GetWeakPtr(), session, callback, |
| 282 | BluetoothRemoteGattService::GATT_ERROR_FAILED)); |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 283 | return; |
| 284 | } |
| 285 | |
| 286 | UnsubscribeFromNotifications( |
| 287 | ccc_descriptor[0], |
| 288 | base::Bind(&BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess, |
| 289 | GetWeakPtr(), session, callback), |
| 290 | base::Bind(&BluetoothRemoteGattCharacteristic::OnStopNotifySessionError, |
| 291 | GetWeakPtr(), session, callback)); |
tommyt | ad8462b | 2016-08-22 08:09:39 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | void BluetoothRemoteGattCharacteristic::CancelStopNotifySession( |
| 295 | base::Closure callback) { |
| 296 | std::unique_ptr<NotifySessionCommand> command = |
| 297 | std::move(pending_notify_commands_.front()); |
| 298 | pending_notify_commands_.pop(); |
| 299 | callback.Run(); |
| 300 | } |
| 301 | |
| 302 | void BluetoothRemoteGattCharacteristic::OnStopNotifySessionSuccess( |
| 303 | BluetoothGattNotifySession* session, |
| 304 | base::Closure callback) { |
| 305 | std::unique_ptr<NotifySessionCommand> command = |
| 306 | std::move(pending_notify_commands_.front()); |
| 307 | |
| 308 | notify_sessions_.erase(session); |
| 309 | |
| 310 | callback.Run(); |
| 311 | |
| 312 | pending_notify_commands_.pop(); |
| 313 | if (!pending_notify_commands_.empty()) { |
| 314 | pending_notify_commands_.front()->Execute( |
| 315 | NotifySessionCommand::COMMAND_STOP, |
| 316 | NotifySessionCommand::RESULT_SUCCESS, |
| 317 | BluetoothRemoteGattService::GATT_ERROR_UNKNOWN); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | void BluetoothRemoteGattCharacteristic::OnStopNotifySessionError( |
| 322 | BluetoothGattNotifySession* session, |
| 323 | base::Closure callback, |
| 324 | BluetoothRemoteGattService::GattErrorCode error) { |
| 325 | std::unique_ptr<NotifySessionCommand> command = |
| 326 | std::move(pending_notify_commands_.front()); |
| 327 | |
| 328 | notify_sessions_.erase(session); |
| 329 | |
| 330 | callback.Run(); |
| 331 | |
| 332 | pending_notify_commands_.pop(); |
| 333 | if (!pending_notify_commands_.empty()) { |
| 334 | pending_notify_commands_.front()->Execute( |
| 335 | NotifySessionCommand::COMMAND_STOP, NotifySessionCommand::RESULT_ERROR, |
| 336 | error); |
| 337 | } |
| 338 | } |
| 339 | |
rkc | 12223975 | 2016-04-20 23:59:08 | [diff] [blame] | 340 | } // namespace device |