Move InMemoryURLIndex into chrome/browser/autocomplete
[chromium-blink-merge.git] / device / bluetooth / bluetooth_audio_sink_chromeos.cc
blobfb1ddf7d6ec09565442cde1013ddaeed52f93150
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.
5 #include "device/bluetooth/bluetooth_audio_sink_chromeos.h"
7 #include <algorithm>
8 #include <sstream>
9 #include <string>
10 #include <vector>
12 #include "base/debug/stack_trace.h"
13 #include "base/logging.h"
14 #include "chromeos/dbus/dbus_thread_manager.h"
15 #include "dbus/message.h"
16 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
18 using dbus::ObjectPath;
19 using device::BluetoothAudioSink;
21 namespace {
23 // TODO(mcchou): Add the constant to dbus/service_constants.h.
24 const char kBluetoothAudioSinkServicePath[] = "/org/chromium/AudioSink";
26 ObjectPath GenerateEndpointPath() {
27 static unsigned int sequence_number = 0;
28 ++sequence_number;
29 std::stringstream path;
30 path << kBluetoothAudioSinkServicePath << "/endpoint" << sequence_number;
31 return ObjectPath(path.str());
34 std::string StateToString(const BluetoothAudioSink::State& state) {
35 switch (state) {
36 case BluetoothAudioSink::STATE_INVALID:
37 return "invalid";
38 case BluetoothAudioSink::STATE_DISCONNECTED:
39 return "disconnected";
40 case BluetoothAudioSink::STATE_IDLE:
41 return "idle";
42 case BluetoothAudioSink::STATE_PENDING:
43 return "pending";
44 case BluetoothAudioSink::STATE_ACTIVE:
45 return "active";
46 default:
47 return "unknown";
51 std::string ErrorCodeToString(const BluetoothAudioSink::ErrorCode& error_code) {
52 switch (error_code) {
53 case BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM:
54 return "unsupported platform";
55 case BluetoothAudioSink::ERROR_INVALID_ADAPTER:
56 return "invalid adapter";
57 case BluetoothAudioSink::ERROR_NOT_REGISTERED:
58 return "not registered";
59 case BluetoothAudioSink::ERROR_NOT_UNREGISTERED:
60 return "not unregistered";
61 default:
62 return "unknown";
66 // A dummy error callback for calling Unregister() in destructor.
67 void UnregisterErrorCallback(
68 device::BluetoothAudioSink::ErrorCode error_code) {
69 VLOG(1) << "UnregisterErrorCallback - " << ErrorCodeToString(error_code)
70 << "(" << error_code << ")";
73 } // namespace
75 namespace chromeos {
77 BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS(
78 scoped_refptr<device::BluetoothAdapter> adapter)
79 : state_(BluetoothAudioSink::STATE_INVALID),
80 volume_(BluetoothAudioSink::kInvalidVolume),
81 read_mtu_(nullptr),
82 write_mtu_(nullptr),
83 adapter_(adapter),
84 weak_ptr_factory_(this) {
85 VLOG(1) << "BluetoothAudioSinkChromeOS created";
87 DCHECK(adapter_.get());
88 DCHECK(adapter_->IsPresent());
90 adapter_->AddObserver(this);
92 chromeos::BluetoothMediaClient* media =
93 DBusThreadManager::Get()->GetBluetoothMediaClient();
94 DCHECK(media);
95 media->AddObserver(this);
97 chromeos::BluetoothMediaTransportClient *transport =
98 chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
99 DCHECK(transport);
100 transport->AddObserver(this);
102 StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED);
105 BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() {
106 VLOG(1) << "BluetoothAudioSinkChromeOS destroyed";
108 DCHECK(adapter_.get());
110 if (state_ != BluetoothAudioSink::STATE_INVALID && media_endpoint_.get()) {
111 Unregister(base::Bind(&base::DoNothing),
112 base::Bind(&UnregisterErrorCallback));
115 adapter_->RemoveObserver(this);
117 chromeos::BluetoothMediaClient* media =
118 DBusThreadManager::Get()->GetBluetoothMediaClient();
119 DCHECK(media);
120 media->RemoveObserver(this);
122 chromeos::BluetoothMediaTransportClient *transport =
123 chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
124 DCHECK(transport);
125 transport->RemoveObserver(this);
128 void BluetoothAudioSinkChromeOS::Unregister(
129 const base::Closure& callback,
130 const device::BluetoothAudioSink::ErrorCallback& error_callback) {
131 VLOG(1) << "Unregister";
133 if (!DBusThreadManager::IsInitialized())
134 error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED);
136 chromeos::BluetoothMediaClient* media =
137 DBusThreadManager::Get()->GetBluetoothMediaClient();
138 DCHECK(media);
140 media->UnregisterEndpoint(
141 media_path_,
142 endpoint_path_,
143 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterSucceeded,
144 weak_ptr_factory_.GetWeakPtr(), callback),
145 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterFailed,
146 weak_ptr_factory_.GetWeakPtr(), error_callback));
149 void BluetoothAudioSinkChromeOS::AddObserver(
150 BluetoothAudioSink::Observer* observer) {
151 DCHECK(observer);
152 observers_.AddObserver(observer);
155 void BluetoothAudioSinkChromeOS::RemoveObserver(
156 BluetoothAudioSink::Observer* observer) {
157 DCHECK(observer);
158 observers_.RemoveObserver(observer);
161 BluetoothAudioSink::State BluetoothAudioSinkChromeOS::GetState() const {
162 return state_;
165 uint16_t BluetoothAudioSinkChromeOS::GetVolume() const {
166 return volume_;
169 void BluetoothAudioSinkChromeOS::AdapterPresentChanged(
170 device::BluetoothAdapter* adapter, bool present) {
171 VLOG(1) << "AdapterPresentChanged: " << present;
173 if (adapter->IsPresent()) {
174 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
175 } else {
176 adapter_->RemoveObserver(this);
177 StateChanged(BluetoothAudioSink::STATE_INVALID);
181 void BluetoothAudioSinkChromeOS::AdapterPoweredChanged(
182 device::BluetoothAdapter* adapter, bool powered) {
183 VLOG(1) << "AdapterPoweredChanged: " << powered;
185 // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED.
186 // If false, the transport is closed, but the endpoint is still valid for use.
187 // If true, the previous transport has been torn down, so the |state_| has to
188 // be disconnected before SetConfigruation is called.
189 if (state_ != BluetoothAudioSink::STATE_INVALID)
190 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
193 void BluetoothAudioSinkChromeOS::MediaRemoved(const ObjectPath& object_path) {
194 if (object_path == media_path_) {
195 VLOG(1) << "MediaRemoved: " << object_path.value();
196 StateChanged(BluetoothAudioSink::STATE_INVALID);
200 void BluetoothAudioSinkChromeOS::MediaTransportRemoved(
201 const ObjectPath& object_path) {
202 // Whenever powered of |adapter_| turns false while present stays true, media
203 // transport object should be removed accordingly, and the state should be
204 // changed to STATE_DISCONNECTED.
205 if (object_path == transport_path_) {
206 VLOG(1) << "MediaTransportRemoved: " << object_path.value();
207 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
211 void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged(
212 const ObjectPath& object_path,
213 const std::string& property_name) {
214 if (object_path != transport_path_)
215 return;
217 VLOG(1) << "MediaTransportPropertyChanged: " << property_name;
219 // Retrieves the property set of the transport object with |object_path|.
220 chromeos::BluetoothMediaTransportClient::Properties* properties =
221 DBusThreadManager::Get()
222 ->GetBluetoothMediaTransportClient()
223 ->GetProperties(object_path);
225 // Dispatches a property changed event to the corresponding handler.
226 if (property_name == properties->state.name()) {
227 if (properties->state.value() ==
228 BluetoothMediaTransportClient::kStateIdle) {
229 StateChanged(BluetoothAudioSink::STATE_IDLE);
230 } else if (properties->state.value() ==
231 BluetoothMediaTransportClient::kStatePending) {
232 StateChanged(BluetoothAudioSink::STATE_PENDING);
233 } else if (properties->state.value() ==
234 BluetoothMediaTransportClient::kStateActive) {
235 StateChanged(BluetoothAudioSink::STATE_ACTIVE);
237 } else if (property_name == properties->volume.name()) {
238 VolumeChanged(properties->volume.value());
242 void BluetoothAudioSinkChromeOS::SetConfiguration(
243 const ObjectPath& transport_path,
244 const TransportProperties& properties) {
245 VLOG(1) << "SetConfiguration";
246 transport_path_ = transport_path;
248 // The initial state for a connection should be "idle".
249 if (properties.state != BluetoothMediaTransportClient::kStateIdle) {
250 VLOG(1) << "SetConfiugration - unexpected state :" << properties.state;
251 return;
254 // Updates |volume_| if the volume level is provided in |properties|.
255 if (properties.volume.get()) {
256 VolumeChanged(*properties.volume);
259 StateChanged(BluetoothAudioSink::STATE_IDLE);
262 void BluetoothAudioSinkChromeOS::SelectConfiguration(
263 const std::vector<uint8_t>& capabilities,
264 const SelectConfigurationCallback& callback) {
265 VLOG(1) << "SelectConfiguration";
266 callback.Run(options_.capabilities);
269 void BluetoothAudioSinkChromeOS::ClearConfiguration(
270 const ObjectPath& transport_path) {
271 if (transport_path != transport_path_)
272 return;
273 VLOG(1) << "ClearConfiguration";
274 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
277 void BluetoothAudioSinkChromeOS::Released() {
278 VLOG(1) << "Released";
279 StateChanged(BluetoothAudioSink::STATE_INVALID);
282 void BluetoothAudioSinkChromeOS::Register(
283 const BluetoothAudioSink::Options& options,
284 const base::Closure& callback,
285 const BluetoothAudioSink::ErrorCallback& error_callback) {
286 VLOG(1) << "Register";
288 DCHECK(adapter_.get());
289 DCHECK_EQ(state_, BluetoothAudioSink::STATE_DISCONNECTED);
291 // Gets system bus.
292 dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
294 // Creates a Media Endpoint with newly-generated path.
295 endpoint_path_ = GenerateEndpointPath();
296 media_endpoint_.reset(
297 BluetoothMediaEndpointServiceProvider::Create(
298 system_bus, endpoint_path_, this));
300 DCHECK(media_endpoint_.get());
302 // Creates endpoint properties with |options|.
303 options_ = options;
304 chromeos::BluetoothMediaClient::EndpointProperties endpoint_properties;
305 endpoint_properties.uuid = BluetoothMediaClient::kBluetoothAudioSinkUUID;
306 endpoint_properties.codec = options_.codec;
307 endpoint_properties.capabilities = options_.capabilities;
309 media_path_ = static_cast<BluetoothAdapterChromeOS*>(
310 adapter_.get())->object_path();
312 chromeos::BluetoothMediaClient* media =
313 DBusThreadManager::Get()->GetBluetoothMediaClient();
314 DCHECK(media);
315 media->RegisterEndpoint(
316 media_path_,
317 endpoint_path_,
318 endpoint_properties,
319 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterSucceeded,
320 weak_ptr_factory_.GetWeakPtr(), callback),
321 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterFailed,
322 weak_ptr_factory_.GetWeakPtr(), error_callback));
325 BluetoothMediaEndpointServiceProvider*
326 BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() {
327 return media_endpoint_.get();
330 void BluetoothAudioSinkChromeOS::StateChanged(
331 BluetoothAudioSink::State state) {
332 if (state == state_)
333 return;
335 VLOG(1) << "StateChnaged: " << StateToString(state);
337 switch (state) {
338 case BluetoothAudioSink::STATE_INVALID:
339 ResetMedia();
340 ResetEndpoint();
341 case BluetoothAudioSink::STATE_DISCONNECTED:
342 ResetTransport();
343 break;
344 case BluetoothAudioSink::STATE_IDLE:
345 // TODO(mcchou): BUG=441581
346 // Triggered by MediaTransportPropertyChanged and SetConfiguration.
347 // Stop watching on file descriptor if there is one.
348 break;
349 case BluetoothAudioSink::STATE_PENDING:
350 // TODO(mcchou): BUG=441581
351 // Call BluetoothMediaTransportClient::Acquire() to get fd and mtus.
352 break;
353 case BluetoothAudioSink::STATE_ACTIVE:
354 // TODO(mcchou): BUG=441581
355 // Read from fd and call DataAvailable.
356 ReadFromFD();
357 break;
358 default:
359 break;
362 state_ = state;
363 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
364 BluetoothAudioSinkStateChanged(this, state_));
367 void BluetoothAudioSinkChromeOS::VolumeChanged(uint16_t volume) {
368 if (volume == volume_)
369 return;
371 VLOG(1) << "VolumeChanged: " << volume;
373 volume_ = std::min(volume, BluetoothAudioSink::kInvalidVolume);
374 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
375 BluetoothAudioSinkVolumeChanged(this, volume_));
378 void BluetoothAudioSinkChromeOS::OnRegisterSucceeded(
379 const base::Closure& callback) {
380 DCHECK(media_endpoint_.get());
381 VLOG(1) << "OnRegisterSucceeded";
383 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
384 callback.Run();
387 void BluetoothAudioSinkChromeOS::OnRegisterFailed(
388 const BluetoothAudioSink::ErrorCallback& error_callback,
389 const std::string& error_name,
390 const std::string& error_message) {
391 VLOG(1) << "OnRegisterFailed - error name: " << error_name
392 << ", error message: " << error_message;
394 ResetEndpoint();
395 error_callback.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED);
398 void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded(
399 const base::Closure& callback) {
400 VLOG(1) << "Unregisterd";
402 // Once the state becomes STATE_INVALID, media, media transport and media
403 // endpoint will be reset.
404 StateChanged(BluetoothAudioSink::STATE_INVALID);
405 callback.Run();
408 void BluetoothAudioSinkChromeOS::OnUnregisterFailed(
409 const device::BluetoothAudioSink::ErrorCallback& error_callback,
410 const std::string& error_name,
411 const std::string& error_message) {
412 VLOG(1) << "OnUnregisterFailed - error name: " << error_name
413 << ", error message: " << error_message;
415 error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED);
418 void BluetoothAudioSinkChromeOS::ReadFromFD() {
419 DCHECK_GE(fd_.value(), 0);
421 // TODO(mcchou): BUG=441581
422 // Read from file descriptor using watcher and create a buffer to contain the
423 // data. Notify |Observers_| while there is audio data available.
426 void BluetoothAudioSinkChromeOS::ResetMedia() {
427 VLOG(1) << "ResetMedia";
429 media_path_ = dbus::ObjectPath("");
432 void BluetoothAudioSinkChromeOS::ResetTransport() {
433 VLOG(1) << "ResetTransport";
435 if (transport_path_.value() == "")
436 return;
437 transport_path_ = dbus::ObjectPath("");
438 VolumeChanged(BluetoothAudioSink::kInvalidVolume);
439 read_mtu_ = 0;
440 write_mtu_ = 0;
441 fd_.PutValue(-1);
444 void BluetoothAudioSinkChromeOS::ResetEndpoint() {
445 VLOG(1) << "ResetEndpoint";
447 endpoint_path_ = ObjectPath("");
448 media_endpoint_ = nullptr;
451 } // namespace chromeos