1// Copyright 2014 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.
4
5#include "components/pairing/bluetooth_controller_pairing_controller.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/strings/string_util.h"
10#include "base/strings/stringprintf.h"
11#include "base/strings/utf_string_conversions.h"
12#include "components/pairing/bluetooth_pairing_constants.h"
13#include "components/pairing/pairing_api.pb.h"
14#include "device/bluetooth/bluetooth_adapter_factory.h"
15#include "device/bluetooth/bluetooth_discovery_session.h"
16#include "net/base/io_buffer.h"
17
18namespace {
19const int kReceiveSize = 16384;
20}
21
22namespace pairing_chromeos {
23
24BluetoothControllerPairingController::BluetoothControllerPairingController()
25    : current_stage_(STAGE_NONE),
26      got_initial_status_(false),
27      proto_decoder_(new ProtoDecoder(this)),
28      ptr_factory_(this) {
29}
30
31BluetoothControllerPairingController::~BluetoothControllerPairingController() {
32  Reset();
33}
34
35device::BluetoothDevice* BluetoothControllerPairingController::GetController() {
36  DCHECK(!controller_device_id_.empty());
37  device::BluetoothDevice* device = adapter_->GetDevice(controller_device_id_);
38  if (!device) {
39    LOG(ERROR) << "Lost connection to controller.";
40    ChangeStage(STAGE_ESTABLISHING_CONNECTION_ERROR);
41  }
42
43  return device;
44}
45
46void BluetoothControllerPairingController::ChangeStage(Stage new_stage) {
47  if (current_stage_ == new_stage)
48    return;
49  current_stage_ = new_stage;
50  FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_,
51                    PairingStageChanged(new_stage));
52}
53
54void BluetoothControllerPairingController::Reset() {
55  got_initial_status_ = false;
56  controller_device_id_.clear();
57  discovery_session_.reset();
58
59  if (socket_.get()) {
60    socket_->Close();
61    socket_ = NULL;
62  }
63
64  if (adapter_.get()) {
65    adapter_->RemoveObserver(this);
66    adapter_ = NULL;
67  }
68}
69
70void BluetoothControllerPairingController::DeviceFound(
71    device::BluetoothDevice* device) {
72  DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY);
73  DCHECK(thread_checker_.CalledOnValidThread());
74  if (StartsWith(device->GetName(), base::ASCIIToUTF16(kDeviceNamePrefix),
75                 false)) {
76    discovered_devices_.insert(device->GetAddress());
77    FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_,
78                      DiscoveredDevicesListChanged());
79  }
80}
81
82void BluetoothControllerPairingController::DeviceLost(
83    device::BluetoothDevice* device) {
84  DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY);
85  DCHECK(thread_checker_.CalledOnValidThread());
86  std::set<std::string>::iterator ix =
87      discovered_devices_.find(device->GetAddress());
88  if (ix != discovered_devices_.end()) {
89    discovered_devices_.erase(ix);
90    FOR_EACH_OBSERVER(ControllerPairingController::Observer, observers_,
91                      DiscoveredDevicesListChanged());
92  }
93}
94
95void BluetoothControllerPairingController::SendBuffer(
96    scoped_refptr<net::IOBuffer> io_buffer, int size) {
97  socket_->Send(
98      io_buffer, size,
99      base::Bind(&BluetoothControllerPairingController::OnSendComplete,
100                 ptr_factory_.GetWeakPtr()),
101      base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage,
102                 ptr_factory_.GetWeakPtr()));
103}
104
105void BluetoothControllerPairingController::OnSetPowered() {
106  DCHECK(thread_checker_.CalledOnValidThread());
107  adapter_->StartDiscoverySession(
108      base::Bind(&BluetoothControllerPairingController::OnStartDiscoverySession,
109                 ptr_factory_.GetWeakPtr()),
110      base::Bind(&BluetoothControllerPairingController::OnError,
111                 ptr_factory_.GetWeakPtr()));
112}
113
114void BluetoothControllerPairingController::OnGetAdapter(
115    scoped_refptr<device::BluetoothAdapter> adapter) {
116  DCHECK(thread_checker_.CalledOnValidThread());
117  DCHECK(!adapter_.get());
118  adapter_ = adapter;
119  adapter_->AddObserver(this);
120
121  if (adapter_->IsPowered()) {
122    OnSetPowered();
123  } else {
124    adapter_->SetPowered(
125        true,
126        base::Bind(&BluetoothControllerPairingController::OnSetPowered,
127                   ptr_factory_.GetWeakPtr()),
128        base::Bind(&BluetoothControllerPairingController::OnError,
129                   ptr_factory_.GetWeakPtr()));
130  }
131}
132
133void BluetoothControllerPairingController::OnStartDiscoverySession(
134    scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
135  DCHECK(thread_checker_.CalledOnValidThread());
136  discovery_session_ = discovery_session.Pass();
137  ChangeStage(STAGE_DEVICES_DISCOVERY);
138
139  device::BluetoothAdapter::DeviceList device_list = adapter_->GetDevices();
140  for (device::BluetoothAdapter::DeviceList::iterator ix = device_list.begin();
141       ix != device_list.end(); ++ix) {
142    DeviceFound(*ix);
143  }
144}
145
146void BluetoothControllerPairingController::OnConnect() {
147  DCHECK(thread_checker_.CalledOnValidThread());
148  device::BluetoothDevice* device = GetController();
149  if (device) {
150    device->ConnectToService(
151        device::BluetoothUUID(kPairingServiceUUID),
152        base::Bind(&BluetoothControllerPairingController::OnConnectToService,
153                   ptr_factory_.GetWeakPtr()),
154        base::Bind(&BluetoothControllerPairingController::OnErrorWithMessage,
155                   ptr_factory_.GetWeakPtr()));
156  }
157}
158
159void BluetoothControllerPairingController::OnConnectToService(
160    scoped_refptr<device::BluetoothSocket> socket) {
161  DCHECK(thread_checker_.CalledOnValidThread());
162  socket_ = socket;
163
164  socket_->Receive(
165      kReceiveSize,
166      base::Bind(&BluetoothControllerPairingController::OnReceiveComplete,
167                 ptr_factory_.GetWeakPtr()),
168      base::Bind(&BluetoothControllerPairingController::OnReceiveError,
169                 ptr_factory_.GetWeakPtr()));
170}
171
172void BluetoothControllerPairingController::OnSendComplete(int bytes_sent) {}
173
174void BluetoothControllerPairingController::OnReceiveComplete(
175    int bytes, scoped_refptr<net::IOBuffer> io_buffer) {
176  DCHECK(thread_checker_.CalledOnValidThread());
177  proto_decoder_->DecodeIOBuffer(bytes, io_buffer);
178
179  socket_->Receive(
180      kReceiveSize,
181      base::Bind(&BluetoothControllerPairingController::OnReceiveComplete,
182                 ptr_factory_.GetWeakPtr()),
183      base::Bind(&BluetoothControllerPairingController::OnReceiveError,
184                 ptr_factory_.GetWeakPtr()));
185}
186
187void BluetoothControllerPairingController::OnError() {
188  LOG(ERROR) << "Pairing initialization failed";
189  ChangeStage(STAGE_INITIALIZATION_ERROR);
190  Reset();
191}
192
193void BluetoothControllerPairingController::OnErrorWithMessage(
194    const std::string& message) {
195  LOG(ERROR) << message;
196  ChangeStage(STAGE_INITIALIZATION_ERROR);
197  Reset();
198}
199
200void BluetoothControllerPairingController::OnConnectError(
201    device::BluetoothDevice::ConnectErrorCode error_code) {
202  DCHECK(thread_checker_.CalledOnValidThread());
203  device::BluetoothDevice* device = GetController();
204
205  if (device && device->IsPaired()) {
206    // The connection attempt is only used to start the pairing between the
207    // devices.  If the connection fails, it's not a problem as long as pairing
208    // was successful.
209    OnConnect();
210  }
211}
212
213void BluetoothControllerPairingController::OnReceiveError(
214    device::BluetoothSocket::ErrorReason reason,
215    const std::string& error_message) {
216  LOG(ERROR) << reason << ", " << error_message;
217  Reset();
218}
219
220void BluetoothControllerPairingController::AddObserver(
221    ControllerPairingController::Observer* observer) {
222  observers_.AddObserver(observer);
223}
224
225void BluetoothControllerPairingController::RemoveObserver(
226    ControllerPairingController::Observer* observer) {
227  observers_.RemoveObserver(observer);
228}
229
230ControllerPairingController::Stage
231BluetoothControllerPairingController::GetCurrentStage() {
232  return current_stage_;
233}
234
235void BluetoothControllerPairingController::StartPairing() {
236  DCHECK(thread_checker_.CalledOnValidThread());
237  DCHECK(current_stage_ == STAGE_NONE ||
238         current_stage_ == STAGE_DEVICE_NOT_FOUND ||
239         current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR ||
240         current_stage_ == STAGE_HOST_ENROLLMENT_ERROR);
241  if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
242    ChangeStage(STAGE_INITIALIZATION_ERROR);
243    return;
244  }
245
246  device::BluetoothAdapterFactory::GetAdapter(
247      base::Bind(&BluetoothControllerPairingController::OnGetAdapter,
248                 ptr_factory_.GetWeakPtr()));
249
250}
251
252ControllerPairingController::DeviceIdList
253BluetoothControllerPairingController::GetDiscoveredDevices() {
254  DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY);
255  return DeviceIdList(discovered_devices_.begin(), discovered_devices_.end());
256}
257
258void BluetoothControllerPairingController::ChooseDeviceForPairing(
259    const std::string& device_id) {
260  DCHECK_EQ(current_stage_, STAGE_DEVICES_DISCOVERY);
261  DCHECK(discovered_devices_.count(device_id));
262  discovery_session_.reset();
263  controller_device_id_ = device_id;
264
265  device::BluetoothDevice* device = GetController();
266
267  if (device) {
268    ChangeStage(STAGE_ESTABLISHING_CONNECTION);
269    if (device->IsPaired()) {
270      OnConnect();
271    } else {
272      device->Connect(
273          this,
274          base::Bind(&BluetoothControllerPairingController::OnConnect,
275                     ptr_factory_.GetWeakPtr()),
276          base::Bind(&BluetoothControllerPairingController::OnConnectError,
277                     ptr_factory_.GetWeakPtr()));
278    }
279  }
280}
281
282void BluetoothControllerPairingController::RepeatDiscovery() {
283  DCHECK(current_stage_ == STAGE_DEVICE_NOT_FOUND ||
284         current_stage_ == STAGE_ESTABLISHING_CONNECTION_ERROR ||
285         current_stage_ == STAGE_HOST_ENROLLMENT_ERROR);
286  Reset();
287  StartPairing();
288}
289
290std::string BluetoothControllerPairingController::GetConfirmationCode() {
291  DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CODE_CONFIRMATION);
292  DCHECK(!confirmation_code_.empty());
293  return confirmation_code_;
294}
295
296void BluetoothControllerPairingController::SetConfirmationCodeIsCorrect(
297    bool correct) {
298  DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CODE_CONFIRMATION);
299
300  device::BluetoothDevice* device = GetController();
301  if (!device)
302    return;
303
304  if (correct) {
305    device->ConfirmPairing();
306    // Once pairing is confirmed, the connection will either be successful, or
307    // fail.  Either case is acceptable as long as the devices are paired.
308  } else {
309    device->RejectPairing();
310    controller_device_id_.clear();
311    RepeatDiscovery();
312  }
313}
314
315void BluetoothControllerPairingController::SetHostConfiguration(
316    bool accepted_eula,
317    const std::string& lang,
318    const std::string& timezone,
319    bool send_reports,
320    const std::string& keyboard_layout) {
321
322  pairing_api::ConfigureHost host_config;
323  host_config.set_api_version(kPairingAPIVersion);
324  host_config.mutable_parameters()->set_accepted_eula(accepted_eula);
325  host_config.mutable_parameters()->set_lang(lang);
326  host_config.mutable_parameters()->set_timezone(timezone);
327  host_config.mutable_parameters()->set_send_reports(send_reports);
328  host_config.mutable_parameters()->set_keyboard_layout(keyboard_layout);
329
330  int size = 0;
331  scoped_refptr<net::IOBuffer> io_buffer(
332      ProtoDecoder::SendConfigureHost(host_config, &size));
333
334  SendBuffer(io_buffer, size);
335}
336
337void BluetoothControllerPairingController::OnAuthenticationDone(
338    const std::string& domain,
339    const std::string& auth_token) {
340  DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CREDENTIALS);
341
342  pairing_api::PairDevices pair_devices;
343  pair_devices.set_api_version(kPairingAPIVersion);
344  pair_devices.mutable_parameters()->set_admin_access_token(auth_token);
345
346  int size = 0;
347  scoped_refptr<net::IOBuffer> io_buffer(
348      ProtoDecoder::SendPairDevices(pair_devices, &size));
349
350  SendBuffer(io_buffer, size);
351  ChangeStage(STAGE_HOST_ENROLLMENT_IN_PROGRESS);
352}
353
354void BluetoothControllerPairingController::StartSession() {
355  DCHECK_EQ(current_stage_, STAGE_PAIRING_DONE);
356  ChangeStage(STAGE_FINISHED);
357}
358
359// ProtoDecoder::Observer:
360void BluetoothControllerPairingController::OnHostStatusMessage(
361    const pairing_api::HostStatus& message) {
362  if (got_initial_status_) {
363    // TODO(zork): Check that the domain matches. (http://crbug.com/405761)
364    // TODO(zork): Handling updating stages (http://crbug.com/405754).
365    pairing_api::CompleteSetup complete_setup;
366    complete_setup.set_api_version(kPairingAPIVersion);
367    // TODO(zork): Get AddAnother from UI (http://crbug.com/405757)
368    complete_setup.mutable_parameters()->set_add_another(false);
369
370    int size = 0;
371    scoped_refptr<net::IOBuffer> io_buffer(
372        ProtoDecoder::SendCompleteSetup(complete_setup, &size));
373
374    SendBuffer(io_buffer, size);
375    ChangeStage(STAGE_PAIRING_DONE);
376  } else {
377    got_initial_status_ = true;
378
379    // TODO(zork): Check domain. (http://crbug.com/405761)
380    ChangeStage(STAGE_WAITING_FOR_CREDENTIALS);
381  }
382}
383
384void BluetoothControllerPairingController::OnConfigureHostMessage(
385    const pairing_api::ConfigureHost& message) {
386  NOTREACHED();
387}
388
389void BluetoothControllerPairingController::OnPairDevicesMessage(
390    const pairing_api::PairDevices& message) {
391  NOTREACHED();
392}
393
394void BluetoothControllerPairingController::OnCompleteSetupMessage(
395    const pairing_api::CompleteSetup& message) {
396  NOTREACHED();
397}
398
399void BluetoothControllerPairingController::OnErrorMessage(
400    const pairing_api::Error& message) {
401  LOG(ERROR) << message.parameters().code() << ", " <<
402      message.parameters().description();
403  ChangeStage(STAGE_HOST_ENROLLMENT_ERROR);
404}
405
406void BluetoothControllerPairingController::DeviceAdded(
407    device::BluetoothAdapter* adapter,
408    device::BluetoothDevice* device) {
409  DCHECK_EQ(adapter, adapter_.get());
410  DeviceFound(device);
411}
412
413void BluetoothControllerPairingController::DeviceRemoved(
414    device::BluetoothAdapter* adapter,
415    device::BluetoothDevice* device) {
416  DCHECK_EQ(adapter, adapter_.get());
417  DeviceLost(device);
418}
419
420void BluetoothControllerPairingController::RequestPinCode(
421    device::BluetoothDevice* device) {
422  // Disallow unknown device.
423  device->RejectPairing();
424}
425
426void BluetoothControllerPairingController::RequestPasskey(
427    device::BluetoothDevice* device) {
428  // Disallow unknown device.
429  device->RejectPairing();
430}
431
432void BluetoothControllerPairingController::DisplayPinCode(
433    device::BluetoothDevice* device,
434    const std::string& pincode) {
435  // Disallow unknown device.
436  device->RejectPairing();
437}
438
439void BluetoothControllerPairingController::DisplayPasskey(
440    device::BluetoothDevice* device,
441    uint32 passkey) {
442  // Disallow unknown device.
443  device->RejectPairing();
444}
445
446void BluetoothControllerPairingController::KeysEntered(
447    device::BluetoothDevice* device,
448    uint32 entered) {
449  // Disallow unknown device.
450  device->RejectPairing();
451}
452
453void BluetoothControllerPairingController::ConfirmPasskey(
454    device::BluetoothDevice* device,
455    uint32 passkey) {
456  confirmation_code_ = base::StringPrintf("%06d", passkey);
457  ChangeStage(STAGE_WAITING_FOR_CODE_CONFIRMATION);
458}
459
460void BluetoothControllerPairingController::AuthorizePairing(
461    device::BluetoothDevice* device) {
462  // Disallow unknown device.
463  device->RejectPairing();
464}
465
466}  // namespace pairing_chromeos
467