Home | History | Annotate | Download | only in chromeos
      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     : should_run_device_discovery_(false),
     60       pairing_device_passkey_(1000000),
     61       pairing_device_entered_(kInvalidEntered),
     62       weak_ptr_factory_(this) {
     63 }
     64 
     65 BluetoothOptionsHandler::~BluetoothOptionsHandler() {
     66   if (adapter_.get())
     67     adapter_->RemoveObserver(this);
     68 }
     69 
     70 void 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 
    148 void 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 
    165 void 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 
    180 void 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 
    189 void 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 
    207 void BluetoothOptionsHandler::InitializeHandler() {
    208   device::BluetoothAdapterFactory::GetAdapter(
    209       base::Bind(&BluetoothOptionsHandler::InitializeAdapter,
    210                  weak_ptr_factory_.GetWeakPtr()));
    211 }
    212 
    213 void 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 
    224 void BluetoothOptionsHandler::InitializeAdapter(
    225     scoped_refptr<device::BluetoothAdapter> adapter) {
    226   adapter_ = adapter;
    227   CHECK(adapter_.get());
    228   adapter_->AddObserver(this);
    229 }
    230 
    231 void 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 
    242 void BluetoothOptionsHandler::EnableChangeError() {
    243   VLOG(1) << "Failed to change power state.";
    244   ReportError("bluetoothChangePowerFailed", std::string());
    245 }
    246 
    247 void 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 
    261 void 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 
    271 void 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 
    281 void 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 
    372 void 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 
    381 void 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 
    422 void BluetoothOptionsHandler::DisconnectError(const std::string& address) {
    423   VLOG(1) << "Failed to disconnect from device: " << address;
    424   ReportError("bluetoothDisconnectFailed", address);
    425 }
    426 
    427 void BluetoothOptionsHandler::ForgetError(const std::string& address) {
    428   VLOG(1) << "Failed to disconnect and unpair device: " << address;
    429   ReportError("bluetoothForgetFailed", address);
    430 }
    431 
    432 void 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 
    445 void BluetoothOptionsHandler::StopDiscoveryError() {
    446   VLOG(1) << "Failed to stop discovery.";
    447   ReportError("bluetoothStopDiscoveryFailed", std::string());
    448 }
    449 
    450 void 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 
    459 void 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 
    505 void BluetoothOptionsHandler::RequestPinCode(device::BluetoothDevice* device) {
    506   base::DictionaryValue params;
    507   params.SetString("pairing", kEnterPinCode);
    508   SendDeviceNotification(device, &params);
    509 }
    510 
    511 void BluetoothOptionsHandler::RequestPasskey(device::BluetoothDevice* device) {
    512   base::DictionaryValue params;
    513   params.SetString("pairing", kEnterPasskey);
    514   SendDeviceNotification(device, &params);
    515 }
    516 
    517 void 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 
    525 void 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 
    533 void BluetoothOptionsHandler::KeysEntered(device::BluetoothDevice* device,
    534                                           uint32 entered) {
    535   base::DictionaryValue params;
    536   params.SetInteger("entered", entered);
    537   SendDeviceNotification(device, &params);
    538 }
    539 
    540 void 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 
    548 void 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 
    556 void 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 
    567 void BluetoothOptionsHandler::DeviceAdded(device::BluetoothAdapter* adapter,
    568                                           device::BluetoothDevice* device) {
    569   DCHECK(adapter == adapter_.get());
    570   DCHECK(device);
    571   SendDeviceNotification(device, NULL);
    572 }
    573 
    574 void BluetoothOptionsHandler::DeviceChanged(device::BluetoothAdapter* adapter,
    575                                             device::BluetoothDevice* device) {
    576   DCHECK(adapter == adapter_.get());
    577   DCHECK(device);
    578   SendDeviceNotification(device, NULL);
    579 }
    580 
    581 void 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 
    598 void 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