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