// Copyright 2013 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 "chromeos/dbus/bluetooth_agent_service_provider.h"
#include <string>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/threading/platform_thread.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h"
#include "dbus/bus.h"
#include "dbus/exported_object.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
// The BluetoothAgentServiceProvider implementation used in production.
class BluetoothAgentServiceProviderImpl
: public BluetoothAgentServiceProvider {
public:
BluetoothAgentServiceProviderImpl(dbus::Bus* bus,
const dbus::ObjectPath& object_path,
Delegate* delegate)
: origin_thread_id_(base::PlatformThread::CurrentId()),
bus_(bus),
delegate_(delegate),
object_path_(object_path),
weak_ptr_factory_(this) {
VLOG(1) << "Creating Bluetooth Agent: " << object_path_.value();
exported_object_ = bus_->GetExportedObject(object_path_);
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kRelease,
base::Bind(&BluetoothAgentServiceProviderImpl::Release,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kRequestPinCode,
base::Bind(&BluetoothAgentServiceProviderImpl::RequestPinCode,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kDisplayPinCode,
base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPinCode,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kRequestPasskey,
base::Bind(&BluetoothAgentServiceProviderImpl::RequestPasskey,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kDisplayPasskey,
base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPasskey,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kRequestConfirmation,
base::Bind(&BluetoothAgentServiceProviderImpl::RequestConfirmation,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kRequestAuthorization,
base::Bind(&BluetoothAgentServiceProviderImpl::RequestAuthorization,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kAuthorizeService,
base::Bind(&BluetoothAgentServiceProviderImpl::AuthorizeService,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object_->ExportMethod(
bluetooth_agent::kBluetoothAgentInterface,
bluetooth_agent::kCancel,
base::Bind(&BluetoothAgentServiceProviderImpl::Cancel,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
weak_ptr_factory_.GetWeakPtr()));
}
virtual ~BluetoothAgentServiceProviderImpl() {
VLOG(1) << "Cleaning up Bluetooth Agent: " << object_path_.value();
// Unregister the object path so we can reuse with a new agent.
bus_->UnregisterExportedObject(object_path_);
}
private:
// Returns true if the current thread is on the origin thread.
bool OnOriginThread() {
return base::PlatformThread::CurrentId() == origin_thread_id_;
}
// Called by dbus:: when the agent is unregistered from the Bluetooth
// daemon, generally at the end of a pairing request.
void Release(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
delegate_->Released();
response_sender.Run(dbus::Response::FromMethodCall(method_call));
}
// Called by dbus:: when the Bluetooth daemon requires a PIN Code for
// device authentication.
void RequestPinCode(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
if (!reader.PopObjectPath(&device_path)) {
LOG(WARNING) << "RequestPinCode called with incorrect paramters: "
<< method_call->ToString();
return;
}
Delegate::PinCodeCallback callback = base::Bind(
&BluetoothAgentServiceProviderImpl::OnPinCode,
weak_ptr_factory_.GetWeakPtr(),
method_call,
response_sender);
delegate_->RequestPinCode(device_path, callback);
}
// Called by dbus:: when the Bluetooth daemon requires that the user
// enter a PIN Code into the remote device so that it may be
// authenticated.
void DisplayPinCode(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
std::string pincode;
if (!reader.PopObjectPath(&device_path) ||
!reader.PopString(&pincode)) {
LOG(WARNING) << "DisplayPinCode called with incorrect paramters: "
<< method_call->ToString();
return;
}
delegate_->DisplayPinCode(device_path, pincode);
response_sender.Run(dbus::Response::FromMethodCall(method_call));
}
// Called by dbus:: when the Bluetooth daemon requires a Passkey for
// device authentication.
void RequestPasskey(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
if (!reader.PopObjectPath(&device_path)) {
LOG(WARNING) << "RequestPasskey called with incorrect paramters: "
<< method_call->ToString();
return;
}
Delegate::PasskeyCallback callback = base::Bind(
&BluetoothAgentServiceProviderImpl::OnPasskey,
weak_ptr_factory_.GetWeakPtr(),
method_call,
response_sender);
delegate_->RequestPasskey(device_path, callback);
}
// Called by dbus:: when the Bluetooth daemon requires that the user
// enter a Passkey into the remote device so that it may be
// authenticated.
void DisplayPasskey(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
uint32 passkey;
uint16 entered;
if (!reader.PopObjectPath(&device_path) ||
!reader.PopUint32(&passkey) ||
!reader.PopUint16(&entered)) {
LOG(WARNING) << "DisplayPasskey called with incorrect paramters: "
<< method_call->ToString();
return;
}
delegate_->DisplayPasskey(device_path, passkey, entered);
response_sender.Run(dbus::Response::FromMethodCall(method_call));
}
// Called by dbus:: when the Bluetooth daemon requires that the user
// confirm that a Passkey is displayed on the screen of the remote
// device so that it may be authenticated.
void RequestConfirmation(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
uint32 passkey;
if (!reader.PopObjectPath(&device_path) ||
!reader.PopUint32(&passkey)) {
LOG(WARNING) << "RequestConfirmation called with incorrect paramters: "
<< method_call->ToString();
return;
}
Delegate::ConfirmationCallback callback = base::Bind(
&BluetoothAgentServiceProviderImpl::OnConfirmation,
weak_ptr_factory_.GetWeakPtr(),
method_call,
response_sender);
delegate_->RequestConfirmation(device_path, passkey, callback);
}
// Called by dbus:: when the Bluetooth daemon requires that the user
// confirm an incoming just-works pairing.
void RequestAuthorization(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
if (!reader.PopObjectPath(&device_path)) {
LOG(WARNING) << "RequestAuthorization called with incorrect paramters: "
<< method_call->ToString();
return;
}
Delegate::ConfirmationCallback callback = base::Bind(
&BluetoothAgentServiceProviderImpl::OnConfirmation,
weak_ptr_factory_.GetWeakPtr(),
method_call,
response_sender);
delegate_->RequestAuthorization(device_path, callback);
}
// Called by dbus:: when the Bluetooth daemon requires that the user
// confirm that that a remote device is authorized to connect to a service
// UUID.
void AuthorizeService(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
dbus::MessageReader reader(method_call);
dbus::ObjectPath device_path;
std::string uuid;
if (!reader.PopObjectPath(&device_path) ||
!reader.PopString(&uuid)) {
LOG(WARNING) << "AuthorizeService called with incorrect paramters: "
<< method_call->ToString();
return;
}
Delegate::ConfirmationCallback callback = base::Bind(
&BluetoothAgentServiceProviderImpl::OnConfirmation,
weak_ptr_factory_.GetWeakPtr(),
method_call,
response_sender);
delegate_->AuthorizeService(device_path, uuid, callback);
}
// Called by dbus:: when the request failed before a reply was returned
// from the device.
void Cancel(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
DCHECK(OnOriginThread());
DCHECK(delegate_);
delegate_->Cancel();
response_sender.Run(dbus::Response::FromMethodCall(method_call));
}
// Called by dbus:: when a method is exported.
void OnExported(const std::string& interface_name,
const std::string& method_name,
bool success) {
LOG_IF(WARNING, !success) << "Failed to export "
<< interface_name << "." << method_name;
}
// Called by the Delegate to response to a method requesting a PIN code.
void OnPinCode(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender,
Delegate::Status status,
const std::string& pincode) {
DCHECK(OnOriginThread());
switch (status) {
case Delegate::SUCCESS: {
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(response.get());
writer.AppendString(pincode);
response_sender.Run(response.Pass());
break;
}
case Delegate::REJECTED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorRejected, "rejected")
.PassAs<dbus::Response>());
break;
}
case Delegate::CANCELLED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorCanceled, "canceled")
.PassAs<dbus::Response>());
break;
}
default:
NOTREACHED() << "Unexpected status code from delegate: " << status;
}
}
// Called by the Delegate to response to a method requesting a Passkey.
void OnPasskey(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender,
Delegate::Status status,
uint32 passkey) {
DCHECK(OnOriginThread());
switch (status) {
case Delegate::SUCCESS: {
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(response.get());
writer.AppendUint32(passkey);
response_sender.Run(response.Pass());
break;
}
case Delegate::REJECTED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorRejected, "rejected")
.PassAs<dbus::Response>());
break;
}
case Delegate::CANCELLED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorCanceled, "canceled")
.PassAs<dbus::Response>());
break;
}
default:
NOTREACHED() << "Unexpected status code from delegate: " << status;
}
}
// Called by the Delegate in response to a method requiring confirmation.
void OnConfirmation(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender,
Delegate::Status status) {
DCHECK(OnOriginThread());
switch (status) {
case Delegate::SUCCESS: {
response_sender.Run(dbus::Response::FromMethodCall(method_call));
break;
}
case Delegate::REJECTED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorRejected, "rejected")
.PassAs<dbus::Response>());
break;
}
case Delegate::CANCELLED: {
response_sender.Run(
dbus::ErrorResponse::FromMethodCall(
method_call, bluetooth_agent::kErrorCanceled, "canceled")
.PassAs<dbus::Response>());
break;
}
default:
NOTREACHED() << "Unexpected status code from delegate: " << status;
}
}
// Origin thread (i.e. the UI thread in production).
base::PlatformThreadId origin_thread_id_;
// D-Bus bus object is exported on, not owned by this object and must
// outlive it.
dbus::Bus* bus_;
// All incoming method calls are passed on to the Delegate and a callback
// passed to generate the reply. |delegate_| is generally the object that
// owns this one, and must outlive it.
Delegate* delegate_;
// D-Bus object path of object we are exporting, kept so we can unregister
// again in our destructor.
dbus::ObjectPath object_path_;
// D-Bus object we are exporting, owned by this object.
scoped_refptr<dbus::ExportedObject> exported_object_;
// Weak pointer factory for generating 'this' pointers that might live longer
// than we do.
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<BluetoothAgentServiceProviderImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BluetoothAgentServiceProviderImpl);
};
BluetoothAgentServiceProvider::BluetoothAgentServiceProvider() {
}
BluetoothAgentServiceProvider::~BluetoothAgentServiceProvider() {
}
// static
BluetoothAgentServiceProvider* BluetoothAgentServiceProvider::Create(
dbus::Bus* bus,
const dbus::ObjectPath& object_path,
Delegate* delegate) {
if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) {
return new BluetoothAgentServiceProviderImpl(bus, object_path, delegate);
} else {
return new FakeBluetoothAgentServiceProvider(object_path, delegate);
}
}
} // namespace chromeos