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_host_pairing_controller.h"
6
7#include "base/bind.h"
8#include "base/hash.h"
9#include "base/logging.h"
10#include "base/strings/stringprintf.h"
11#include "components/pairing/bluetooth_pairing_constants.h"
12#include "components/pairing/pairing_api.pb.h"
13#include "components/pairing/proto_decoder.h"
14#include "device/bluetooth/bluetooth_adapter_factory.h"
15#include "net/base/io_buffer.h"
16
17namespace {
18const int kReceiveSize = 16384;
19}
20
21namespace pairing_chromeos {
22
23BluetoothHostPairingController::BluetoothHostPairingController()
24    : current_stage_(STAGE_NONE),
25      device_(NULL),
26      proto_decoder_(new ProtoDecoder(this)),
27      ptr_factory_(this) {
28}
29
30BluetoothHostPairingController::~BluetoothHostPairingController() {
31  if (adapter_.get()) {
32    if (adapter_->IsDiscoverable()) {
33      adapter_->SetDiscoverable(false, base::Closure(), base::Closure());
34    }
35    adapter_->RemoveObserver(this);
36    adapter_ = NULL;
37  }
38}
39
40void BluetoothHostPairingController::ChangeStage(Stage new_stage) {
41  if (current_stage_ == new_stage)
42    return;
43  current_stage_ = new_stage;
44  FOR_EACH_OBSERVER(Observer, observers_, PairingStageChanged(new_stage));
45}
46
47void BluetoothHostPairingController::SendHostStatus() {
48  pairing_api::HostStatus host_status;
49
50  host_status.set_api_version(kPairingAPIVersion);
51  if (!enrollment_domain_.empty())
52    host_status.mutable_parameters()->set_domain(enrollment_domain_);
53
54  // TODO(zork): Get these values from the UI. (http://crbug.com/405744)
55  host_status.mutable_parameters()->set_connectivity(
56      pairing_api::HostStatusParameters::CONNECTIVITY_CONNECTED);
57  host_status.mutable_parameters()->set_update_status(
58      pairing_api::HostStatusParameters::UPDATE_STATUS_UPDATED);
59
60  // TODO(zork): Get a list of other paired controllers.
61  // (http://crbug.com/405757)
62
63  int size = 0;
64  scoped_refptr<net::IOBuffer> io_buffer(
65      ProtoDecoder::SendHostStatus(host_status, &size));
66
67  controller_socket_->Send(
68      io_buffer, size,
69      base::Bind(&BluetoothHostPairingController::OnSendComplete,
70                 ptr_factory_.GetWeakPtr()),
71      base::Bind(&BluetoothHostPairingController::OnSendError,
72                 ptr_factory_.GetWeakPtr()));
73}
74
75void BluetoothHostPairingController::AbortWithError(
76    int code,
77    const std::string& message) {
78  if (controller_socket_.get()) {
79    pairing_api::Error error;
80
81    error.set_api_version(kPairingAPIVersion);
82    error.mutable_parameters()->set_code(PAIRING_ERROR_PAIRING_OR_ENROLLMENT);
83    error.mutable_parameters()->set_description(message);
84
85    int size = 0;
86    scoped_refptr<net::IOBuffer> io_buffer(
87        ProtoDecoder::SendError(error, &size));
88
89    controller_socket_->Send(
90        io_buffer, size,
91        base::Bind(&BluetoothHostPairingController::OnSendComplete,
92                   ptr_factory_.GetWeakPtr()),
93        base::Bind(&BluetoothHostPairingController::OnSendError,
94                   ptr_factory_.GetWeakPtr()));
95  }
96  Reset();
97}
98
99void BluetoothHostPairingController::Reset() {
100  if (controller_socket_.get()) {
101    controller_socket_->Close();
102    controller_socket_ = NULL;
103  }
104
105  if (service_socket_.get()) {
106    service_socket_->Close();
107    service_socket_ = NULL;
108  }
109  ChangeStage(STAGE_NONE);
110}
111
112void BluetoothHostPairingController::OnGetAdapter(
113    scoped_refptr<device::BluetoothAdapter> adapter) {
114  DCHECK(thread_checker_.CalledOnValidThread());
115  DCHECK(!adapter_.get());
116  adapter_ = adapter;
117
118  if (adapter_->IsPresent()) {
119    SetName();
120  } else {
121    // Set the name once the adapter is present.
122    adapter_->AddObserver(this);
123  }
124}
125
126void BluetoothHostPairingController::SetName() {
127  // Hash the bluetooth address and take the lower 2 bytes to create a human
128  // readable device name.
129  const uint32 device_id = base::Hash(adapter_->GetAddress()) & 0xFFFF;
130  device_name_ = base::StringPrintf("%s%04X", kDeviceNamePrefix, device_id);
131
132  adapter_->SetName(
133      device_name_,
134      base::Bind(&BluetoothHostPairingController::OnSetName,
135                 ptr_factory_.GetWeakPtr()),
136      base::Bind(&BluetoothHostPairingController::OnSetError,
137                 ptr_factory_.GetWeakPtr()));
138}
139
140void BluetoothHostPairingController::OnSetName() {
141  DCHECK(thread_checker_.CalledOnValidThread());
142  if (adapter_->IsPowered()) {
143    OnSetPowered();
144  } else {
145    adapter_->SetPowered(
146        true,
147        base::Bind(&BluetoothHostPairingController::OnSetPowered,
148                   ptr_factory_.GetWeakPtr()),
149        base::Bind(&BluetoothHostPairingController::OnSetError,
150                   ptr_factory_.GetWeakPtr()));
151  }
152}
153
154void BluetoothHostPairingController::OnSetPowered() {
155  DCHECK(thread_checker_.CalledOnValidThread());
156  adapter_->AddPairingDelegate(
157      this, device::BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);
158
159  device::BluetoothAdapter::ServiceOptions options;
160  options.name.reset(new std::string(kPairingServiceName));
161
162  adapter_->CreateRfcommService(
163      device::BluetoothUUID(kPairingServiceUUID), options,
164      base::Bind(&BluetoothHostPairingController::OnCreateService,
165                 ptr_factory_.GetWeakPtr()),
166      base::Bind(&BluetoothHostPairingController::OnCreateServiceError,
167                 ptr_factory_.GetWeakPtr()));
168}
169
170void BluetoothHostPairingController::OnCreateService(
171    scoped_refptr<device::BluetoothSocket> socket) {
172  DCHECK(thread_checker_.CalledOnValidThread());
173  service_socket_ = socket;
174
175  service_socket_->Accept(
176      base::Bind(&BluetoothHostPairingController::OnAccept,
177                 ptr_factory_.GetWeakPtr()),
178      base::Bind(&BluetoothHostPairingController::OnAcceptError,
179                 ptr_factory_.GetWeakPtr()));
180
181  adapter_->SetDiscoverable(
182      true,
183      base::Bind(&BluetoothHostPairingController::OnSetDiscoverable,
184                 ptr_factory_.GetWeakPtr(), true),
185      base::Bind(&BluetoothHostPairingController::OnSetError,
186                 ptr_factory_.GetWeakPtr()));
187}
188
189void BluetoothHostPairingController::OnAccept(
190    const device::BluetoothDevice* device,
191    scoped_refptr<device::BluetoothSocket> socket) {
192  DCHECK(thread_checker_.CalledOnValidThread());
193  adapter_->SetDiscoverable(
194      false,
195      base::Bind(&BluetoothHostPairingController::OnSetDiscoverable,
196                 ptr_factory_.GetWeakPtr(), false),
197      base::Bind(&BluetoothHostPairingController::OnSetError,
198                 ptr_factory_.GetWeakPtr()));
199
200  controller_socket_ = socket;
201  service_socket_ = NULL;
202
203  // TODO: Update Host. (http://crbug.com/405754)
204  SendHostStatus();
205
206  controller_socket_->Receive(
207      kReceiveSize,
208      base::Bind(&BluetoothHostPairingController::OnReceiveComplete,
209                 ptr_factory_.GetWeakPtr()),
210      base::Bind(&BluetoothHostPairingController::OnReceiveError,
211                 ptr_factory_.GetWeakPtr()));
212
213  ChangeStage(STAGE_WAITING_FOR_CREDENTIALS);
214}
215
216void BluetoothHostPairingController::OnSetDiscoverable(bool change_stage) {
217  DCHECK(thread_checker_.CalledOnValidThread());
218  if (change_stage) {
219    DCHECK_EQ(current_stage_, STAGE_NONE);
220    ChangeStage(STAGE_WAITING_FOR_CONTROLLER);
221  }
222}
223
224void BluetoothHostPairingController::OnSendComplete(int bytes_sent) {}
225
226void BluetoothHostPairingController::OnReceiveComplete(
227    int bytes, scoped_refptr<net::IOBuffer> io_buffer) {
228  DCHECK(thread_checker_.CalledOnValidThread());
229  proto_decoder_->DecodeIOBuffer(bytes, io_buffer);
230
231  controller_socket_->Receive(
232      kReceiveSize,
233      base::Bind(&BluetoothHostPairingController::OnReceiveComplete,
234                 ptr_factory_.GetWeakPtr()),
235      base::Bind(&BluetoothHostPairingController::OnReceiveError,
236                 ptr_factory_.GetWeakPtr()));
237}
238
239void BluetoothHostPairingController::OnCreateServiceError(
240    const std::string& message) {
241  LOG(ERROR) << message;
242  ChangeStage(STAGE_INITIALIZATION_ERROR);
243}
244
245void BluetoothHostPairingController::OnSetError() {
246  adapter_->RemovePairingDelegate(this);
247  ChangeStage(STAGE_INITIALIZATION_ERROR);
248}
249
250void BluetoothHostPairingController::OnAcceptError(
251    const std::string& error_message) {
252  LOG(ERROR) << error_message;
253}
254
255void BluetoothHostPairingController::OnSendError(
256    const std::string& error_message) {
257  LOG(ERROR) << error_message;
258}
259
260void BluetoothHostPairingController::OnReceiveError(
261    device::BluetoothSocket::ErrorReason reason,
262    const std::string& error_message) {
263  LOG(ERROR) << reason << ", " << error_message;
264}
265
266void BluetoothHostPairingController::OnHostStatusMessage(
267    const pairing_api::HostStatus& message) {
268  NOTREACHED();
269}
270
271void BluetoothHostPairingController::OnConfigureHostMessage(
272    const pairing_api::ConfigureHost& message) {
273  FOR_EACH_OBSERVER(Observer, observers_,
274                    ConfigureHost(message.parameters().accepted_eula(),
275                                  message.parameters().lang(),
276                                  message.parameters().timezone(),
277                                  message.parameters().send_reports(),
278                                  message.parameters().keyboard_layout()));
279}
280
281void BluetoothHostPairingController::OnPairDevicesMessage(
282    const pairing_api::PairDevices& message) {
283  DCHECK(thread_checker_.CalledOnValidThread());
284  if (current_stage_ != STAGE_WAITING_FOR_CREDENTIALS) {
285    AbortWithError(PAIRING_ERROR_PAIRING_OR_ENROLLMENT, kErrorInvalidProtocol);
286    return;
287  }
288
289  ChangeStage(STAGE_ENROLLING);
290  FOR_EACH_OBSERVER(Observer, observers_,
291                    EnrollHost(message.parameters().admin_access_token()));
292}
293
294void BluetoothHostPairingController::SetEnrollmentComplete(bool success) {
295  DCHECK_EQ(current_stage_, STAGE_ENROLLING);
296  DCHECK(thread_checker_.CalledOnValidThread());
297  if (success) {
298    ChangeStage(STAGE_PAIRING_DONE);
299    SendHostStatus();
300  } else {
301    AbortWithError(PAIRING_ERROR_PAIRING_OR_ENROLLMENT, kErrorEnrollmentFailed);
302  }
303}
304
305void BluetoothHostPairingController::OnCompleteSetupMessage(
306    const pairing_api::CompleteSetup& message) {
307  DCHECK(thread_checker_.CalledOnValidThread());
308  if (current_stage_ != STAGE_PAIRING_DONE) {
309    AbortWithError(PAIRING_ERROR_PAIRING_OR_ENROLLMENT, kErrorInvalidProtocol);
310    return;
311  }
312
313  // TODO(zork): Handle adding another controller. (http://crbug.com/405757)
314  ChangeStage(STAGE_FINISHED);
315}
316
317void BluetoothHostPairingController::OnErrorMessage(
318    const pairing_api::Error& message) {
319  NOTREACHED();
320}
321
322void BluetoothHostPairingController::AdapterPresentChanged(
323    device::BluetoothAdapter* adapter,
324    bool present) {
325  DCHECK_EQ(adapter, adapter_.get());
326  if (present) {
327    adapter_->RemoveObserver(this);
328    SetName();
329  }
330}
331
332void BluetoothHostPairingController::AddObserver(Observer* observer) {
333  observers_.AddObserver(observer);
334}
335
336void BluetoothHostPairingController::RemoveObserver(Observer* observer) {
337  observers_.RemoveObserver(observer);
338}
339
340HostPairingController::Stage BluetoothHostPairingController::GetCurrentStage() {
341  return current_stage_;
342}
343
344void BluetoothHostPairingController::StartPairing() {
345  DCHECK_EQ(current_stage_, STAGE_NONE);
346  bool bluetooth_available =
347      device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable();
348  if (!bluetooth_available) {
349    ChangeStage(STAGE_INITIALIZATION_ERROR);
350    return;
351  }
352
353  device::BluetoothAdapterFactory::GetAdapter(
354      base::Bind(&BluetoothHostPairingController::OnGetAdapter,
355                 ptr_factory_.GetWeakPtr()));
356}
357
358std::string BluetoothHostPairingController::GetDeviceName() {
359  return device_name_;
360}
361
362std::string BluetoothHostPairingController::GetConfirmationCode() {
363  DCHECK_EQ(current_stage_, STAGE_WAITING_FOR_CODE_CONFIRMATION);
364  return confirmation_code_;
365}
366
367std::string BluetoothHostPairingController::GetEnrollmentDomain() {
368  return enrollment_domain_;
369}
370
371void BluetoothHostPairingController::OnUpdateStatusChanged(
372    UpdateStatus update_status) {
373  // TODO(zork): Handling updating stages (http://crbug.com/405754).
374}
375
376void BluetoothHostPairingController::RequestPinCode(
377    device::BluetoothDevice* device) {
378  // Disallow unknown device.
379  device->RejectPairing();
380}
381
382void BluetoothHostPairingController::RequestPasskey(
383    device::BluetoothDevice* device) {
384  // Disallow unknown device.
385  device->RejectPairing();
386}
387
388void BluetoothHostPairingController::DisplayPinCode(
389    device::BluetoothDevice* device,
390    const std::string& pincode) {
391  // Disallow unknown device.
392  device->RejectPairing();
393}
394
395void BluetoothHostPairingController::DisplayPasskey(
396    device::BluetoothDevice* device,
397    uint32 passkey) {
398  // Disallow unknown device.
399  device->RejectPairing();
400}
401
402void BluetoothHostPairingController::KeysEntered(
403    device::BluetoothDevice* device,
404    uint32 entered) {
405  // Disallow unknown device.
406  device->RejectPairing();
407}
408
409void BluetoothHostPairingController::ConfirmPasskey(
410    device::BluetoothDevice* device,
411    uint32 passkey) {
412  confirmation_code_ = base::StringPrintf("%06d", passkey);
413  device->ConfirmPairing();
414  ChangeStage(STAGE_WAITING_FOR_CODE_CONFIRMATION);
415}
416
417void BluetoothHostPairingController::AuthorizePairing(
418    device::BluetoothDevice* device) {
419  // Disallow unknown device.
420  device->RejectPairing();
421}
422
423}  // namespace pairing_chromeos
424