// 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 "components/pairing/bluetooth_controller_pairing_controller.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "components/pairing/bluetooth_pairing_constants.h" #include "components/pairing/pairing_api.pb.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_discovery_session.h" #include "net/base/io_buffer.h" namespace { const int kReceiveSize = 16384; } namespace pairing_chromeos { BluetoothControllerPairingController::BluetoothControllerPairingController() : current_stage_(STAGE_NONE), proto_decoder_(new ProtoDecoder(this)), ptr_factory_(this) { } BluetoothControllerPairingController::~BluetoothControllerPairingController() { Reset(); } device::BluetoothDevice* BluetoothControllerPairingController::GetController() { DCHECK(!controller_device_id_.empty()); device::BluetoothDevice* device = adapter_->GetDevice(controller_device_id_); if (!device) { LOG(ERROR) << "Lost connection to controller."; ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR); } return device; } void BluetoothControllerPairingController::ChangeStage(Stage new_stage) { if (current_stage_ == new_stage) return; VLOG(1) << "ChangeStage " << new_stage; current_stage_ = new_stage; FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_, PairingStageChanged(new_stage)); } void BluetoothControllerPairingController::Reset() { controller_device_id_.clear(); discovery_session_.reset(); if (socket_.get()) { socket_->Close(); socket_ = NULL; } if (adapter_.get()) { adapter_->RemoveObserver(this); adapter_ = NULL; } } void BluetoothControllerPairingController::DeviceFound( device::BluetoothDevice* device) { DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY); DCHECK(thread_checker_.CalledOnValidThread()); if (StartsWith(device->GetName(), base::ASCIIToUTF16(kDeviceNamePrefix), false)) { discovered_devices_.insert(device->GetAddress()); FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_, DiscoveredDevicesListChanged()); } } void BluetoothControllerPairingController::DeviceLost( device::BluetoothDevice* device) { DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY); DCHECK(thread_checker_.CalledOnValidThread()); std::set::iterator ix = discovered_devices_.find(device->GetAddress()); if (ix != discovered_devices_.end()) { discovered_devices_.erase(ix); FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_, DiscoveredDevicesListChanged()); } } void BluetoothControllerPairingController::SendBuffer( scoped_refptr io_buffer, int size) { socket_->Send( io_buffer, size, base::Bind(&BluetoothControllerPairingController::OnSendComplete, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage, ptr_factory_.GetWeakPtr())); } void BluetoothControllerPairingController::OnSetPowered() { DCHECK(thread_checker_.CalledOnValidThread()); adapter_->StartDiscoverySession( base::Bind(&BluetoothControllerPairingController::OnStartDiscoverySession, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnError, ptr_factory_.GetWeakPtr())); } void BluetoothControllerPairingController::OnGetAdapter( scoped_refptr adapter) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!adapter_.get()); adapter_ = adapter; adapter_->AddObserver(this); if (adapter_->IsPowered()) { OnSetPowered(); } else { adapter_->SetPowered( true, base::Bind(&BluetoothControllerPairingController::OnSetPowered, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnError, ptr_factory_.GetWeakPtr())); } } void BluetoothControllerPairingController::OnStartDiscoverySession( scoped_ptr discovery_session) { DCHECK(thread_checker_.CalledOnValidThread()); discovery_session_ = discovery_session.Pass(); ChangeStage(STAGE_DEVICES_DISCOVERY); for (const auto& device : adapter_->GetDevices()) DeviceFound(device); } void BluetoothControllerPairingController::OnConnect() { DCHECK(thread_checker_.CalledOnValidThread()); device::BluetoothDevice* device = GetController(); if (device) { device->ConnectToService( device::BluetoothUUID(kPairingServiceUUID), base::Bind(&BluetoothControllerPairingController::OnConnectToService, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage, ptr_factory_.GetWeakPtr())); } } void BluetoothControllerPairingController::OnConnectToService( scoped_refptr socket) { DCHECK(thread_checker_.CalledOnValidThread()); socket_ = socket; socket_->Receive( kReceiveSize, base::Bind(&BluetoothControllerPairingController::OnReceiveComplete, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnReceiveError, ptr_factory_.GetWeakPtr())); ChangeStage(STAGE_PAIRING_DONE); } void BluetoothControllerPairingController::OnSendComplete(int bytes_sent) {} void BluetoothControllerPairingController::OnReceiveComplete( int bytes, scoped_refptr io_buffer) { DCHECK(thread_checker_.CalledOnValidThread()); proto_decoder_->DecodeIOBuffer(bytes, io_buffer); socket_->Receive( kReceiveSize, base::Bind(&BluetoothControllerPairingController::OnReceiveComplete, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnReceiveError, ptr_factory_.GetWeakPtr())); } void BluetoothControllerPairingController::OnError() { LOG(ERROR) << "Pairing initialization failed"; ChangeStage(STAGE_INITIALIZATION_ERROR); Reset(); } void BluetoothControllerPairingController::OnErrorWithMessage( const std::string& message) { LOG(ERROR) << message; ChangeStage(STAGE_INITIALIZATION_ERROR); Reset(); } void BluetoothControllerPairingController::OnConnectError( device::BluetoothDevice::ConnectErrorCode error_code) { DCHECK(thread_checker_.CalledOnValidThread()); device::BluetoothDevice* device = GetController(); if (device && device->IsPaired()) { // The connection attempt is only used to start the pairing between the // devices. If the connection fails, it's not a problem as long as pairing // was successful. OnConnect(); } else { // This can happen if the confirmation dialog times out. ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR); } } void BluetoothControllerPairingController::OnReceiveError( device::BluetoothSocket::ErrorReason reason, const std::string& error_message) { LOG(ERROR) << reason << ", " << error_message; Reset(); } void BluetoothControllerPairingController::AddObserver( ControllerPairingController::Observer* observer) { observers_.AddObserver(observer); } void BluetoothControllerPairingController::RemoveObserver( ControllerPairingController::Observer* observer) { observers_.RemoveObserver(observer); } ControllerPairingController::Stage BluetoothControllerPairingController::GetCurrentStage() { return current_stage_; } void BluetoothControllerPairingController::StartPairing() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(current_stage_ == STAGE_NONE || current_stage_ == STAGE_DEVICE_NOT_FOUND || current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR || current_stage_ == STAGE_HOST_ENROLLMENT_ERROR); if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { ChangeStage(STAGE_INITIALIZATION_ERROR); return; } device::BluetoothAdapterFactory::GetAdapter( base::Bind(&BluetoothControllerPairingController::OnGetAdapter, ptr_factory_.GetWeakPtr())); } ControllerPairingController::DeviceIdList BluetoothControllerPairingController::GetDiscoveredDevices() { DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY); return DeviceIdList(discovered_devices_.begin(), discovered_devices_.end()); } void BluetoothControllerPairingController::ChooseDeviceForPairing( const std::string& device_id) { DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY); DCHECK(discovered_devices_.count(device_id)); discovery_session_.reset(); controller_device_id_ = device_id; device::BluetoothDevice* device = GetController(); if (device) { ChangeStage(STAGE_ESTABLISHING_CONNECTION); if (device->IsPaired()) { OnConnect(); } else { device->Connect( this, base::Bind(&BluetoothControllerPairingController::OnConnect, ptr_factory_.GetWeakPtr()), base::Bind(&BluetoothControllerPairingController::OnConnectError, ptr_factory_.GetWeakPtr())); } } } void BluetoothControllerPairingController::RepeatDiscovery() { DCHECK(current_stage_ == STAGE_DEVICE_NOT_FOUND || current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR || current_stage_ == STAGE_HOST_ENROLLMENT_ERROR); Reset(); StartPairing(); } std::string BluetoothControllerPairingController::GetConfirmationCode() { DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CODE_CONFIRMATION); DCHECK(!confirmation_code_.empty()); return confirmation_code_; } void BluetoothControllerPairingController::SetConfirmationCodeIsCorrect( bool correct) { DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CODE_CONFIRMATION); device::BluetoothDevice* device = GetController(); if (!device) return; if (correct) { device->ConfirmPairing(); // Once pairing is confirmed, the connection will either be successful, or // fail. Either case is acceptable as long as the devices are paired. } else { device->RejectPairing(); controller_device_id_.clear(); RepeatDiscovery(); } } void BluetoothControllerPairingController::SetHostConfiguration( bool accepted_eula, const std::string& lang, const std::string& timezone, bool send_reports, const std::string& keyboard_layout) { VLOG(1) << "SetHostConfiguration lang=" << lang << ", timezone=" << timezone << ", keyboard_layout=" << keyboard_layout; pairing_api::ConfigureHost host_config; host_config.set_api_version(kPairingAPIVersion); host_config.mutable_parameters()->set_accepted_eula(accepted_eula); host_config.mutable_parameters()->set_lang(lang); host_config.mutable_parameters()->set_timezone(timezone); host_config.mutable_parameters()->set_send_reports(send_reports); host_config.mutable_parameters()->set_keyboard_layout(keyboard_layout); int size = 0; scoped_refptr io_buffer( ProtoDecoder::SendConfigureHost(host_config, &size)); SendBuffer(io_buffer, size); } void BluetoothControllerPairingController::OnAuthenticationDone( const std::string& domain, const std::string& auth_token) { DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CREDENTIALS); pairing_api::PairDevices pair_devices; pair_devices.set_api_version(kPairingAPIVersion); pair_devices.mutable_parameters()->set_admin_access_token(auth_token); int size = 0; scoped_refptr io_buffer( ProtoDecoder::SendPairDevices(pair_devices, &size)); SendBuffer(io_buffer, size); ChangeStage(STAGE_HOST_ENROLLMENT_IN_PROGRESS); } void BluetoothControllerPairingController::StartSession() { DCHECK_EQ(current_stage_, STAGE_HOST_ENROLLMENT_SUCCESS); ChangeStage(STAGE_FINISHED); } void BluetoothControllerPairingController::OnHostStatusMessage( const pairing_api::HostStatus& message) { pairing_api::HostStatusParameters::UpdateStatus update_status = message.parameters().update_status(); pairing_api::HostStatusParameters::EnrollmentStatus enrollment_status = message.parameters().enrollment_status(); VLOG(1) << "OnHostStatusMessage, update_status=" << update_status; // TODO(zork): Check domain. (http://crbug.com/405761) if (enrollment_status == pairing_api::HostStatusParameters::ENROLLMENT_STATUS_SUCCESS) { // TODO(achuith, zork): Need to ensure that controller has also successfully // enrolled. CompleteSetup(); } else if (update_status == pairing_api::HostStatusParameters::UPDATE_STATUS_UPDATING) { ChangeStage(STAGE_HOST_UPDATE_IN_PROGRESS); } else if (update_status == pairing_api::HostStatusParameters::UPDATE_STATUS_UPDATED) { ChangeStage(STAGE_WAITING_FOR_CREDENTIALS); } } void BluetoothControllerPairingController::CompleteSetup() { pairing_api::CompleteSetup complete_setup; complete_setup.set_api_version(kPairingAPIVersion); // TODO(zork): Get AddAnother from UI (http://crbug.com/405757) complete_setup.mutable_parameters()->set_add_another(false); int size = 0; scoped_refptr io_buffer( ProtoDecoder::SendCompleteSetup(complete_setup, &size)); SendBuffer(io_buffer, size); ChangeStage(STAGE_HOST_ENROLLMENT_SUCCESS); } void BluetoothControllerPairingController::OnConfigureHostMessage( const pairing_api::ConfigureHost& message) { NOTREACHED(); } void BluetoothControllerPairingController::OnPairDevicesMessage( const pairing_api::PairDevices& message) { NOTREACHED(); } void BluetoothControllerPairingController::OnCompleteSetupMessage( const pairing_api::CompleteSetup& message) { NOTREACHED(); } void BluetoothControllerPairingController::OnErrorMessage( const pairing_api::Error& message) { LOG(ERROR) << message.parameters().code() << ", " << message.parameters().description(); ChangeStage(STAGE_HOST_ENROLLMENT_ERROR); } void BluetoothControllerPairingController::DeviceAdded( device::BluetoothAdapter* adapter, device::BluetoothDevice* device) { DCHECK_EQ(adapter, adapter_.get()); DeviceFound(device); } void BluetoothControllerPairingController::DeviceRemoved( device::BluetoothAdapter* adapter, device::BluetoothDevice* device) { DCHECK_EQ(adapter, adapter_.get()); DeviceLost(device); } void BluetoothControllerPairingController::RequestPinCode( device::BluetoothDevice* device) { // Disallow unknown device. device->RejectPairing(); } void BluetoothControllerPairingController::RequestPasskey( device::BluetoothDevice* device) { // Disallow unknown device. device->RejectPairing(); } void BluetoothControllerPairingController::DisplayPinCode( device::BluetoothDevice* device, const std::string& pincode) { // Disallow unknown device. device->RejectPairing(); } void BluetoothControllerPairingController::DisplayPasskey( device::BluetoothDevice* device, uint32 passkey) { // Disallow unknown device. device->RejectPairing(); } void BluetoothControllerPairingController::KeysEntered( device::BluetoothDevice* device, uint32 entered) { // Disallow unknown device. device->RejectPairing(); } void BluetoothControllerPairingController::ConfirmPasskey( device::BluetoothDevice* device, uint32 passkey) { confirmation_code_ = base::StringPrintf("%06d", passkey); ChangeStage(STAGE_WAITING_FOR_CODE_CONFIRMATION); } void BluetoothControllerPairingController::AuthorizePairing( device::BluetoothDevice* device) { // Disallow unknown device. device->RejectPairing(); } } // namespace pairing_chromeos