bluetooth_options_handler.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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    discovering_(false),
60    pairing_device_passkey_(1000000),
61    pairing_device_entered_(kInvalidEntered),
62    weak_ptr_factory_(this) {
63}
64
65BluetoothOptionsHandler::~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
76void 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
153void 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
170void 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
179void 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
197void BluetoothOptionsHandler::InitializeHandler() {
198  device::BluetoothAdapterFactory::GetAdapter(
199      base::Bind(&BluetoothOptionsHandler::InitializeAdapter,
200                 weak_ptr_factory_.GetWeakPtr()));
201}
202
203void 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
213void BluetoothOptionsHandler::InitializeAdapter(
214    scoped_refptr<device::BluetoothAdapter> adapter) {
215  adapter_ = adapter;
216  CHECK(adapter_);
217  adapter_->AddObserver(this);
218}
219
220void 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
231void BluetoothOptionsHandler::EnableChangeError() {
232  VLOG(1) << "Failed to change power state.";
233  ReportError("bluetoothChangePowerFailed", std::string());
234}
235
236void 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
247void BluetoothOptionsHandler::FindDevicesError() {
248  VLOG(1) << "Failed to start discovery.";
249  ReportError("bluetoothStartDiscoveryFailed", std::string());
250}
251
252void 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
342void 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
351void 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
392void BluetoothOptionsHandler::DisconnectError(const std::string& address) {
393  VLOG(1) << "Failed to disconnect from device: " << address;
394  ReportError("bluetoothDisconnectFailed", address);
395}
396
397void BluetoothOptionsHandler::ForgetError(const std::string& address) {
398  VLOG(1) << "Failed to disconnect and unpair device: " << address;
399  ReportError("bluetoothForgetFailed", address);
400}
401
402void 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
413void BluetoothOptionsHandler::StopDiscoveryError() {
414  VLOG(1) << "Failed to stop discovery.";
415  ReportError("bluetoothStopDiscoveryFailed", std::string());
416}
417
418void 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
427void 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
473void BluetoothOptionsHandler::RequestPinCode(device::BluetoothDevice* device) {
474  DictionaryValue params;
475  params.SetString("pairing", kEnterPinCode);
476  SendDeviceNotification(device, &params);
477}
478
479void BluetoothOptionsHandler::RequestPasskey(device::BluetoothDevice* device) {
480  DictionaryValue params;
481  params.SetString("pairing", kEnterPasskey);
482  SendDeviceNotification(device, &params);
483}
484
485void 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
493void 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
501void BluetoothOptionsHandler::KeysEntered(device::BluetoothDevice* device,
502                                          uint32 entered) {
503  DictionaryValue params;
504  params.SetInteger("entered", entered);
505  SendDeviceNotification(device, &params);
506}
507
508void 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
516void BluetoothOptionsHandler::DismissDisplayOrConfirm() {
517  DCHECK(adapter_);
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
531void 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
542void BluetoothOptionsHandler::DeviceAdded(device::BluetoothAdapter* adapter,
543                                          device::BluetoothDevice* device) {
544  DCHECK(adapter == adapter_.get());
545  DCHECK(device);
546  SendDeviceNotification(device, NULL);
547}
548
549void BluetoothOptionsHandler::DeviceChanged(device::BluetoothAdapter* adapter,
550                                            device::BluetoothDevice* device) {
551  DCHECK(adapter == adapter_.get());
552  DCHECK(device);
553  SendDeviceNotification(device, NULL);
554}
555
556void 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
573void 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
583