1// Copyright 2014 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/chromeos/login/hid_detection_screen_handler.h"
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/compiler_specific.h"
10#include "base/macros.h"
11#include "base/metrics/histogram.h"
12#include "base/prefs/pref_service.h"
13#include "base/strings/string16.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/utf_string_conversions.h"
16#include "chrome/browser/browser_process.h"
17#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/grit/generated_resources.h"
20#include "chromeos/chromeos_switches.h"
21#include "device/bluetooth/bluetooth_adapter_factory.h"
22#include "ui/base/l10n/l10n_util.h"
23
24namespace {
25
26const char kJsScreenPath[] = "login.HIDDetectionScreen";
27
28// Variants of pairing state.
29const char kRemotePinCode[] = "bluetoothRemotePinCode";
30const char kRemotePasskey[] = "bluetoothRemotePasskey";
31
32// Possible ui-states for device-blocks.
33const char kSearchingState[] = "searching";
34const char kUSBConnectedState[] = "connected";
35const char kBTPairedState[] = "paired";
36const char kBTPairingState[] = "pairing";
37// Special state for notifications that don't switch ui-state, but add info.
38const char kBTUpdateState[] = "update";
39
40// Names of possible arguments used for ui update.
41const char kPincodeArgName[] = "pincode";
42const char kDeviceNameArgName[] = "name";
43const char kLabelArgName[] = "keyboard-label";
44
45// Standard length of pincode for pairing BT keyboards.
46const int kPincodeLength = 6;
47
48bool DeviceIsPointing(device::BluetoothDevice::DeviceType device_type) {
49  return device_type == device::BluetoothDevice::DEVICE_MOUSE ||
50         device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO ||
51         device_type == device::BluetoothDevice::DEVICE_TABLET;
52}
53
54bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo& info) {
55  return info.is_mouse || info.is_touchpad || info.is_touchscreen ||
56         info.is_tablet;
57}
58
59bool DeviceIsKeyboard(device::BluetoothDevice::DeviceType device_type) {
60  return device_type == device::BluetoothDevice::DEVICE_KEYBOARD ||
61         device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO;
62}
63
64}  // namespace
65
66namespace chromeos {
67
68HIDDetectionScreenHandler::HIDDetectionScreenHandler(
69    CoreOobeActor* core_oobe_actor)
70    : BaseScreenHandler(kJsScreenPath),
71      delegate_(NULL),
72      core_oobe_actor_(core_oobe_actor),
73      show_on_init_(false),
74      mouse_is_pairing_(false),
75      pointing_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN),
76      keyboard_is_pairing_(false),
77      keyboard_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN),
78      switch_on_adapter_when_ready_(false),
79      weak_ptr_factory_(this) {
80}
81
82HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
83  adapter_initially_powered_.reset();
84  if (adapter_.get())
85    adapter_->RemoveObserver(this);
86  input_service_proxy_.RemoveObserver(this);
87  if (delegate_)
88    delegate_->OnActorDestroyed(this);
89}
90
91void HIDDetectionScreenHandler::OnStartDiscoverySession(
92    scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
93  VLOG(1) << "BT Discovery session started";
94  discovery_session_ = discovery_session.Pass();
95  UpdateDevices();
96}
97
98void HIDDetectionScreenHandler::SetPoweredError() {
99  LOG(ERROR) << "Failed to power BT adapter";
100}
101
102void HIDDetectionScreenHandler::SetPoweredOffError() {
103  LOG(ERROR) << "Failed to power off BT adapter";
104}
105
106void HIDDetectionScreenHandler::FindDevicesError() {
107  VLOG(1) << "Failed to start Bluetooth discovery.";
108}
109
110void HIDDetectionScreenHandler::Show() {
111  if (!page_is_ready()) {
112    show_on_init_ = true;
113    return;
114  }
115  core_oobe_actor_->InitDemoModeDetection();
116  input_service_proxy_.AddObserver(this);
117  UpdateDevices();
118
119  PrefService* local_state = g_browser_process->local_state();
120  int num_of_times_dialog_was_shown = local_state->GetInteger(
121      prefs::kTimesHIDDialogShown);
122  local_state->SetInteger(prefs::kTimesHIDDialogShown,
123                          num_of_times_dialog_was_shown + 1);
124
125  ShowScreen(OobeUI::kScreenHIDDetection, NULL);
126  if (!pointing_device_id_.empty())
127    SendPointingDeviceNotification();
128  if (!keyboard_device_id_.empty())
129    SendKeyboardDeviceNotification(NULL);
130}
131
132void HIDDetectionScreenHandler::Hide() {
133  if (adapter_.get())
134    adapter_->RemoveObserver(this);
135  input_service_proxy_.RemoveObserver(this);
136}
137
138void HIDDetectionScreenHandler::SetDelegate(Delegate* delegate) {
139  delegate_ = delegate;
140  if (page_is_ready())
141    Initialize();
142}
143
144void HIDDetectionScreenHandler::CheckIsScreenRequired(
145    const base::Callback<void(bool)>& on_check_done) {
146  input_service_proxy_.GetDevices(
147      base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesListForCheck,
148                 weak_ptr_factory_.GetWeakPtr(),
149                 on_check_done));
150}
151
152void HIDDetectionScreenHandler::DeclareLocalizedValues(
153    LocalizedValuesBuilder* builder) {
154  builder->Add("hidDetectionContinue", IDS_HID_DETECTION_CONTINUE_BUTTON);
155  builder->Add("hidDetectionInvitation", IDS_HID_DETECTION_INVITATION_TEXT);
156  builder->Add("hidDetectionPrerequisites",
157      IDS_HID_DETECTION_PRECONDITION_TEXT);
158  builder->Add("hidDetectionMouseSearching", IDS_HID_DETECTION_SEARCHING_MOUSE);
159  builder->Add("hidDetectionKeyboardSearching",
160      IDS_HID_DETECTION_SEARCHING_KEYBOARD);
161  builder->Add("hidDetectionUSBMouseConnected",
162      IDS_HID_DETECTION_CONNECTED_USB_MOUSE);
163  builder->Add("hidDetectionUSBKeyboardConnected",
164      IDS_HID_DETECTION_CONNECTED_USB_KEYBOARD);
165  builder->Add("hidDetectionBTMousePaired",
166      IDS_HID_DETECTION_PAIRED_BLUETOOTH_MOUSE);
167  builder->Add("hidDetectionBTEnterKey", IDS_HID_DETECTION_BLUETOOTH_ENTER_KEY);
168}
169
170void HIDDetectionScreenHandler::Initialize() {
171  if (!page_is_ready() || !delegate_)
172    return;
173
174  device::BluetoothAdapterFactory::GetAdapter(
175      base::Bind(&HIDDetectionScreenHandler::InitializeAdapter,
176                 weak_ptr_factory_.GetWeakPtr()));
177
178  if (show_on_init_) {
179    Show();
180    show_on_init_ = false;
181  }
182}
183
184void HIDDetectionScreenHandler::RegisterMessages() {
185  AddCallback(
186      "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
187}
188
189void HIDDetectionScreenHandler::HandleOnContinue() {
190  // Continue button pressed.
191  ContinueScenarioType scenario_type;
192  if (!pointing_device_id_.empty() && !keyboard_device_id_.empty())
193    scenario_type = All_DEVICES_DETECTED;
194  else if (pointing_device_id_.empty())
195    scenario_type = KEYBOARD_DEVICE_ONLY_DETECTED;
196  else
197    scenario_type = POINTING_DEVICE_ONLY_DETECTED;
198
199  UMA_HISTOGRAM_ENUMERATION(
200      "HIDDetection.OOBEDevicesDetectedOnContinuePressed",
201      scenario_type,
202      CONTINUE_SCENARIO_TYPE_SIZE);
203
204  // Switch off BT adapter if it was off before the screen and no BT device
205  // connected.
206  if (adapter_.get() && adapter_->IsPresent() && adapter_->IsPowered() &&
207      !(pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH ||
208        keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) &&
209      adapter_initially_powered_ && !(*adapter_initially_powered_)) {
210    VLOG(1) << "Switching off BT adapter after HID OOBE screen as unused.";
211    adapter_->SetPowered(
212        false,
213        base::Bind(&base::DoNothing),
214        base::Bind(&HIDDetectionScreenHandler::SetPoweredOffError,
215                   weak_ptr_factory_.GetWeakPtr()));
216  }
217
218  core_oobe_actor_->StopDemoModeDetection();
219  if (delegate_)
220    delegate_->OnExit();
221}
222
223void HIDDetectionScreenHandler::InitializeAdapter(
224    scoped_refptr<device::BluetoothAdapter> adapter) {
225  adapter_ = adapter;
226  CHECK(adapter_.get());
227
228  adapter_->AddObserver(this);
229  UpdateDevices();
230}
231
232void HIDDetectionScreenHandler::StartBTDiscoverySession() {
233  adapter_->StartDiscoverySession(
234      base::Bind(&HIDDetectionScreenHandler::OnStartDiscoverySession,
235                 weak_ptr_factory_.GetWeakPtr()),
236      base::Bind(&HIDDetectionScreenHandler::FindDevicesError,
237                 weak_ptr_factory_.GetWeakPtr()));
238}
239
240void HIDDetectionScreenHandler::RequestPinCode(
241    device::BluetoothDevice* device) {
242  VLOG(1) << "RequestPinCode id = " << device->GetDeviceID()
243          << " name = " << device->GetName();
244  device->CancelPairing();
245}
246
247void HIDDetectionScreenHandler::RequestPasskey(
248    device::BluetoothDevice* device) {
249  VLOG(1) << "RequestPassKey id = " << device->GetDeviceID()
250          << " name = " << device->GetName();
251  device->CancelPairing();
252}
253
254void HIDDetectionScreenHandler::DisplayPinCode(device::BluetoothDevice* device,
255                                               const std::string& pincode) {
256  VLOG(1) << "DisplayPinCode id = " << device->GetDeviceID()
257          << " name = " << device->GetName();
258  base::DictionaryValue params;
259  params.SetString("state", kBTPairingState);
260  params.SetString("pairing-state", kRemotePinCode);
261  params.SetString("pincode", pincode);
262  params.SetString(kDeviceNameArgName, device->GetName());
263  SendKeyboardDeviceNotification(&params);
264}
265
266void HIDDetectionScreenHandler::DisplayPasskey(
267    device::BluetoothDevice* device, uint32 passkey) {
268  VLOG(1) << "DisplayPassKey id = " << device->GetDeviceID()
269          << " name = " << device->GetName();
270  base::DictionaryValue params;
271  params.SetString("state", kBTPairingState);
272  params.SetString("pairing-state", kRemotePasskey);
273  params.SetInteger("passkey", passkey);
274  std::string pincode = base::UintToString(passkey);
275  pincode = std::string(kPincodeLength - pincode.length(), '0').append(pincode);
276  params.SetString("pincode", pincode);
277  params.SetString(kDeviceNameArgName, device->GetName());
278  SendKeyboardDeviceNotification(&params);
279}
280
281void HIDDetectionScreenHandler::KeysEntered(
282    device::BluetoothDevice* device, uint32 entered) {
283  VLOG(1) << "Keys entered";
284  base::DictionaryValue params;
285  params.SetString("state", kBTUpdateState);
286  params.SetInteger("keysEntered", entered);
287  SendKeyboardDeviceNotification(&params);
288}
289
290void HIDDetectionScreenHandler::ConfirmPasskey(
291    device::BluetoothDevice* device, uint32 passkey) {
292  VLOG(1) << "Confirm Passkey";
293  device->CancelPairing();
294}
295
296void HIDDetectionScreenHandler::AuthorizePairing(
297    device::BluetoothDevice* device) {
298  // There is never any circumstance where this will be called, since the
299  // HID detection screen  handler will only be used for outgoing pairing
300  // requests, but play it safe.
301  VLOG(1) << "Authorize pairing";
302  device->ConfirmPairing();
303}
304
305void HIDDetectionScreenHandler::AdapterPresentChanged(
306    device::BluetoothAdapter* adapter, bool present) {
307  if (present && switch_on_adapter_when_ready_) {
308    VLOG(1) << "Switching on BT adapter on HID OOBE screen.";
309    adapter_initially_powered_.reset(new bool(adapter_->IsPowered()));
310    adapter_->SetPowered(
311        true,
312        base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
313                   weak_ptr_factory_.GetWeakPtr()),
314        base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
315                   weak_ptr_factory_.GetWeakPtr()));
316  }
317}
318
319void HIDDetectionScreenHandler::TryPairingAsPointingDevice(
320    device::BluetoothDevice* device) {
321  if (pointing_device_id_.empty() &&
322      DeviceIsPointing(device->GetDeviceType()) &&
323      device->IsPairable() && !device->IsPaired() && !mouse_is_pairing_) {
324    ConnectBTDevice(device);
325  }
326}
327
328void HIDDetectionScreenHandler::TryPairingAsKeyboardDevice(
329    device::BluetoothDevice* device) {
330  if (keyboard_device_id_.empty() &&
331      DeviceIsKeyboard(device->GetDeviceType()) &&
332      device->IsPairable() && !device->IsPaired() && !keyboard_is_pairing_) {
333    ConnectBTDevice(device);
334  }
335}
336
337void HIDDetectionScreenHandler::DeviceAdded(
338    device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
339  VLOG(1) << "BT input device added id = " << device->GetDeviceID() <<
340      " name = " << device->GetName();
341  TryPairingAsPointingDevice(device);
342  TryPairingAsKeyboardDevice(device);
343}
344
345void HIDDetectionScreenHandler::DeviceChanged(
346    device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
347  VLOG(1) << "BT device changed id = " << device->GetDeviceID() << " name = " <<
348      device->GetName();
349  TryPairingAsPointingDevice(device);
350  TryPairingAsKeyboardDevice(device);
351}
352
353void HIDDetectionScreenHandler::DeviceRemoved(
354    device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
355  VLOG(1) << "BT device removed id = " << device->GetDeviceID() << " name = " <<
356      device->GetName();
357}
358
359void HIDDetectionScreenHandler::OnInputDeviceAdded(
360    const InputDeviceInfo& info) {
361  VLOG(1) << "Input device added id = " << info.id << " name = " << info.name;
362  // TODO(merkulova): deal with all available device types, e.g. joystick.
363  if (!keyboard_device_id_.empty() && !pointing_device_id_.empty())
364    return;
365
366  if (pointing_device_id_.empty() && DeviceIsPointing(info)) {
367    pointing_device_id_ = info.id;
368    pointing_device_name_ = info.name;
369    pointing_device_connect_type_ = info.type;
370    SendPointingDeviceNotification();
371  }
372  if (keyboard_device_id_.empty() && info.is_keyboard) {
373    keyboard_device_id_ = info.id;
374    keyboard_device_name_ = info.name;
375    keyboard_device_connect_type_ = info.type;
376    SendKeyboardDeviceNotification(NULL);
377  }
378}
379
380void HIDDetectionScreenHandler::OnInputDeviceRemoved(const std::string& id) {
381  if (id == keyboard_device_id_) {
382    keyboard_device_id_.clear();
383    keyboard_device_name_.clear();
384    keyboard_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
385    SendKeyboardDeviceNotification(NULL);
386    UpdateDevices();
387  } else if (id == pointing_device_id_) {
388    pointing_device_id_.clear();
389    pointing_device_name_.clear();
390    pointing_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
391    SendPointingDeviceNotification();
392    UpdateDevices();
393  }
394}
395
396// static
397void HIDDetectionScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
398  registry->RegisterIntegerPref(prefs::kTimesHIDDialogShown, 0);
399}
400
401void HIDDetectionScreenHandler::UpdateDevices() {
402  input_service_proxy_.GetDevices(
403      base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesList,
404                 weak_ptr_factory_.GetWeakPtr()));
405}
406
407void HIDDetectionScreenHandler::UpdateBTDevices() {
408  if (!adapter_.get() || !adapter_->IsPresent() || !adapter_->IsPowered())
409    return;
410
411  // If no connected devices found as pointing device and keyboard, we try to
412  // connect some type-suitable active bluetooth device.
413  std::vector<device::BluetoothDevice*> bt_devices = adapter_->GetDevices();
414  for (std::vector<device::BluetoothDevice*>::const_iterator it =
415           bt_devices.begin();
416       it != bt_devices.end() &&
417           (keyboard_device_id_.empty() || pointing_device_id_.empty());
418       ++it) {
419    TryPairingAsPointingDevice(*it);
420    TryPairingAsKeyboardDevice(*it);
421  }
422}
423
424void HIDDetectionScreenHandler::ProcessConnectedDevicesList(
425    const std::vector<InputDeviceInfo>& devices) {
426  for (std::vector<InputDeviceInfo>::const_iterator it = devices.begin();
427       it != devices.end() &&
428       (pointing_device_id_.empty() || keyboard_device_id_.empty());
429       ++it) {
430    if (pointing_device_id_.empty() && DeviceIsPointing(*it)) {
431      pointing_device_id_ = it->id;
432      pointing_device_name_ = it->name;
433      pointing_device_connect_type_ = it->type;
434      if (page_is_ready())
435        SendPointingDeviceNotification();
436    }
437    if (keyboard_device_id_.empty() && it->is_keyboard) {
438      keyboard_device_id_ = it->id;
439      keyboard_device_name_ = it->name;
440      keyboard_device_connect_type_ = it->type;
441      if (page_is_ready())
442        SendKeyboardDeviceNotification(NULL);
443    }
444  }
445}
446
447void HIDDetectionScreenHandler::TryInitiateBTDevicesUpdate() {
448  if ((pointing_device_id_.empty() || keyboard_device_id_.empty()) &&
449      adapter_.get()) {
450    if (!adapter_->IsPresent()) {
451      // Switch on BT adapter later when it's available.
452      switch_on_adapter_when_ready_ = true;
453    } else if (!adapter_->IsPowered()) {
454      VLOG(1) << "Switching on BT adapter on HID OOBE screen.";
455      adapter_initially_powered_.reset(new bool(false));
456      adapter_->SetPowered(
457          true,
458          base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
459                     weak_ptr_factory_.GetWeakPtr()),
460          base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
461                     weak_ptr_factory_.GetWeakPtr()));
462    } else {
463      UpdateBTDevices();
464    }
465  }
466}
467
468void HIDDetectionScreenHandler::OnGetInputDevicesListForCheck(
469    const base::Callback<void(bool)>& on_check_done,
470    const std::vector<InputDeviceInfo>& devices) {
471  ProcessConnectedDevicesList(devices);
472
473  // Screen is not required if both devices are present.
474  bool all_devices_autodetected = !pointing_device_id_.empty() &&
475                                  !keyboard_device_id_.empty();
476  UMA_HISTOGRAM_BOOLEAN("HIDDetection.OOBEDialogShown",
477                        !all_devices_autodetected);
478
479  on_check_done.Run(!all_devices_autodetected);
480}
481
482void HIDDetectionScreenHandler::OnGetInputDevicesList(
483    const std::vector<InputDeviceInfo>& devices) {
484  ProcessConnectedDevicesList(devices);
485  TryInitiateBTDevicesUpdate();
486}
487
488void HIDDetectionScreenHandler::ConnectBTDevice(
489    device::BluetoothDevice* device) {
490  if (!device->IsPairable() || device->IsPaired())
491    return;
492  device::BluetoothDevice::DeviceType device_type = device->GetDeviceType();
493
494  if (device_type == device::BluetoothDevice::DEVICE_MOUSE ||
495      device_type == device::BluetoothDevice::DEVICE_TABLET) {
496    if (mouse_is_pairing_)
497      return;
498    mouse_is_pairing_ = true;
499  } else if (device_type == device::BluetoothDevice::DEVICE_KEYBOARD) {
500    if (keyboard_is_pairing_)
501      return;
502    keyboard_is_pairing_ = true;
503  } else if (device_type ==
504      device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO) {
505    if (mouse_is_pairing_ || keyboard_is_pairing_)
506      return;
507    mouse_is_pairing_ = true;
508    keyboard_is_pairing_ = true;
509  }
510  device->Connect(this,
511            base::Bind(&HIDDetectionScreenHandler::BTConnected,
512                       weak_ptr_factory_.GetWeakPtr(), device_type),
513            base::Bind(&HIDDetectionScreenHandler::BTConnectError,
514                       weak_ptr_factory_.GetWeakPtr(),
515                       device->GetAddress(), device_type));
516}
517
518void HIDDetectionScreenHandler::BTConnected(
519    device::BluetoothDevice::DeviceType device_type) {
520  if (DeviceIsPointing(device_type))
521    mouse_is_pairing_ = false;
522  if (DeviceIsKeyboard(device_type))
523    keyboard_is_pairing_ = false;
524}
525
526void HIDDetectionScreenHandler::BTConnectError(
527    const std::string& address,
528    device::BluetoothDevice::DeviceType device_type,
529    device::BluetoothDevice::ConnectErrorCode error_code) {
530  LOG(WARNING) << "BTConnectError while connecting " << address
531               << " error code = " << error_code;
532  if (DeviceIsPointing(device_type))
533    mouse_is_pairing_ = false;
534  if (DeviceIsKeyboard(device_type)) {
535    keyboard_is_pairing_ = false;
536    SendKeyboardDeviceNotification(NULL);
537  }
538
539  if (pointing_device_id_.empty() || keyboard_device_id_.empty())
540    UpdateDevices();
541}
542
543
544void HIDDetectionScreenHandler::SendPointingDeviceNotification() {
545  std::string state;
546  if (pointing_device_id_.empty())
547    state = kSearchingState;
548  else if (pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH)
549    state = kBTPairedState;
550  else
551    state = kUSBConnectedState;
552  CallJS("setPointingDeviceState", state);
553}
554
555void HIDDetectionScreenHandler::SendKeyboardDeviceNotification(
556    base::DictionaryValue* params) {
557  base::DictionaryValue state_info;
558  if (params)
559    state_info.MergeDictionary(params);
560
561  base::string16 device_name;
562  if (!state_info.GetString(kDeviceNameArgName, &device_name)) {
563    device_name = l10n_util::GetStringUTF16(
564        IDS_HID_DETECTION_DEFAULT_KEYBOARD_NAME);
565  }
566
567  if (keyboard_device_id_.empty()) {
568    if (!state_info.HasKey("state")) {
569      state_info.SetString("state", kSearchingState);
570    } else if (state_info.HasKey(kPincodeArgName)) {
571      state_info.SetString(
572          kLabelArgName,
573          l10n_util::GetStringFUTF16(
574              IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST,
575              device_name));
576    }
577  } else if (keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) {
578    state_info.SetString("state", kBTPairedState);
579    state_info.SetString(
580        kLabelArgName,
581        l10n_util::GetStringFUTF16(
582            IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD,
583            base::UTF8ToUTF16(keyboard_device_name_)));
584  } else {
585    state_info.SetString("state", kUSBConnectedState);
586  }
587  CallJS("setKeyboardDeviceState", state_info);
588}
589
590}  // namespace chromeos
591