Index: components/pairing/bluetooth_controller_pairing_controller.cc |
diff --git a/components/pairing/bluetooth_controller_pairing_controller.cc b/components/pairing/bluetooth_controller_pairing_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b95410d96fd0bb0ac289fd4e8759384230fdc183 |
--- /dev/null |
+++ b/components/pairing/bluetooth_controller_pairing_controller.cc |
@@ -0,0 +1,460 @@ |
+// 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 char* kFakeEnrollmentDomain = "http://fake.com"; |
+const int kReceiveSize = 16384; |
+} |
+ |
+namespace pairing_chromeos { |
+ |
+BluetoothControllerPairingController::BluetoothControllerPairingController() |
+ : current_stage_(STAGE_NONE), |
+ got_initial_status_(false), |
+ proto_decoder_(new ProtoDecoder(this)), |
+ ptr_factory_(this) { |
+} |
+ |
+BluetoothControllerPairingController::~BluetoothControllerPairingController() { |
+ Reset(); |
+} |
+ |
+void BluetoothControllerPairingController::ChangeStage(Stage new_stage) { |
+ if (current_stage_ == new_stage) |
+ return; |
+ current_stage_ = new_stage; |
+ FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_, |
+ PairingStageChanged(new_stage)); |
+} |
+ |
+void BluetoothControllerPairingController::Reset() { |
+ got_initial_status_ = false; |
+ controller_device_id_ = ""; |
achuithb
2014/08/21 07:08:58
nit: I prefer clear() to this assignment.
Zachary Kuznia
2014/08/26 19:29:44
Done.
|
+ discovery_session_.reset(); |
+ |
+ if (socket_) { |
+ socket_->Close(); |
+ socket_ = NULL; |
+ } |
+ |
+ if (adapter_) { |
+ 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<std::string>::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::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<device::BluetoothAdapter> adapter) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(!adapter_); |
+ 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<device::BluetoothDiscoverySession> discovery_session) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ discovery_session_ = discovery_session.Pass(); |
+ ChangeStage(STAGE_DEVICES_DISCOVERY); |
+ |
+ device::BluetoothAdapter::DeviceList device_list = adapter_->GetDevices(); |
+ for (device::BluetoothAdapter::DeviceList::iterator ix = device_list.begin(); |
+ ix != device_list.end(); ++ix) { |
+ DeviceFound(*ix); |
+ } |
+} |
+ |
+void BluetoothControllerPairingController::OnConnect() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ device::BluetoothDevice* device = adapter_->GetDevice(controller_device_id_); |
achuithb
2014/08/21 07:08:58
You're using this pattern often enough that you sh
Zachary Kuznia
2014/08/26 19:29:44
Done.
|
+ if (!device) { |
+ ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR); |
achuithb
2014/08/21 07:08:58
Should this log an error?
Zachary Kuznia
2014/08/26 19:29:44
Put the LOG in the shared function.
|
+ return; |
+ } |
+ |
+ device->ConnectToService( |
+ device::BluetoothUUID(kPairingServiceUUID), |
+ base::Bind(&BluetoothControllerPairingController::OnConnectToService, |
+ ptr_factory_.GetWeakPtr()), |
+ base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage, |
+ ptr_factory_.GetWeakPtr())); |
+} |
+ |
+void BluetoothControllerPairingController::OnConnectToService( |
+ scoped_refptr<device::BluetoothSocket> socket) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ socket_ = socket; |
+ |
+ socket_->Receive( |
+ kReceiveSize, |
+ base::Bind(&BluetoothControllerPairingController::OnReceiveComplete, |
+ ptr_factory_.GetWeakPtr()), |
+ base::Bind(&BluetoothControllerPairingController::OnReceiveError, |
+ ptr_factory_.GetWeakPtr())); |
+} |
+ |
+void BluetoothControllerPairingController::OnSendComplete(int bytes_sent) {} |
+ |
+void BluetoothControllerPairingController::OnReceiveComplete( |
+ int bytes, scoped_refptr<net::IOBuffer> 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() { |
+ // TODO(zork): Add a stage for initialization error. (http://crbug.com/405744) |
+ LOG(ERROR) << "Pairing initialization failed"; |
+ Reset(); |
+} |
+ |
+void BluetoothControllerPairingController::OnErrorWithMessage( |
+ const std::string& message) { |
+ // TODO(zork): Add a stage for initialization error. (http://crbug.com/405744) |
+ LOG(ERROR) << message; |
+ Reset(); |
+} |
+ |
+void BluetoothControllerPairingController::OnConnectError( |
+ device::BluetoothDevice::ConnectErrorCode error_code) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ device::BluetoothDevice* device = adapter_->GetDevice(controller_device_id_); |
+ 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 { |
+ 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); |
+ // TODO(zork): Add a stage for no bluetooth. (http://crbug.com/405744) |
+ if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) { |
+ ChangeStage(STAGE_DEVICE_NOT_FOUND); |
+ 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 = adapter_->GetDevice(controller_device_id_); |
+ |
+ 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())); |
+ } |
+ } else { |
+ ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR); |
+ } |
+} |
+ |
+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 = adapter_->GetDevice(controller_device_id_); |
+ if (!device) { |
+ ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR); |
+ 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_ = ""; |
achuithb
2014/08/21 07:08:58
nit: clear()
Zachary Kuznia
2014/08/26 19:29:44
Done.
|
+ RepeatDiscovery(); |
+ } |
+} |
+ |
+void BluetoothControllerPairingController::OnAuthenticationDone( |
+ const chromeos::UserContext& user_context, |
+ content::BrowserContext* browser_context) { |
+ DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CREDENTIALS); |
+ |
+ // TODO(zork): Get configuration from UI and send to Host. |
+ // (http://crbug.com/405744) |
+ |
+ // TODO(zork): Get proper credentials. (http://crbug.com/405744) |
+ // For now, send a fake domain. |
+ pairing_api::PairDevices pair_devices; |
+ pair_devices.set_api_version(kPairingAPIVersion); |
+ pair_devices.mutable_parameters()->set_admin_access_token( |
+ kFakeEnrollmentDomain); |
+ |
+ int size = 0; |
+ scoped_refptr<net::IOBuffer> io_buffer( |
+ ProtoDecoder::SendPairDevices(pair_devices, &size)); |
+ |
+ socket_->Send( |
+ io_buffer, size, |
+ base::Bind(&BluetoothControllerPairingController::OnSendComplete, |
+ ptr_factory_.GetWeakPtr()), |
+ base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage, |
+ ptr_factory_.GetWeakPtr())); |
+ ChangeStage(STAGE_HOST_ENROLLMENT_IN_PROGRESS); |
+} |
+ |
+void BluetoothControllerPairingController::StartSession() { |
+ DCHECK_EQ(current_stage_, STAGE_PAIRING_DONE); |
+ ChangeStage(STAGE_FINISHED); |
+} |
+ |
+// ProtoDecoder::Observer: |
+void BluetoothControllerPairingController::OnHostStatusMessage( |
+ const pairing_api::HostStatus& message) { |
+ if (got_initial_status_) { |
+ if (message.parameters().has_domain()) { |
+ // TODO(zork): Remove this if we don't actually need the domain for UI. |
+ // (http://crbug.com/405761) |
+ if (message.parameters().domain() == kFakeEnrollmentDomain) { |
+ 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<net::IOBuffer> io_buffer( |
+ ProtoDecoder::SendCompleteSetup(complete_setup, &size)); |
+ |
+ socket_->Send( |
+ io_buffer, size, |
+ base::Bind(&BluetoothControllerPairingController::OnSendComplete, |
+ ptr_factory_.GetWeakPtr()), |
+ base::Bind( |
+ &BluetoothControllerPairingController::OnErrorWithMessage, |
+ ptr_factory_.GetWeakPtr())); |
+ ChangeStage(STAGE_PAIRING_DONE); |
+ } else { |
+ ChangeStage(STAGE_HOST_ENROLLMENT_ERROR); |
+ } |
+ } else { |
+ ChangeStage(STAGE_HOST_ENROLLMENT_ERROR); |
+ } |
+ } else { |
+ got_initial_status_ = true; |
+ |
+ // TODO(zork): Check domain. (http://crbug.com/405761) |
+ |
+ // TODO(zork): Handling updating stages (http://crbug.com/405754). |
+ ChangeStage(STAGE_WAITING_FOR_CREDENTIALS); |
+ } |
+} |
+ |
+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 |