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