Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(246)

Side by Side Diff: device/bluetooth/bluetooth_audio_sink_bluez.cc

Issue 1898643002: Refactor device::BluetoothGattXXX classes to split into remote/local. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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_audio_sink_bluez.h"
6
7 #include <unistd.h>
8
9 #include <algorithm>
10 #include <sstream>
11 #include <string>
12 #include <vector>
13
14 #include "base/debug/stack_trace.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "dbus/message.h"
18 #include "device/bluetooth/bluetooth_adapter_bluez.h"
19 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
20
21 using dbus::ObjectPath;
22 using device::BluetoothAudioSink;
23
24 namespace {
25
26 // TODO(mcchou): Add the constant to dbus/service_constants.h.
27 const char kBluetoothAudioSinkServicePath[] = "/org/chromium/AudioSink";
28
29 const int kInvalidFd = -1;
30 const uint16_t kInvalidReadMtu = 0;
31 const uint16_t kInvalidWriteMtu = 0;
32
33 ObjectPath GenerateEndpointPath() {
34 static unsigned int sequence_number = 0;
35 ++sequence_number;
36 std::stringstream path;
37 path << kBluetoothAudioSinkServicePath << "/endpoint" << sequence_number;
38 return ObjectPath(path.str());
39 }
40
41 std::string StateToString(const BluetoothAudioSink::State& state) {
42 switch (state) {
43 case BluetoothAudioSink::STATE_INVALID:
44 return "invalid";
45 case BluetoothAudioSink::STATE_DISCONNECTED:
46 return "disconnected";
47 case BluetoothAudioSink::STATE_IDLE:
48 return "idle";
49 case BluetoothAudioSink::STATE_PENDING:
50 return "pending";
51 case BluetoothAudioSink::STATE_ACTIVE:
52 return "active";
53 default:
54 return "unknown";
55 }
56 }
57
58 std::string ErrorCodeToString(const BluetoothAudioSink::ErrorCode& error_code) {
59 switch (error_code) {
60 case BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM:
61 return "unsupported platform";
62 case BluetoothAudioSink::ERROR_INVALID_ADAPTER:
63 return "invalid adapter";
64 case BluetoothAudioSink::ERROR_NOT_REGISTERED:
65 return "not registered";
66 case BluetoothAudioSink::ERROR_NOT_UNREGISTERED:
67 return "not unregistered";
68 default:
69 return "unknown";
70 }
71 }
72
73 // A dummy error callback for calling Unregister() in destructor.
74 void UnregisterErrorCallback(device::BluetoothAudioSink::ErrorCode error_code) {
75 VLOG(1) << "UnregisterErrorCallback - " << ErrorCodeToString(error_code)
76 << "(" << error_code << ")";
77 }
78
79 } // namespace
80
81 namespace bluez {
82
83 BluetoothAudioSinkBlueZ::BluetoothAudioSinkBlueZ(
84 scoped_refptr<device::BluetoothAdapter> adapter)
85 : state_(BluetoothAudioSink::STATE_INVALID),
86 volume_(BluetoothAudioSink::kInvalidVolume),
87 read_mtu_(kInvalidReadMtu),
88 write_mtu_(kInvalidWriteMtu),
89 read_has_failed_(false),
90 adapter_(adapter),
91 weak_ptr_factory_(this) {
92 VLOG(1) << "BluetoothAudioSinkBlueZ created";
93
94 CHECK(adapter_.get());
95 CHECK(adapter_->IsPresent());
96 CHECK(bluez::BluezDBusManager::IsInitialized());
97
98 adapter_->AddObserver(this);
99
100 bluez::BluetoothMediaClient* media =
101 bluez::BluezDBusManager::Get()->GetBluetoothMediaClient();
102 CHECK(media);
103 media->AddObserver(this);
104
105 bluez::BluetoothMediaTransportClient* transport =
106 bluez::BluezDBusManager::Get()->GetBluetoothMediaTransportClient();
107 CHECK(transport);
108 transport->AddObserver(this);
109
110 StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED);
111 }
112
113 BluetoothAudioSinkBlueZ::~BluetoothAudioSinkBlueZ() {
114 VLOG(1) << "BluetoothAudioSinkBlueZ destroyed";
115
116 DCHECK(adapter_.get());
117
118 if (state_ != BluetoothAudioSink::STATE_INVALID && media_endpoint_.get()) {
119 Unregister(base::Bind(&base::DoNothing),
120 base::Bind(&UnregisterErrorCallback));
121 }
122
123 adapter_->RemoveObserver(this);
124
125 bluez::BluetoothMediaClient* media =
126 bluez::BluezDBusManager::Get()->GetBluetoothMediaClient();
127 CHECK(media);
128 media->RemoveObserver(this);
129
130 bluez::BluetoothMediaTransportClient* transport =
131 bluez::BluezDBusManager::Get()->GetBluetoothMediaTransportClient();
132 CHECK(transport);
133 transport->RemoveObserver(this);
134 }
135
136 void BluetoothAudioSinkBlueZ::Unregister(
137 const base::Closure& callback,
138 const device::BluetoothAudioSink::ErrorCallback& error_callback) {
139 VLOG(1) << "Unregister";
140
141 if (!bluez::BluezDBusManager::IsInitialized())
142 error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED);
143
144 bluez::BluetoothMediaClient* media =
145 bluez::BluezDBusManager::Get()->GetBluetoothMediaClient();
146 CHECK(media);
147
148 media->UnregisterEndpoint(
149 media_path_, endpoint_path_,
150 base::Bind(&BluetoothAudioSinkBlueZ::OnUnregisterSucceeded,
151 weak_ptr_factory_.GetWeakPtr(), callback),
152 base::Bind(&BluetoothAudioSinkBlueZ::OnUnregisterFailed,
153 weak_ptr_factory_.GetWeakPtr(), error_callback));
154 }
155
156 void BluetoothAudioSinkBlueZ::AddObserver(
157 BluetoothAudioSink::Observer* observer) {
158 CHECK(observer);
159 observers_.AddObserver(observer);
160 }
161
162 void BluetoothAudioSinkBlueZ::RemoveObserver(
163 BluetoothAudioSink::Observer* observer) {
164 CHECK(observer);
165 observers_.RemoveObserver(observer);
166 }
167
168 BluetoothAudioSink::State BluetoothAudioSinkBlueZ::GetState() const {
169 return state_;
170 }
171
172 uint16_t BluetoothAudioSinkBlueZ::GetVolume() const {
173 return volume_;
174 }
175
176 void BluetoothAudioSinkBlueZ::Register(
177 const BluetoothAudioSink::Options& options,
178 const base::Closure& callback,
179 const BluetoothAudioSink::ErrorCallback& error_callback) {
180 VLOG(1) << "Register";
181
182 DCHECK(adapter_.get());
183 DCHECK_EQ(state_, BluetoothAudioSink::STATE_DISCONNECTED);
184
185 // Gets system bus.
186 dbus::Bus* system_bus = bluez::BluezDBusManager::Get()->GetSystemBus();
187
188 // Creates a Media Endpoint with newly-generated path.
189 endpoint_path_ = GenerateEndpointPath();
190 media_endpoint_.reset(bluez::BluetoothMediaEndpointServiceProvider::Create(
191 system_bus, endpoint_path_, this));
192
193 DCHECK(media_endpoint_.get());
194
195 // Creates endpoint properties with |options|.
196 options_ = options;
197 bluez::BluetoothMediaClient::EndpointProperties endpoint_properties;
198 endpoint_properties.uuid =
199 bluez::BluetoothMediaClient::kBluetoothAudioSinkUUID;
200 endpoint_properties.codec = options_.codec;
201 endpoint_properties.capabilities = options_.capabilities;
202
203 media_path_ =
204 static_cast<BluetoothAdapterBlueZ*>(adapter_.get())->object_path();
205
206 bluez::BluetoothMediaClient* media =
207 bluez::BluezDBusManager::Get()->GetBluetoothMediaClient();
208 CHECK(media);
209 media->RegisterEndpoint(
210 media_path_, endpoint_path_, endpoint_properties,
211 base::Bind(&BluetoothAudioSinkBlueZ::OnRegisterSucceeded,
212 weak_ptr_factory_.GetWeakPtr(), callback),
213 base::Bind(&BluetoothAudioSinkBlueZ::OnRegisterFailed,
214 weak_ptr_factory_.GetWeakPtr(), error_callback));
215 }
216
217 bluez::BluetoothMediaEndpointServiceProvider*
218 BluetoothAudioSinkBlueZ::GetEndpointServiceProvider() {
219 return media_endpoint_.get();
220 }
221
222 void BluetoothAudioSinkBlueZ::AdapterPresentChanged(
223 device::BluetoothAdapter* adapter,
224 bool present) {
225 VLOG(1) << "AdapterPresentChanged: " << present;
226
227 if (adapter != adapter_.get())
228 return;
229
230 if (adapter->IsPresent()) {
231 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
232 } else {
233 adapter_->RemoveObserver(this);
234 StateChanged(BluetoothAudioSink::STATE_INVALID);
235 }
236 }
237
238 void BluetoothAudioSinkBlueZ::AdapterPoweredChanged(
239 device::BluetoothAdapter* adapter,
240 bool powered) {
241 VLOG(1) << "AdapterPoweredChanged: " << powered;
242
243 if (adapter != adapter_.get())
244 return;
245
246 // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED.
247 // If false, the transport is closed, but the endpoint is still valid for use.
248 // If true, the previous transport has been torn down, so the |state_| has to
249 // be disconnected before SetConfigruation is called.
250 if (state_ != BluetoothAudioSink::STATE_INVALID)
251 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
252 }
253
254 void BluetoothAudioSinkBlueZ::MediaRemoved(const ObjectPath& object_path) {
255 if (object_path == media_path_) {
256 VLOG(1) << "MediaRemoved: " << object_path.value();
257 StateChanged(BluetoothAudioSink::STATE_INVALID);
258 }
259 }
260
261 void BluetoothAudioSinkBlueZ::MediaTransportRemoved(
262 const ObjectPath& object_path) {
263 // Whenever powered of |adapter_| turns false while present stays true, media
264 // transport object should be removed accordingly, and the state should be
265 // changed to STATE_DISCONNECTED.
266 if (object_path == transport_path_) {
267 VLOG(1) << "MediaTransportRemoved: " << object_path.value();
268 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
269 }
270 }
271
272 void BluetoothAudioSinkBlueZ::MediaTransportPropertyChanged(
273 const ObjectPath& object_path,
274 const std::string& property_name) {
275 if (object_path != transport_path_)
276 return;
277
278 VLOG(1) << "MediaTransportPropertyChanged: " << property_name;
279
280 // Retrieves the property set of the transport object with |object_path|.
281 bluez::BluetoothMediaTransportClient::Properties* properties =
282 bluez::BluezDBusManager::Get()
283 ->GetBluetoothMediaTransportClient()
284 ->GetProperties(object_path);
285
286 // Dispatches a property changed event to the corresponding handler.
287 if (property_name == properties->state.name()) {
288 if (properties->state.value() ==
289 bluez::BluetoothMediaTransportClient::kStateIdle) {
290 StateChanged(BluetoothAudioSink::STATE_IDLE);
291 } else if (properties->state.value() ==
292 bluez::BluetoothMediaTransportClient::kStatePending) {
293 StateChanged(BluetoothAudioSink::STATE_PENDING);
294 } else if (properties->state.value() ==
295 bluez::BluetoothMediaTransportClient::kStateActive) {
296 StateChanged(BluetoothAudioSink::STATE_ACTIVE);
297 }
298 } else if (property_name == properties->volume.name()) {
299 VolumeChanged(properties->volume.value());
300 }
301 }
302
303 void BluetoothAudioSinkBlueZ::SetConfiguration(
304 const ObjectPath& transport_path,
305 const TransportProperties& properties) {
306 VLOG(1) << "SetConfiguration";
307 transport_path_ = transport_path;
308
309 // The initial state for a connection should be "idle".
310 if (properties.state != bluez::BluetoothMediaTransportClient::kStateIdle) {
311 VLOG(1) << "SetConfiugration - unexpected state :" << properties.state;
312 return;
313 }
314
315 // Updates |volume_| if the volume level is provided in |properties|.
316 if (properties.volume.get()) {
317 VolumeChanged(*properties.volume);
318 }
319
320 StateChanged(BluetoothAudioSink::STATE_IDLE);
321 }
322
323 void BluetoothAudioSinkBlueZ::SelectConfiguration(
324 const std::vector<uint8_t>& capabilities,
325 const SelectConfigurationCallback& callback) {
326 VLOG(1) << "SelectConfiguration";
327 callback.Run(options_.capabilities);
328 }
329
330 void BluetoothAudioSinkBlueZ::ClearConfiguration(
331 const ObjectPath& transport_path) {
332 if (transport_path != transport_path_)
333 return;
334
335 VLOG(1) << "ClearConfiguration";
336 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
337 }
338
339 void BluetoothAudioSinkBlueZ::Released() {
340 VLOG(1) << "Released";
341 StateChanged(BluetoothAudioSink::STATE_INVALID);
342 }
343
344 void BluetoothAudioSinkBlueZ::OnFileCanReadWithoutBlocking(int fd) {
345 ReadFromFile();
346 }
347
348 void BluetoothAudioSinkBlueZ::OnFileCanWriteWithoutBlocking(int fd) {
349 // Do nothing for now.
350 }
351
352 void BluetoothAudioSinkBlueZ::AcquireFD() {
353 VLOG(1) << "AcquireFD - transport path: " << transport_path_.value();
354
355 read_has_failed_ = false;
356
357 bluez::BluezDBusManager::Get()->GetBluetoothMediaTransportClient()->Acquire(
358 transport_path_, base::Bind(&BluetoothAudioSinkBlueZ::OnAcquireSucceeded,
359 weak_ptr_factory_.GetWeakPtr()),
360 base::Bind(&BluetoothAudioSinkBlueZ::OnAcquireFailed,
361 weak_ptr_factory_.GetWeakPtr()));
362 }
363
364 void BluetoothAudioSinkBlueZ::WatchFD() {
365 CHECK(file_.get() && file_->IsValid());
366
367 VLOG(1) << "WatchFD - file: " << file_->GetPlatformFile()
368 << ", file validity: " << file_->IsValid();
369
370 base::MessageLoopForIO::current()->WatchFileDescriptor(
371 file_->GetPlatformFile(), true, base::MessageLoopForIO::WATCH_READ,
372 &fd_read_watcher_, this);
373 }
374
375 void BluetoothAudioSinkBlueZ::StopWatchingFD() {
376 if (!file_.get()) {
377 VLOG(1) << "StopWatchingFD - skip";
378 return;
379 }
380
381 bool stopped = fd_read_watcher_.StopWatchingFileDescriptor();
382 VLOG(1) << "StopWatchingFD - watch stopped: " << stopped;
383 CHECK(stopped);
384
385 read_mtu_ = kInvalidReadMtu;
386 write_mtu_ = kInvalidWriteMtu;
387 file_.reset(); // This will close the file descriptor.
388 }
389
390 void BluetoothAudioSinkBlueZ::ReadFromFile() {
391 DCHECK(file_.get() && file_->IsValid());
392 DCHECK(data_.get());
393
394 int size = file_->ReadAtCurrentPosNoBestEffort(data_.get(), read_mtu_);
395
396 if (size == -1) {
397 // To reduce the number of logs, log only once for multiple failures.
398 if (!read_has_failed_) {
399 VLOG(1) << "ReadFromFile - failed";
400 read_has_failed_ = true;
401 }
402 return;
403 }
404
405 VLOG(1) << "ReadFromFile - read " << size << " bytes";
406 FOR_EACH_OBSERVER(
407 BluetoothAudioSink::Observer, observers_,
408 BluetoothAudioSinkDataAvailable(this, data_.get(), size, read_mtu_));
409 }
410
411 void BluetoothAudioSinkBlueZ::StateChanged(BluetoothAudioSink::State state) {
412 if (state == state_)
413 return;
414
415 VLOG(1) << "StateChanged - state: " << StateToString(state);
416
417 switch (state) {
418 case BluetoothAudioSink::STATE_INVALID:
419 ResetMedia();
420 ResetEndpoint();
421 case BluetoothAudioSink::STATE_DISCONNECTED:
422 ResetTransport();
423 break;
424 case BluetoothAudioSink::STATE_IDLE:
425 StopWatchingFD();
426 break;
427 case BluetoothAudioSink::STATE_PENDING:
428 AcquireFD();
429 break;
430 case BluetoothAudioSink::STATE_ACTIVE:
431 WatchFD();
432 break;
433 default:
434 break;
435 }
436
437 state_ = state;
438 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
439 BluetoothAudioSinkStateChanged(this, state_));
440 }
441
442 void BluetoothAudioSinkBlueZ::VolumeChanged(uint16_t volume) {
443 if (volume == volume_)
444 return;
445
446 VLOG(1) << "VolumeChanged: " << volume;
447
448 volume_ = std::min(volume, BluetoothAudioSink::kInvalidVolume);
449 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
450 BluetoothAudioSinkVolumeChanged(this, volume_));
451 }
452
453 void BluetoothAudioSinkBlueZ::OnRegisterSucceeded(
454 const base::Closure& callback) {
455 DCHECK(media_endpoint_.get());
456 VLOG(1) << "OnRegisterSucceeded";
457
458 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
459 callback.Run();
460 }
461
462 void BluetoothAudioSinkBlueZ::OnRegisterFailed(
463 const BluetoothAudioSink::ErrorCallback& error_callback,
464 const std::string& error_name,
465 const std::string& error_message) {
466 VLOG(1) << "OnRegisterFailed - error name: " << error_name
467 << ", error message: " << error_message;
468
469 ResetEndpoint();
470 error_callback.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED);
471 }
472
473 void BluetoothAudioSinkBlueZ::OnUnregisterSucceeded(
474 const base::Closure& callback) {
475 VLOG(1) << "Unregistered - endpoint: " << endpoint_path_.value();
476
477 // Once the state becomes STATE_INVALID, media, media transport and media
478 // endpoint will be reset.
479 StateChanged(BluetoothAudioSink::STATE_INVALID);
480 callback.Run();
481 }
482
483 void BluetoothAudioSinkBlueZ::OnUnregisterFailed(
484 const device::BluetoothAudioSink::ErrorCallback& error_callback,
485 const std::string& error_name,
486 const std::string& error_message) {
487 VLOG(1) << "OnUnregisterFailed - error name: " << error_name
488 << ", error message: " << error_message;
489
490 error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED);
491 }
492
493 void BluetoothAudioSinkBlueZ::OnAcquireSucceeded(dbus::FileDescriptor* fd,
494 const uint16_t read_mtu,
495 const uint16_t write_mtu) {
496 CHECK(fd);
497 fd->CheckValidity();
498 CHECK(fd->is_valid() && fd->value() != kInvalidFd);
499 CHECK_GT(read_mtu, kInvalidReadMtu);
500 CHECK_GT(write_mtu, kInvalidWriteMtu);
501
502 // Avoids unnecessary memory reallocation if read MTU doesn't change.
503 if (read_mtu != read_mtu_) {
504 read_mtu_ = read_mtu;
505 data_.reset(new char[read_mtu_]);
506 VLOG(1) << "OnAcquireSucceeded - allocate " << read_mtu_
507 << " bytes of memory";
508 }
509
510 write_mtu_ = write_mtu;
511
512 // Avoids closing the same file descriptor caused by reassignment.
513 if (!file_.get() || file_->GetPlatformFile() != fd->value()) {
514 // Takes ownership of the file descriptor.
515 file_.reset(new base::File(fd->TakeValue()));
516 DCHECK(file_->IsValid());
517 VLOG(1) << "OnAcquireSucceeded - update file";
518 }
519
520 VLOG(1) << "OnAcquireSucceeded - file: " << file_->GetPlatformFile()
521 << ", read MTU: " << read_mtu_ << ", write MTU: " << write_mtu_;
522 }
523
524 void BluetoothAudioSinkBlueZ::OnAcquireFailed(
525 const std::string& error_name,
526 const std::string& error_message) {
527 VLOG(1) << "OnAcquireFailed - error name: " << error_name
528 << ", error message: " << error_message;
529 }
530
531 void BluetoothAudioSinkBlueZ::OnReleaseFDSucceeded() {
532 VLOG(1) << "OnReleaseFDSucceeded";
533 }
534
535 void BluetoothAudioSinkBlueZ::OnReleaseFDFailed(
536 const std::string& error_name,
537 const std::string& error_message) {
538 VLOG(1) << "OnReleaseFDFailed - error name: " << error_name
539 << ", error message: " << error_message;
540 }
541
542 void BluetoothAudioSinkBlueZ::ResetMedia() {
543 VLOG(1) << "ResetMedia";
544
545 media_path_ = dbus::ObjectPath("");
546 }
547
548 void BluetoothAudioSinkBlueZ::ResetTransport() {
549 if (!transport_path_.IsValid()) {
550 VLOG(1) << "ResetTransport - skip";
551 return;
552 }
553
554 VLOG(1) << "ResetTransport - clean-up";
555
556 VolumeChanged(BluetoothAudioSink::kInvalidVolume);
557 transport_path_ = dbus::ObjectPath("");
558 read_mtu_ = kInvalidReadMtu;
559 write_mtu_ = kInvalidWriteMtu;
560 file_.reset();
561 }
562
563 void BluetoothAudioSinkBlueZ::ResetEndpoint() {
564 VLOG(1) << "ResetEndpoint";
565
566 endpoint_path_ = ObjectPath("");
567 media_endpoint_ = nullptr;
568 }
569
570 } // namespace bluez
OLDNEW
« no previous file with comments | « device/bluetooth/bluetooth_audio_sink_bluez.h ('k') | device/bluetooth/bluetooth_audio_sink_bluez_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698