1// Copyright (c) 2012 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 "chrome/browser/ui/webui/options/chromeos/bluetooth_options_handler.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/command_line.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "content/public/browser/web_ui.h"
15#include "device/bluetooth/bluetooth_adapter.h"
16#include "device/bluetooth/bluetooth_adapter_factory.h"
17#include "device/bluetooth/bluetooth_device.h"
18#include "grit/chromium_strings.h"
19#include "grit/generated_resources.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21#include "ui/base/l10n/l10n_util.h"
22
23namespace {
24
25// |UpdateDeviceCallback| takes a variable length list as an argument. The
26// value stored in each list element is indicated by the following constants.
27const int kUpdateDeviceAddressIndex = 0;
28const int kUpdateDeviceCommandIndex = 1;
29const int kUpdateDeviceAuthTokenIndex = 2;
30
31// |UpdateDeviceCallback| provides a command value of one of the following
32// constants that indicates what update it is providing to us.
33const char kConnectCommand[] = "connect";
34const char kCancelCommand[] = "cancel";
35const char kAcceptCommand[] = "accept";
36const char kRejectCommand[] = "reject";
37const char kDisconnectCommand[] = "disconnect";
38const char kForgetCommand[] = "forget";
39
40// |SendDeviceNotification| may include a pairing parameter whose value
41// is one of the following constants instructing the UI to perform a certain
42// action.
43const char kStartConnecting[] = "bluetoothStartConnecting";
44const char kEnterPinCode[] = "bluetoothEnterPinCode";
45const char kEnterPasskey[] = "bluetoothEnterPasskey";
46const char kRemotePinCode[] = "bluetoothRemotePinCode";
47const char kRemotePasskey[] = "bluetoothRemotePasskey";
48const char kConfirmPasskey[] = "bluetoothConfirmPasskey";
49
50// An invalid |entered| value to represent the "undefined" value.
51const int kInvalidEntered = 0xFFFF;
52
53}  // namespace
54
55namespace chromeos {
56namespace options {
57
58BluetoothOptionsHandler::BluetoothOptionsHandler()
59    : should_run_device_discovery_(false),
60      pairing_device_passkey_(1000000),
61      pairing_device_entered_(kInvalidEntered),
62      weak_ptr_factory_(this) {
63}
64
65BluetoothOptionsHandler::~BluetoothOptionsHandler() {
66  if (adapter_.get())
67    adapter_->RemoveObserver(this);
68}
69
70void BluetoothOptionsHandler::GetLocalizedValues(
71    base::DictionaryValue* localized_strings) {
72  DCHECK(localized_strings);
73
74  static OptionsStringResource resources[] = {
75    { "bluetooth", IDS_OPTIONS_SETTINGS_SECTION_TITLE_BLUETOOTH },
76    { "disableBluetooth", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISABLE },
77    { "enableBluetooth", IDS_OPTIONS_SETTINGS_BLUETOOTH_ENABLE },
78    { "addBluetoothDevice", IDS_OPTIONS_SETTINGS_ADD_BLUETOOTH_DEVICE },
79    { "bluetoothAddDeviceTitle",
80        IDS_OPTIONS_SETTINGS_BLUETOOTH_ADD_DEVICE_TITLE },
81    { "bluetoothOptionsPageTabTitle",
82        IDS_OPTIONS_SETTINGS_BLUETOOTH_ADD_DEVICE_TITLE },
83    { "findBluetoothDevices", IDS_OPTIONS_SETTINGS_FIND_BLUETOOTH_DEVICES },
84    { "bluetoothNoDevices", IDS_OPTIONS_SETTINGS_BLUETOOTH_NO_DEVICES },
85    { "bluetoothNoDevicesFound",
86        IDS_OPTIONS_SETTINGS_BLUETOOTH_NO_DEVICES_FOUND },
87    { "bluetoothScanning", IDS_OPTIONS_SETTINGS_BLUETOOTH_SCANNING },
88    { "bluetoothScanStopped", IDS_OPTIONS_SETTINGS_BLUETOOTH_SCAN_STOPPED },
89    { "bluetoothDeviceConnecting", IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECTING },
90    { "bluetoothConnectDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT },
91    { "bluetoothDisconnectDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISCONNECT },
92    { "bluetoothForgetDevice", IDS_OPTIONS_SETTINGS_BLUETOOTH_FORGET },
93    { "bluetoothCancel", IDS_OPTIONS_SETTINGS_BLUETOOTH_CANCEL },
94    { "bluetoothEnterKey", IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_KEY },
95    { "bluetoothDismissError", IDS_OPTIONS_SETTINGS_BLUETOOTH_DISMISS_ERROR },
96
97    // Device connecting and pairing.
98    { "bluetoothStartConnecting",
99        IDS_OPTIONS_SETTINGS_BLUETOOTH_START_CONNECTING },
100    { "bluetoothAcceptPasskey",
101        IDS_OPTIONS_SETTINGS_BLUETOOTH_ACCEPT_PASSKEY },
102    { "bluetoothRejectPasskey",
103        IDS_OPTIONS_SETTINGS_BLUETOOTH_REJECT_PASSKEY },
104    { "bluetoothEnterPinCode",
105        IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_PIN_CODE_REQUEST },
106    { "bluetoothEnterPasskey",
107        IDS_OPTIONS_SETTINGS_BLUETOOTH_ENTER_PASSKEY_REQUEST },
108    { "bluetoothRemotePinCode",
109        IDS_OPTIONS_SETTINGS_BLUETOOTH_REMOTE_PIN_CODE_REQUEST },
110    { "bluetoothRemotePasskey",
111        IDS_OPTIONS_SETTINGS_BLUETOOTH_REMOTE_PASSKEY_REQUEST },
112    { "bluetoothConfirmPasskey",
113        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONFIRM_PASSKEY_REQUEST },
114
115    // Error messages.
116    { "bluetoothStartDiscoveryFailed",
117        IDS_OPTIONS_SETTINGS_BLUETOOTH_START_DISCOVERY_FAILED },
118    { "bluetoothStopDiscoveryFailed",
119        IDS_OPTIONS_SETTINGS_BLUETOOTH_STOP_DISCOVERY_FAILED },
120    { "bluetoothChangePowerFailed",
121        IDS_OPTIONS_SETTINGS_BLUETOOTH_CHANGE_POWER_FAILED },
122    { "bluetoothConnectUnknownError",
123        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_UNKNOWN_ERROR },
124    { "bluetoothConnectInProgress",
125        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_IN_PROGRESS },
126    { "bluetoothConnectFailed",
127        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_FAILED },
128    { "bluetoothConnectAuthFailed",
129        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_FAILED },
130    { "bluetoothConnectAuthCanceled",
131        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_CANCELED },
132    { "bluetoothConnectAuthRejected",
133        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_REJECTED },
134    { "bluetoothConnectAuthTimeout",
135        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_AUTH_TIMEOUT },
136    { "bluetoothConnectUnsupportedDevice",
137        IDS_OPTIONS_SETTINGS_BLUETOOTH_CONNECT_UNSUPPORTED_DEVICE },
138    { "bluetoothDisconnectFailed",
139        IDS_OPTIONS_SETTINGS_BLUETOOTH_DISCONNECT_FAILED },
140    { "bluetoothForgetFailed",
141        IDS_OPTIONS_SETTINGS_BLUETOOTH_FORGET_FAILED }};
142
143  RegisterStrings(localized_strings, resources, arraysize(resources));
144}
145
146// TODO(kevers): Reorder methods to match ordering in the header file.
147
148void BluetoothOptionsHandler::AdapterPresentChanged(
149    device::BluetoothAdapter* adapter,
150    bool present) {
151  DCHECK(adapter == adapter_.get());
152  if (present) {
153    web_ui()->CallJavascriptFunction(
154        "options.BrowserOptions.showBluetoothSettings");
155
156    // Update the checkbox and visibility based on the powered state of the
157    // new adapter.
158    AdapterPoweredChanged(adapter_.get(), adapter_->IsPowered());
159  } else {
160    web_ui()->CallJavascriptFunction(
161        "options.BrowserOptions.hideBluetoothSettings");
162  }
163}
164
165void BluetoothOptionsHandler::AdapterPoweredChanged(
166    device::BluetoothAdapter* adapter,
167    bool powered) {
168  DCHECK(adapter == adapter_.get());
169  base::FundamentalValue checked(powered);
170  web_ui()->CallJavascriptFunction(
171      "options.BrowserOptions.setBluetoothState", checked);
172
173  // If the "Add device" overlay is visible, dismiss it.
174  if (!powered) {
175    web_ui()->CallJavascriptFunction(
176        "options.BluetoothOptions.dismissOverlay");
177  }
178}
179
180void BluetoothOptionsHandler::AdapterDiscoveringChanged(
181    device::BluetoothAdapter* adapter,
182    bool discovering) {
183  DCHECK(adapter == adapter_.get());
184  base::FundamentalValue discovering_value(discovering);
185  web_ui()->CallJavascriptFunction(
186      "options.BluetoothOptions.updateDiscoveryState", discovering_value);
187}
188
189void BluetoothOptionsHandler::RegisterMessages() {
190  web_ui()->RegisterMessageCallback("bluetoothEnableChange",
191      base::Bind(&BluetoothOptionsHandler::EnableChangeCallback,
192                 base::Unretained(this)));
193  web_ui()->RegisterMessageCallback("findBluetoothDevices",
194      base::Bind(&BluetoothOptionsHandler::FindDevicesCallback,
195                 base::Unretained(this)));
196  web_ui()->RegisterMessageCallback("updateBluetoothDevice",
197      base::Bind(&BluetoothOptionsHandler::UpdateDeviceCallback,
198                 base::Unretained(this)));
199  web_ui()->RegisterMessageCallback("stopBluetoothDeviceDiscovery",
200      base::Bind(&BluetoothOptionsHandler::StopDiscoveryCallback,
201                 base::Unretained(this)));
202  web_ui()->RegisterMessageCallback("getPairedBluetoothDevices",
203      base::Bind(&BluetoothOptionsHandler::GetPairedDevicesCallback,
204                 base::Unretained(this)));
205}
206
207void BluetoothOptionsHandler::InitializeHandler() {
208  device::BluetoothAdapterFactory::GetAdapter(
209      base::Bind(&BluetoothOptionsHandler::InitializeAdapter,
210                 weak_ptr_factory_.GetWeakPtr()));
211}
212
213void BluetoothOptionsHandler::InitializePage() {
214  // Show or hide the bluetooth settings and update the checkbox based
215  // on the current present/powered state.
216  AdapterPresentChanged(adapter_.get(), adapter_->IsPresent());
217
218  // Automatically start device discovery if the "Add Bluetooth Device"
219  // overlay is visible.
220  web_ui()->CallJavascriptFunction(
221      "options.BluetoothOptions.startDeviceDiscovery");
222}
223
224void BluetoothOptionsHandler::InitializeAdapter(
225    scoped_refptr<device::BluetoothAdapter> adapter) {
226  adapter_ = adapter;
227  CHECK(adapter_.get());
228  adapter_->AddObserver(this);
229}
230
231void BluetoothOptionsHandler::EnableChangeCallback(
232    const base::ListValue* args) {
233  bool bluetooth_enabled;
234  args->GetBoolean(0, &bluetooth_enabled);
235
236  adapter_->SetPowered(bluetooth_enabled,
237                       base::Bind(&base::DoNothing),
238                       base::Bind(&BluetoothOptionsHandler::EnableChangeError,
239                                  weak_ptr_factory_.GetWeakPtr()));
240}
241
242void BluetoothOptionsHandler::EnableChangeError() {
243  VLOG(1) << "Failed to change power state.";
244  ReportError("bluetoothChangePowerFailed", std::string());
245}
246
247void BluetoothOptionsHandler::FindDevicesCallback(
248    const base::ListValue* args) {
249  if (discovery_session_.get() && discovery_session_->IsActive()) {
250    VLOG(1) << "Already have an active discovery session.";
251    return;
252  }
253  should_run_device_discovery_ = true;
254  adapter_->StartDiscoverySession(
255      base::Bind(&BluetoothOptionsHandler::OnStartDiscoverySession,
256                 weak_ptr_factory_.GetWeakPtr()),
257      base::Bind(&BluetoothOptionsHandler::FindDevicesError,
258                 weak_ptr_factory_.GetWeakPtr()));
259}
260
261void BluetoothOptionsHandler::OnStartDiscoverySession(
262    scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
263  // If the discovery session was returned after a request to stop discovery
264  // (e.g. the "Add Device" dialog was dismissed), don't claim the discovery
265  // session and let it clean up.
266  if (!should_run_device_discovery_)
267    return;
268  discovery_session_ = discovery_session.Pass();
269}
270
271void BluetoothOptionsHandler::FindDevicesError() {
272  VLOG(1) << "Failed to start discovery.";
273  ReportError("bluetoothStartDiscoveryFailed", std::string());
274  if (!adapter_.get())
275    return;
276  base::FundamentalValue discovering(adapter_->IsDiscovering());
277  web_ui()->CallJavascriptFunction(
278      "options.BluetoothOptions.updateDiscoveryState", discovering);
279}
280
281void BluetoothOptionsHandler::UpdateDeviceCallback(
282    const base::ListValue* args) {
283  std::string address;
284  args->GetString(kUpdateDeviceAddressIndex, &address);
285
286  device::BluetoothDevice* device = adapter_->GetDevice(address);
287  if (!device)
288    return;
289
290  std::string command;
291  args->GetString(kUpdateDeviceCommandIndex, &command);
292
293  if (command == kConnectCommand) {
294    int size = args->GetSize();
295    if (size > kUpdateDeviceAuthTokenIndex) {
296      // PIN code or Passkey entry during the pairing process.
297      std::string auth_token;
298      args->GetString(kUpdateDeviceAuthTokenIndex, &auth_token);
299
300      if (device->ExpectingPinCode()) {
301        DeviceConnecting(device);
302        // PIN Code is an array of 1 to 16 8-bit bytes, the usual
303        // interpretation, and the one shared by BlueZ, is a UTF-8 string
304        // of as many characters that will fit in that space, thus we
305        // can use the auth token from JavaScript unmodified.
306        VLOG(1) << "PIN Code supplied: " << address << ": " << auth_token;
307        device->SetPinCode(auth_token);
308      } else if (device->ExpectingPasskey()) {
309        DeviceConnecting(device);
310        // Passkey is a numeric in the range 0-999999, in this case the
311        // JavaScript code should have ensured the auth token string only
312        // contains digits so a simple conversion is sufficient. In the
313        // failure case, just use 0 since that's the most likely Passkey
314        // anyway, and if it's refused the device will request a new one.
315        unsigned passkey = 0;
316        base::StringToUint(auth_token, &passkey);
317
318        VLOG(1) << "Passkey supplied: " << address << ": " << passkey;
319        device->SetPasskey(passkey);
320      } else {
321        LOG(WARNING) << "Auth token supplied after pairing ended: " << address
322                     << ": " << auth_token;
323      }
324    } else {
325      // Determine if the device supports pairing:
326      PairingDelegate* delegate = NULL;
327      if (device->IsPairable())
328        delegate = this;
329
330      // Connection request.
331      VLOG(1) << "Connect: " << address;
332      device->Connect(
333          delegate,
334          base::Bind(&BluetoothOptionsHandler::Connected,
335                     weak_ptr_factory_.GetWeakPtr()),
336          base::Bind(&BluetoothOptionsHandler::ConnectError,
337                     weak_ptr_factory_.GetWeakPtr(),
338                     device->GetAddress()));
339    }
340  } else if (command == kCancelCommand) {
341    // Cancel pairing.
342    VLOG(1) << "Cancel pairing: " << address;
343    device->CancelPairing();
344  } else if (command == kAcceptCommand) {
345    DeviceConnecting(device);
346    // Confirm displayed Passkey.
347    VLOG(1) << "Confirm pairing: " << address;
348    device->ConfirmPairing();
349  } else if (command == kRejectCommand) {
350    // Reject displayed Passkey.
351    VLOG(1) << "Reject pairing: " << address;
352    device->RejectPairing();
353  } else if (command == kDisconnectCommand) {
354    // Disconnect from device.
355    VLOG(1) << "Disconnect device: " << address;
356    device->Disconnect(
357        base::Bind(&base::DoNothing),
358        base::Bind(&BluetoothOptionsHandler::DisconnectError,
359                   weak_ptr_factory_.GetWeakPtr(),
360                   device->GetAddress()));
361  } else if (command == kForgetCommand) {
362    // Disconnect from device and delete pairing information.
363    VLOG(1) << "Forget device: " << address;
364    device->Forget(base::Bind(&BluetoothOptionsHandler::ForgetError,
365                              weak_ptr_factory_.GetWeakPtr(),
366                              device->GetAddress()));
367  } else {
368    LOG(WARNING) << "Unknown updateBluetoothDevice command: " << command;
369  }
370}
371
372void BluetoothOptionsHandler::Connected() {
373  // Invalidate the local cache.
374  pairing_device_address_.clear();
375  pairing_device_entered_ = kInvalidEntered;
376
377  web_ui()->CallJavascriptFunction(
378      "options.BluetoothPairing.dismissDialog");
379}
380
381void BluetoothOptionsHandler::ConnectError(
382    const std::string& address,
383    device::BluetoothDevice::ConnectErrorCode error_code) {
384  const char* error_name = NULL;
385
386  // Invalidate the local cache.
387  pairing_device_address_.clear();
388  pairing_device_entered_ = kInvalidEntered;
389
390  VLOG(1) << "Failed to connect to device: " << address;
391  switch (error_code) {
392    case device::BluetoothDevice::ERROR_UNKNOWN:
393      error_name = "bluetoothConnectUnknownError";
394      break;
395    case device::BluetoothDevice::ERROR_INPROGRESS:
396      error_name = "bluetoothConnectInProgress";
397      break;
398    case device::BluetoothDevice::ERROR_FAILED:
399      error_name = "bluetoothConnectFailed";
400      break;
401    case device::BluetoothDevice::ERROR_AUTH_FAILED:
402      error_name = "bluetoothConnectAuthFailed";
403      break;
404    case device::BluetoothDevice::ERROR_AUTH_CANCELED:
405      error_name = "bluetoothConnectAuthCanceled";
406      break;
407    case device::BluetoothDevice::ERROR_AUTH_REJECTED:
408      error_name = "bluetoothConnectAuthRejected";
409      break;
410    case device::BluetoothDevice::ERROR_AUTH_TIMEOUT:
411      error_name = "bluetoothConnectAuthTimeout";
412      break;
413    case device::BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
414      error_name = "bluetoothConnectUnsupportedDevice";
415      break;
416  }
417  // Report an error only if there's an error to report.
418  if (error_name)
419    ReportError(error_name, address);
420}
421
422void BluetoothOptionsHandler::DisconnectError(const std::string& address) {
423  VLOG(1) << "Failed to disconnect from device: " << address;
424  ReportError("bluetoothDisconnectFailed", address);
425}
426
427void BluetoothOptionsHandler::ForgetError(const std::string& address) {
428  VLOG(1) << "Failed to disconnect and unpair device: " << address;
429  ReportError("bluetoothForgetFailed", address);
430}
431
432void BluetoothOptionsHandler::StopDiscoveryCallback(
433    const base::ListValue* args) {
434  should_run_device_discovery_ = false;
435  if (!discovery_session_.get() || !discovery_session_->IsActive()) {
436    VLOG(1) << "No active discovery session.";
437    return;
438  }
439  discovery_session_->Stop(
440      base::Bind(&base::DoNothing),
441      base::Bind(&BluetoothOptionsHandler::StopDiscoveryError,
442                 weak_ptr_factory_.GetWeakPtr()));
443}
444
445void BluetoothOptionsHandler::StopDiscoveryError() {
446  VLOG(1) << "Failed to stop discovery.";
447  ReportError("bluetoothStopDiscoveryFailed", std::string());
448}
449
450void BluetoothOptionsHandler::GetPairedDevicesCallback(
451    const base::ListValue* args) {
452  device::BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
453
454  for (device::BluetoothAdapter::DeviceList::iterator iter = devices.begin();
455       iter != devices.end(); ++iter)
456    SendDeviceNotification(*iter, NULL);
457}
458
459void BluetoothOptionsHandler::SendDeviceNotification(
460    const device::BluetoothDevice* device,
461    base::DictionaryValue* params) {
462  base::DictionaryValue js_properties;
463  js_properties.SetString("name", device->GetName());
464  js_properties.SetString("address", device->GetAddress());
465  js_properties.SetBoolean("paired", device->IsPaired());
466  js_properties.SetBoolean("connected", device->IsConnected());
467  js_properties.SetBoolean("connecting", device->IsConnecting());
468  js_properties.SetBoolean("connectable", device->IsConnectable());
469  if (params)
470    js_properties.MergeDictionary(params);
471
472  // Use the cached values to update js_property.
473  if (device->GetAddress() == pairing_device_address_) {
474    std::string pairing;
475    if (!js_properties.GetString("pairing", &pairing)) {
476      pairing = pairing_device_pairing_;
477      js_properties.SetString("pairing", pairing);
478    }
479    if (pairing == kRemotePinCode && !js_properties.HasKey("pincode"))
480      js_properties.SetString("pincode", pairing_device_pincode_);
481    if (pairing == kRemotePasskey && !js_properties.HasKey("passkey"))
482      js_properties.SetInteger("passkey", pairing_device_passkey_);
483    if ((pairing == kRemotePinCode || pairing == kRemotePasskey) &&
484        !js_properties.HasKey("entered") &&
485        pairing_device_entered_ != kInvalidEntered) {
486      js_properties.SetInteger("entered", pairing_device_entered_);
487    }
488  }
489
490  // Update the cache with the new information.
491  if (js_properties.HasKey("pairing")) {
492    pairing_device_address_ = device->GetAddress();
493    js_properties.GetString("pairing", &pairing_device_pairing_);
494    js_properties.GetString("pincode", &pairing_device_pincode_);
495    js_properties.GetInteger("passkey", &pairing_device_passkey_);
496    if (!js_properties.GetInteger("entered", &pairing_device_entered_))
497      pairing_device_entered_ = kInvalidEntered;
498  }
499
500  web_ui()->CallJavascriptFunction(
501      "options.BrowserOptions.addBluetoothDevice",
502      js_properties);
503}
504
505void BluetoothOptionsHandler::RequestPinCode(device::BluetoothDevice* device) {
506  base::DictionaryValue params;
507  params.SetString("pairing", kEnterPinCode);
508  SendDeviceNotification(device, &params);
509}
510
511void BluetoothOptionsHandler::RequestPasskey(device::BluetoothDevice* device) {
512  base::DictionaryValue params;
513  params.SetString("pairing", kEnterPasskey);
514  SendDeviceNotification(device, &params);
515}
516
517void BluetoothOptionsHandler::DisplayPinCode(device::BluetoothDevice* device,
518                                             const std::string& pincode) {
519  base::DictionaryValue params;
520  params.SetString("pairing", kRemotePinCode);
521  params.SetString("pincode", pincode);
522  SendDeviceNotification(device, &params);
523}
524
525void BluetoothOptionsHandler::DisplayPasskey(device::BluetoothDevice* device,
526                                             uint32 passkey) {
527  base::DictionaryValue params;
528  params.SetString("pairing", kRemotePasskey);
529  params.SetInteger("passkey", passkey);
530  SendDeviceNotification(device, &params);
531}
532
533void BluetoothOptionsHandler::KeysEntered(device::BluetoothDevice* device,
534                                          uint32 entered) {
535  base::DictionaryValue params;
536  params.SetInteger("entered", entered);
537  SendDeviceNotification(device, &params);
538}
539
540void BluetoothOptionsHandler::ConfirmPasskey(device::BluetoothDevice* device,
541                                             uint32 passkey) {
542  base::DictionaryValue params;
543  params.SetString("pairing", kConfirmPasskey);
544  params.SetInteger("passkey", passkey);
545  SendDeviceNotification(device, &params);
546}
547
548void BluetoothOptionsHandler::AuthorizePairing(
549    device::BluetoothDevice* device) {
550  // There is never any circumstance where this will be called, since the
551  // options handler will only be used for outgoing pairing requests, but
552  // play it safe.
553  device->ConfirmPairing();
554}
555
556void BluetoothOptionsHandler::ReportError(
557    const std::string& error,
558    const std::string& address) {
559  base::DictionaryValue properties;
560  properties.SetString("label", error);
561  properties.SetString("address", address);
562  web_ui()->CallJavascriptFunction(
563      "options.BluetoothPairing.showMessage",
564      properties);
565}
566
567void BluetoothOptionsHandler::DeviceAdded(device::BluetoothAdapter* adapter,
568                                          device::BluetoothDevice* device) {
569  DCHECK(adapter == adapter_.get());
570  DCHECK(device);
571  SendDeviceNotification(device, NULL);
572}
573
574void BluetoothOptionsHandler::DeviceChanged(device::BluetoothAdapter* adapter,
575                                            device::BluetoothDevice* device) {
576  DCHECK(adapter == adapter_.get());
577  DCHECK(device);
578  SendDeviceNotification(device, NULL);
579}
580
581void BluetoothOptionsHandler::DeviceRemoved(device::BluetoothAdapter* adapter,
582                                            device::BluetoothDevice* device) {
583  DCHECK(adapter == adapter_.get());
584  DCHECK(device);
585
586  // Invalidate the local cache if the pairing device is removed.
587  if (pairing_device_address_ == device->GetAddress()) {
588    pairing_device_address_.clear();
589    pairing_device_entered_ = kInvalidEntered;
590  }
591
592  base::StringValue address(device->GetAddress());
593  web_ui()->CallJavascriptFunction(
594      "options.BrowserOptions.removeBluetoothDevice",
595      address);
596}
597
598void BluetoothOptionsHandler::DeviceConnecting(
599    device::BluetoothDevice* device) {
600  DCHECK(device);
601  base::DictionaryValue params;
602  params.SetString("pairing", kStartConnecting);
603  SendDeviceNotification(device, &params);
604}
605
606}  // namespace options
607}  // namespace chromeos
608