| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/bluetooth/dbus/fake_bluetooth_media_transport_client.h" |
| |
| #include <stddef.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| |
| #include <memory> |
| #include <sstream> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "device/bluetooth/dbus/bluetooth_media_client.h" |
| #include "device/bluetooth/dbus/bluez_dbus_manager.h" |
| #include "device/bluetooth/dbus/fake_bluetooth_adapter_client.h" |
| #include "device/bluetooth/dbus/fake_bluetooth_media_client.h" |
| #include "device/bluetooth/dbus/fake_bluetooth_media_endpoint_service_provider.h" |
| |
| using dbus::ObjectPath; |
| |
| namespace { |
| |
| // TODO(mcchou): Remove this constants once it is in cros_system_api. |
| const char kBluetoothMediaTransportInterface[] = "org.bluez.MediaTransport1"; |
| const char kNotImplemented[] = "org.bluez.NotImplemented"; |
| const char kNotAuthorized[] = "org.bluez.NotAuthorized"; |
| const char kFailed[] = "org.bluez.Failed"; |
| const char kNotAvailable[] = "org.bluez.NotAvailable"; |
| |
| const int kInvalidFd = -1; |
| |
| ObjectPath GenerateTransportPath() { |
| static unsigned int sequence_number = 0; |
| ++sequence_number; |
| std::stringstream path; |
| path << bluez::FakeBluetoothAdapterClient::kAdapterPath |
| << bluez::FakeBluetoothMediaTransportClient::kTransportDevicePath |
| << "/fd" << sequence_number; |
| return ObjectPath(path.str()); |
| } |
| |
| #define UINT8_VECTOR_FROM_ARRAY(array) \ |
| std::vector<uint8_t>(array, array + arraysize(array)) |
| |
| } // namespace |
| |
| namespace bluez { |
| |
| // static |
| const char FakeBluetoothMediaTransportClient::kTransportDevicePath[] = |
| "/fake_audio_source"; |
| const uint8_t FakeBluetoothMediaTransportClient::kTransportCodec = 0x00; |
| const uint8_t FakeBluetoothMediaTransportClient::kTransportConfiguration[] = { |
| 0x21, 0x15, 0x33, 0x2C}; |
| const uint8_t FakeBluetoothMediaTransportClient::kTransportConfigurationLength = |
| arraysize(FakeBluetoothMediaTransportClient::kTransportConfiguration); |
| const uint16_t FakeBluetoothMediaTransportClient::kTransportDelay = 5; |
| const uint16_t FakeBluetoothMediaTransportClient::kTransportVolume = 50; |
| const uint16_t FakeBluetoothMediaTransportClient::kDefaultReadMtu = 20; |
| const uint16_t FakeBluetoothMediaTransportClient::kDefaultWriteMtu = 25; |
| |
| FakeBluetoothMediaTransportClient::Properties::Properties( |
| const PropertyChangedCallback& callback) |
| : BluetoothMediaTransportClient::Properties( |
| nullptr, |
| kBluetoothMediaTransportInterface, |
| callback) {} |
| |
| FakeBluetoothMediaTransportClient::Properties::~Properties() = default; |
| |
| void FakeBluetoothMediaTransportClient::Properties::Get( |
| dbus::PropertyBase* property, |
| dbus::PropertySet::GetCallback callback) { |
| VLOG(1) << "Get " << property->name(); |
| callback.Run(false); |
| } |
| |
| void FakeBluetoothMediaTransportClient::Properties::GetAll() { |
| VLOG(1) << "GetAll called."; |
| } |
| |
| void FakeBluetoothMediaTransportClient::Properties::Set( |
| dbus::PropertyBase* property, |
| dbus::PropertySet::SetCallback callback) { |
| VLOG(1) << "Set " << property->name(); |
| callback.Run(false); |
| } |
| |
| FakeBluetoothMediaTransportClient::Transport::Transport( |
| const ObjectPath& transport_path, |
| std::unique_ptr<Properties> transport_properties) |
| : path(transport_path), properties(std::move(transport_properties)) {} |
| |
| FakeBluetoothMediaTransportClient::Transport::~Transport() = default; |
| |
| FakeBluetoothMediaTransportClient::FakeBluetoothMediaTransportClient() = |
| default; |
| |
| FakeBluetoothMediaTransportClient::~FakeBluetoothMediaTransportClient() = |
| default; |
| |
| // DBusClient override. |
| void FakeBluetoothMediaTransportClient::Init(dbus::Bus* bus) {} |
| |
| void FakeBluetoothMediaTransportClient::AddObserver( |
| BluetoothMediaTransportClient::Observer* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void FakeBluetoothMediaTransportClient::RemoveObserver( |
| BluetoothMediaTransportClient::Observer* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| FakeBluetoothMediaTransportClient::Properties* |
| FakeBluetoothMediaTransportClient::GetProperties( |
| const ObjectPath& object_path) { |
| const ObjectPath& endpoint_path = GetEndpointPath(object_path); |
| Transport* transport = GetTransport(endpoint_path); |
| if (!transport) |
| return nullptr; |
| return transport->properties.get(); |
| } |
| |
| void FakeBluetoothMediaTransportClient::Acquire( |
| const ObjectPath& object_path, |
| const AcquireCallback& callback, |
| const ErrorCallback& error_callback) { |
| VLOG(1) << "Acquire - transport path: " << object_path.value(); |
| AcquireInternal(false, object_path, callback, error_callback); |
| } |
| |
| void FakeBluetoothMediaTransportClient::TryAcquire( |
| const ObjectPath& object_path, |
| const AcquireCallback& callback, |
| const ErrorCallback& error_callback) { |
| VLOG(1) << "TryAcquire - transport path: " << object_path.value(); |
| AcquireInternal(true, object_path, callback, error_callback); |
| } |
| |
| void FakeBluetoothMediaTransportClient::Release( |
| const ObjectPath& object_path, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| error_callback.Run(kNotImplemented, ""); |
| } |
| |
| void FakeBluetoothMediaTransportClient::SetValid( |
| FakeBluetoothMediaEndpointServiceProvider* endpoint, |
| bool valid) { |
| FakeBluetoothMediaClient* media = static_cast<FakeBluetoothMediaClient*>( |
| bluez::BluezDBusManager::Get()->GetBluetoothMediaClient()); |
| DCHECK(media); |
| |
| ObjectPath endpoint_path(endpoint->object_path()); |
| if (!media->IsRegistered(endpoint_path)) |
| return; |
| |
| if (valid) { |
| ObjectPath transport_path = GenerateTransportPath(); |
| VLOG(1) << "New transport, " << transport_path.value() |
| << " is created for endpoint " << endpoint_path.value(); |
| |
| // Sets the fake property set with default values. |
| std::unique_ptr<Properties> properties(new Properties( |
| base::Bind(&FakeBluetoothMediaTransportClient::OnPropertyChanged, |
| base::Unretained(this)))); |
| properties->device.ReplaceValue(ObjectPath(kTransportDevicePath)); |
| properties->uuid.ReplaceValue( |
| BluetoothMediaClient::kBluetoothAudioSinkUUID); |
| properties->codec.ReplaceValue(kTransportCodec); |
| properties->configuration.ReplaceValue( |
| UINT8_VECTOR_FROM_ARRAY(kTransportConfiguration)); |
| properties->state.ReplaceValue(BluetoothMediaTransportClient::kStateIdle); |
| properties->delay.ReplaceValue(kTransportDelay); |
| properties->volume.ReplaceValue(kTransportVolume); |
| |
| endpoint_to_transport_map_[endpoint_path] = |
| std::make_unique<Transport>(transport_path, std::move(properties)); |
| transport_to_endpoint_map_[transport_path] = endpoint_path; |
| return; |
| } |
| |
| Transport* transport = GetTransport(endpoint_path); |
| if (!transport) |
| return; |
| ObjectPath transport_path = transport->path; |
| |
| // Notifies observers about the state change of the transport. |
| for (auto& observer : observers_) |
| observer.MediaTransportRemoved(transport_path); |
| |
| endpoint->ClearConfiguration(transport_path); |
| endpoint_to_transport_map_.erase(endpoint_path); |
| transport_to_endpoint_map_.erase(transport_path); |
| } |
| |
| void FakeBluetoothMediaTransportClient::SetState( |
| const ObjectPath& endpoint_path, |
| const std::string& state) { |
| VLOG(1) << "SetState - state: " << state; |
| |
| Transport* transport = GetTransport(endpoint_path); |
| if (!transport) |
| return; |
| |
| transport->properties->state.ReplaceValue(state); |
| for (auto& observer : observers_) { |
| observer.MediaTransportPropertyChanged( |
| transport->path, BluetoothMediaTransportClient::kStateProperty); |
| } |
| } |
| |
| void FakeBluetoothMediaTransportClient::SetVolume( |
| const ObjectPath& endpoint_path, |
| const uint16_t& volume) { |
| Transport* transport = GetTransport(endpoint_path); |
| if (!transport) |
| return; |
| |
| transport->properties->volume.ReplaceValue(volume); |
| for (auto& observer : observers_) { |
| observer.MediaTransportPropertyChanged( |
| transport->path, BluetoothMediaTransportClient::kVolumeProperty); |
| } |
| } |
| |
| void FakeBluetoothMediaTransportClient::WriteData( |
| const ObjectPath& endpoint_path, |
| const std::vector<char>& bytes) { |
| VLOG(1) << "WriteData - write " << bytes.size() << " bytes"; |
| |
| Transport* transport = GetTransport(endpoint_path); |
| |
| if (!transport || transport->properties->state.value() != "active") { |
| VLOG(1) << "WriteData - write operation rejected, since the state isn't " |
| "active for endpoint: " |
| << endpoint_path.value(); |
| return; |
| } |
| |
| if (!transport->input_fd.get()) { |
| VLOG(1) << "WriteData - invalid input file descriptor"; |
| return; |
| } |
| |
| ssize_t written_len = |
| write(transport->input_fd->GetPlatformFile(), bytes.data(), bytes.size()); |
| if (written_len < 0) { |
| VLOG(1) << "WriteData - failed to write to the socket"; |
| return; |
| } |
| |
| VLOG(1) << "WriteData - wrote " << written_len << " bytes to the socket"; |
| } |
| |
| ObjectPath FakeBluetoothMediaTransportClient::GetTransportPath( |
| const ObjectPath& endpoint_path) { |
| Transport* transport = GetTransport(endpoint_path); |
| return transport ? transport->path : ObjectPath(""); |
| } |
| |
| void FakeBluetoothMediaTransportClient::OnPropertyChanged( |
| const std::string& property_name) { |
| VLOG(1) << "Property " << property_name << " changed"; |
| } |
| |
| ObjectPath FakeBluetoothMediaTransportClient::GetEndpointPath( |
| const ObjectPath& transport_path) { |
| const auto& it = transport_to_endpoint_map_.find(transport_path); |
| return it != transport_to_endpoint_map_.end() ? it->second : ObjectPath(""); |
| } |
| |
| FakeBluetoothMediaTransportClient::Transport* |
| FakeBluetoothMediaTransportClient::GetTransport( |
| const ObjectPath& endpoint_path) { |
| const auto& it = endpoint_to_transport_map_.find(endpoint_path); |
| return (it != endpoint_to_transport_map_.end()) ? it->second.get() : nullptr; |
| } |
| |
| FakeBluetoothMediaTransportClient::Transport* |
| FakeBluetoothMediaTransportClient::GetTransportByPath( |
| const dbus::ObjectPath& transport_path) { |
| return GetTransport(GetEndpointPath(transport_path)); |
| } |
| |
| void FakeBluetoothMediaTransportClient::AcquireInternal( |
| bool try_flag, |
| const ObjectPath& object_path, |
| const AcquireCallback& callback, |
| const ErrorCallback& error_callback) { |
| const ObjectPath& endpoint_path = GetEndpointPath(object_path); |
| Transport* transport = GetTransport(endpoint_path); |
| if (!transport) { |
| error_callback.Run(kFailed, ""); |
| return; |
| } |
| |
| std::string state = transport->properties->state.value(); |
| if (state == "active") { |
| error_callback.Run(kNotAuthorized, ""); |
| return; |
| } |
| if (state != "pending") { |
| error_callback.Run(try_flag ? kNotAvailable : kFailed, ""); |
| return; |
| } |
| |
| int fds[2]; |
| if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { |
| transport->input_fd.reset(); |
| error_callback.Run(kFailed, ""); |
| return; |
| } |
| DCHECK((fds[0] > kInvalidFd) && (fds[1] > kInvalidFd)); |
| transport->input_fd.reset(new base::File(fds[0])); |
| |
| base::ScopedFD out_fd(fds[1]); |
| callback.Run(std::move(out_fd), kDefaultReadMtu, kDefaultWriteMtu); |
| SetState(endpoint_path, "active"); |
| } |
| |
| } // namespace bluez |