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.
5 #include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
8 #include "base/compiler_specific.h"
9 #include "base/macros.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
17 #include "chrome/common/pref_names.h"
18 #include "device/bluetooth/bluetooth_adapter_factory.h"
19 #include "grit/chromium_strings.h"
20 #include "grit/generated_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
25 const char kJsScreenPath[] = "login.HIDDetectionScreen";
27 // Variants of pairing state.
28 const char kRemotePinCode[] = "bluetoothRemotePinCode";
29 const char kRemotePasskey[] = "bluetoothRemotePasskey";
31 // Possible ui-states for device-blocks.
32 const char kSearchingState[] = "searching";
33 const char kUSBConnectedState[] = "connected";
34 const char kBTPairedState[] = "paired";
35 const char kBTPairingState[] = "pairing";
36 // Special state for notifications that don't switch ui-state, but add info.
37 const char kBTUpdateState[] = "update";
39 // Names of possible arguments used for ui update.
40 const char kPincodeArgName[] = "pincode";
41 const char kDeviceNameArgName[] = "name";
42 const char kLabelArgName[] = "keyboard-label";
44 // Standard length of pincode for pairing BT keyboards.
45 const int kPincodeLength = 6;
47 bool DeviceIsPointing(device::BluetoothDevice::DeviceType device_type) {
48 return device_type == device::BluetoothDevice::DEVICE_MOUSE ||
49 device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO ||
50 device_type == device::BluetoothDevice::DEVICE_TABLET;
53 bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo& info) {
54 return info.is_mouse || info.is_touchpad || info.is_touchscreen ||
58 bool DeviceIsKeyboard(device::BluetoothDevice::DeviceType device_type) {
59 return device_type == device::BluetoothDevice::DEVICE_KEYBOARD ||
60 device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO;
67 HIDDetectionScreenHandler::HIDDetectionScreenHandler(
68 CoreOobeActor* core_oobe_actor)
69 : BaseScreenHandler(kJsScreenPath),
71 core_oobe_actor_(core_oobe_actor),
73 mouse_is_pairing_(false),
74 keyboard_is_pairing_(false),
75 switch_on_adapter_when_ready_(false),
76 first_time_screen_show_(true),
77 weak_ptr_factory_(this) {
80 HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
82 adapter_->RemoveObserver(this);
83 input_service_proxy_.RemoveObserver(this);
85 delegate_->OnActorDestroyed(this);
88 void HIDDetectionScreenHandler::OnStartDiscoverySession(
89 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
90 VLOG(1) << "BT Discovery session started";
91 discovery_session_ = discovery_session.Pass();
95 void HIDDetectionScreenHandler::SetPoweredError() {
96 LOG(ERROR) << "Failed to power BT adapter";
99 void HIDDetectionScreenHandler::FindDevicesError() {
100 VLOG(1) << "Failed to start Bluetooth discovery.";
103 void HIDDetectionScreenHandler::Show() {
104 if (!page_is_ready()) {
105 show_on_init_ = true;
108 core_oobe_actor_->InitDemoModeDetection();
109 input_service_proxy_.AddObserver(this);
110 first_time_screen_show_ = true;
111 GetDevicesFirstTime();
112 ShowScreen(OobeUI::kScreenHIDDetection, NULL);
115 void HIDDetectionScreenHandler::Hide() {
117 adapter_->RemoveObserver(this);
118 input_service_proxy_.RemoveObserver(this);
121 void HIDDetectionScreenHandler::SetDelegate(Delegate* delegate) {
122 delegate_ = delegate;
127 void HIDDetectionScreenHandler::DeclareLocalizedValues(
128 LocalizedValuesBuilder* builder) {
129 builder->Add("hidDetectionContinue", IDS_HID_DETECTION_CONTINUE_BUTTON);
130 builder->Add("hidDetectionInvitation", IDS_HID_DETECTION_INVITATION_TEXT);
131 builder->Add("hidDetectionPrerequisites",
132 IDS_HID_DETECTION_PRECONDITION_TEXT);
133 builder->Add("hidDetectionMouseSearching", IDS_HID_DETECTION_SEARCHING_MOUSE);
134 builder->Add("hidDetectionKeyboardSearching",
135 IDS_HID_DETECTION_SEARCHING_KEYBOARD);
136 builder->Add("hidDetectionUSBMouseConnected",
137 IDS_HID_DETECTION_CONNECTED_USB_MOUSE);
138 builder->Add("hidDetectionUSBKeyboardConnected",
139 IDS_HID_DETECTION_CONNECTED_USB_KEYBOARD);
140 builder->Add("hidDetectionBTMousePaired",
141 IDS_HID_DETECTION_PAIRED_BLUETOOTH_MOUSE);
142 builder->Add("hidDetectionBTEnterKey", IDS_HID_DETECTION_BLUETOOTH_ENTER_KEY);
145 void HIDDetectionScreenHandler::Initialize() {
146 if (!page_is_ready() || !delegate_)
149 device::BluetoothAdapterFactory::GetAdapter(
150 base::Bind(&HIDDetectionScreenHandler::InitializeAdapter,
151 weak_ptr_factory_.GetWeakPtr()));
155 show_on_init_ = false;
159 void HIDDetectionScreenHandler::RegisterMessages() {
161 "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
164 void HIDDetectionScreenHandler::HandleOnContinue() {
165 if (!first_time_screen_show_) {
166 // Continue button pressed.
167 ContinueScenarioType scenario_type;
168 if (!pointing_device_id_.empty() && !keyboard_device_id_.empty())
169 scenario_type = All_DEVICES_DETECTED;
170 else if (pointing_device_id_.empty())
171 scenario_type = KEYBOARD_DEVICE_ONLY_DETECTED;
173 scenario_type = POINTING_DEVICE_ONLY_DETECTED;
175 UMA_HISTOGRAM_ENUMERATION(
176 "HIDDetection.OOBEDevicesDetectedOnContinuePressed",
178 CONTINUE_SCENARIO_TYPE_SIZE);
180 core_oobe_actor_->StopDemoModeDetection();
185 void HIDDetectionScreenHandler::InitializeAdapter(
186 scoped_refptr<device::BluetoothAdapter> adapter) {
188 CHECK(adapter_.get());
190 adapter_->AddObserver(this);
194 void HIDDetectionScreenHandler::StartBTDiscoverySession() {
195 adapter_->StartDiscoverySession(
196 base::Bind(&HIDDetectionScreenHandler::OnStartDiscoverySession,
197 weak_ptr_factory_.GetWeakPtr()),
198 base::Bind(&HIDDetectionScreenHandler::FindDevicesError,
199 weak_ptr_factory_.GetWeakPtr()));
202 void HIDDetectionScreenHandler::RequestPinCode(
203 device::BluetoothDevice* device) {
204 VLOG(1) << "RequestPinCode id = " << device->GetDeviceID()
205 << " name = " << device->GetName();
206 device->CancelPairing();
209 void HIDDetectionScreenHandler::RequestPasskey(
210 device::BluetoothDevice* device) {
211 VLOG(1) << "RequestPassKey id = " << device->GetDeviceID()
212 << " name = " << device->GetName();
213 device->CancelPairing();
216 void HIDDetectionScreenHandler::DisplayPinCode(device::BluetoothDevice* device,
217 const std::string& pincode) {
218 VLOG(1) << "DisplayPinCode id = " << device->GetDeviceID()
219 << " name = " << device->GetName();
220 base::DictionaryValue params;
221 params.SetString("state", kBTPairingState);
222 params.SetString("pairing-state", kRemotePinCode);
223 params.SetString("pincode", pincode);
224 params.SetString(kDeviceNameArgName, device->GetName());
225 SendKeyboardDeviceNotification(¶ms);
228 void HIDDetectionScreenHandler::DisplayPasskey(
229 device::BluetoothDevice* device, uint32 passkey) {
230 VLOG(1) << "DisplayPassKey id = " << device->GetDeviceID()
231 << " name = " << device->GetName();
232 base::DictionaryValue params;
233 params.SetString("state", kBTPairingState);
234 params.SetString("pairing-state", kRemotePasskey);
235 params.SetInteger("passkey", passkey);
236 std::string pincode = base::UintToString(passkey);
237 pincode = std::string(kPincodeLength - pincode.length(), '0').append(pincode);
238 params.SetString("pincode", pincode);
239 params.SetString(kDeviceNameArgName, device->GetName());
240 SendKeyboardDeviceNotification(¶ms);
243 void HIDDetectionScreenHandler::KeysEntered(
244 device::BluetoothDevice* device, uint32 entered) {
245 VLOG(1) << "Keys entered";
246 base::DictionaryValue params;
247 params.SetString("state", kBTUpdateState);
248 params.SetInteger("keysEntered", entered);
249 SendKeyboardDeviceNotification(¶ms);
252 void HIDDetectionScreenHandler::ConfirmPasskey(
253 device::BluetoothDevice* device, uint32 passkey) {
254 VLOG(1) << "Confirm Passkey";
255 device->CancelPairing();
258 void HIDDetectionScreenHandler::AuthorizePairing(
259 device::BluetoothDevice* device) {
260 // There is never any circumstance where this will be called, since the
261 // HID detection screen handler will only be used for outgoing pairing
262 // requests, but play it safe.
263 VLOG(1) << "Authorize pairing";
264 device->ConfirmPairing();
267 void HIDDetectionScreenHandler::AdapterPresentChanged(
268 device::BluetoothAdapter* adapter, bool present) {
269 if (present && switch_on_adapter_when_ready_) {
270 adapter_->SetPowered(
272 base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
273 weak_ptr_factory_.GetWeakPtr()),
274 base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
275 weak_ptr_factory_.GetWeakPtr()));
279 void HIDDetectionScreenHandler::TryPairingAsPointingDevice(
280 device::BluetoothDevice* device) {
281 if (pointing_device_id_.empty() &&
282 DeviceIsPointing(device->GetDeviceType()) &&
283 device->IsPairable() && !device->IsPaired() && !mouse_is_pairing_) {
284 ConnectBTDevice(device);
288 void HIDDetectionScreenHandler::TryPairingAsKeyboardDevice(
289 device::BluetoothDevice* device) {
290 if (keyboard_device_id_.empty() &&
291 DeviceIsKeyboard(device->GetDeviceType()) &&
292 device->IsPairable() && !device->IsPaired() && !keyboard_is_pairing_) {
293 ConnectBTDevice(device);
297 void HIDDetectionScreenHandler::DeviceAdded(
298 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
299 VLOG(1) << "BT input device added id = " << device->GetDeviceID() <<
300 " name = " << device->GetName();
301 TryPairingAsPointingDevice(device);
302 TryPairingAsKeyboardDevice(device);
305 void HIDDetectionScreenHandler::DeviceChanged(
306 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
307 VLOG(1) << "BT device changed id = " << device->GetDeviceID() << " name = " <<
309 TryPairingAsPointingDevice(device);
310 TryPairingAsKeyboardDevice(device);
313 void HIDDetectionScreenHandler::DeviceRemoved(
314 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
315 VLOG(1) << "BT device removed id = " << device->GetDeviceID() << " name = " <<
319 void HIDDetectionScreenHandler::OnInputDeviceAdded(
320 const InputDeviceInfo& info) {
321 VLOG(1) << "Input device added id = " << info.id << " name = " << info.name;
322 // TODO(merkulova): deal with all available device types, e.g. joystick.
323 if (!keyboard_device_id_.empty() && !pointing_device_id_.empty())
326 if (pointing_device_id_.empty() && DeviceIsPointing(info)) {
327 pointing_device_id_ = info.id;
328 pointing_device_name_ = info.name;
329 pointing_device_connect_type_ = info.type;
330 SendPointingDeviceNotification();
332 if (keyboard_device_id_.empty() && info.is_keyboard) {
333 keyboard_device_id_ = info.id;
334 keyboard_device_name_ = info.name;
335 keyboard_device_connect_type_ = info.type;
336 SendKeyboardDeviceNotification(NULL);
340 void HIDDetectionScreenHandler::OnInputDeviceRemoved(const std::string& id) {
341 if (id == keyboard_device_id_) {
342 keyboard_device_id_.clear();
343 keyboard_device_name_.clear();
344 keyboard_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
345 SendKeyboardDeviceNotification(NULL);
347 } else if (id == pointing_device_id_) {
348 pointing_device_id_.clear();
349 pointing_device_name_.clear();
350 pointing_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
351 SendPointingDeviceNotification();
357 void HIDDetectionScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
358 registry->RegisterIntegerPref(prefs::kTimesHIDDialogShown, 0);
361 void HIDDetectionScreenHandler::GetDevicesFirstTime() {
362 input_service_proxy_.GetDevices(
363 base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesListFirstTime,
364 weak_ptr_factory_.GetWeakPtr()));
367 void HIDDetectionScreenHandler::UpdateDevices() {
368 input_service_proxy_.GetDevices(
369 base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesList,
370 weak_ptr_factory_.GetWeakPtr()));
373 void HIDDetectionScreenHandler::UpdateBTDevices() {
374 if (!adapter_ || !adapter_->IsPresent() || !adapter_->IsPowered())
377 // If no connected devices found as pointing device and keyboard, we try to
378 // connect some type-suitable active bluetooth device.
379 std::vector<device::BluetoothDevice*> bt_devices = adapter_->GetDevices();
380 for (std::vector<device::BluetoothDevice*>::const_iterator it =
382 it != bt_devices.end() &&
383 (keyboard_device_id_.empty() || pointing_device_id_.empty());
385 TryPairingAsPointingDevice(*it);
386 TryPairingAsKeyboardDevice(*it);
390 void HIDDetectionScreenHandler::ProcessConnectedDevicesList(
391 const std::vector<InputDeviceInfo>& devices) {
392 for (std::vector<InputDeviceInfo>::const_iterator it = devices.begin();
393 it != devices.end() &&
394 (pointing_device_id_.empty() || keyboard_device_id_.empty());
396 if (pointing_device_id_.empty() && DeviceIsPointing(*it)) {
397 pointing_device_id_ = it->id;
398 pointing_device_name_ = it->name;
399 pointing_device_connect_type_ = it->type;
400 SendPointingDeviceNotification();
402 if (keyboard_device_id_.empty() && it->is_keyboard) {
403 keyboard_device_id_ = it->id;
404 keyboard_device_name_ = it->name;
405 keyboard_device_connect_type_ = it->type;
406 SendKeyboardDeviceNotification(NULL);
411 void HIDDetectionScreenHandler::TryInitiateBTDevicesUpdate() {
412 if ((pointing_device_id_.empty() || keyboard_device_id_.empty()) &&
414 if (!adapter_->IsPresent()) {
415 // Switch on BT adapter later when it's available.
416 switch_on_adapter_when_ready_ = true;
417 } else if (!adapter_->IsPowered()) {
418 adapter_->SetPowered(
420 base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
421 weak_ptr_factory_.GetWeakPtr()),
422 base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
423 weak_ptr_factory_.GetWeakPtr()));
430 void HIDDetectionScreenHandler::OnGetInputDevicesListFirstTime(
431 const std::vector<InputDeviceInfo>& devices) {
432 ProcessConnectedDevicesList(devices);
434 // Skip screen if both devices are present.
435 bool all_devices_autodetected = !pointing_device_id_.empty() &&
436 !keyboard_device_id_.empty();
437 UMA_HISTOGRAM_BOOLEAN("HIDDetection.OOBEDialogShown",
438 !all_devices_autodetected);
439 if (all_devices_autodetected) {
443 PrefService* local_state = g_browser_process->local_state();
444 int num_of_times_dialog_was_shown = local_state->GetInteger(
445 prefs::kTimesHIDDialogShown);
446 local_state->SetInteger(prefs::kTimesHIDDialogShown,
447 num_of_times_dialog_was_shown + 1);
448 first_time_screen_show_ = false;
450 TryInitiateBTDevicesUpdate();
453 void HIDDetectionScreenHandler::OnGetInputDevicesList(
454 const std::vector<InputDeviceInfo>& devices) {
455 ProcessConnectedDevicesList(devices);
456 TryInitiateBTDevicesUpdate();
459 void HIDDetectionScreenHandler::ConnectBTDevice(
460 device::BluetoothDevice* device) {
461 if (!device->IsPairable() || device->IsPaired())
463 device::BluetoothDevice::DeviceType device_type = device->GetDeviceType();
465 if (device_type == device::BluetoothDevice::DEVICE_MOUSE ||
466 device_type == device::BluetoothDevice::DEVICE_TABLET) {
467 if (mouse_is_pairing_)
469 mouse_is_pairing_ = true;
470 } else if (device_type == device::BluetoothDevice::DEVICE_KEYBOARD) {
471 if (keyboard_is_pairing_)
473 keyboard_is_pairing_ = true;
474 } else if (device_type ==
475 device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO) {
476 if (mouse_is_pairing_ || keyboard_is_pairing_)
478 mouse_is_pairing_ = true;
479 keyboard_is_pairing_ = true;
481 device->Connect(this,
482 base::Bind(&HIDDetectionScreenHandler::BTConnected,
483 weak_ptr_factory_.GetWeakPtr(), device_type),
484 base::Bind(&HIDDetectionScreenHandler::BTConnectError,
485 weak_ptr_factory_.GetWeakPtr(),
486 device->GetAddress(), device_type));
489 void HIDDetectionScreenHandler::BTConnected(
490 device::BluetoothDevice::DeviceType device_type) {
491 if (DeviceIsPointing(device_type))
492 mouse_is_pairing_ = false;
493 if (DeviceIsKeyboard(device_type))
494 keyboard_is_pairing_ = false;
497 void HIDDetectionScreenHandler::BTConnectError(
498 const std::string& address,
499 device::BluetoothDevice::DeviceType device_type,
500 device::BluetoothDevice::ConnectErrorCode error_code) {
501 LOG(WARNING) << "BTConnectError while connecting " << address
502 << " error code = " << error_code;
503 if (DeviceIsPointing(device_type))
504 mouse_is_pairing_ = false;
505 if (DeviceIsKeyboard(device_type)) {
506 keyboard_is_pairing_ = false;
507 SendKeyboardDeviceNotification(NULL);
510 if (pointing_device_id_.empty() || keyboard_device_id_.empty())
515 void HIDDetectionScreenHandler::SendPointingDeviceNotification() {
517 if (pointing_device_id_.empty())
518 state = kSearchingState;
519 else if (pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH)
520 state = kBTPairedState;
522 state = kUSBConnectedState;
523 CallJS("setPointingDeviceState", state);
526 void HIDDetectionScreenHandler::SendKeyboardDeviceNotification(
527 base::DictionaryValue* params) {
528 base::DictionaryValue state_info;
530 state_info.MergeDictionary(params);
532 base::string16 device_name;
533 if (!state_info.GetString(kDeviceNameArgName, &device_name)) {
534 device_name = l10n_util::GetStringUTF16(
535 IDS_HID_DETECTION_DEFAULT_KEYBOARD_NAME);
538 if (keyboard_device_id_.empty()) {
539 if (!state_info.HasKey("state")) {
540 state_info.SetString("state", kSearchingState);
541 } else if (state_info.HasKey(kPincodeArgName)) {
542 state_info.SetString(
544 l10n_util::GetStringFUTF16(
545 IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST,
548 } else if (keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) {
549 state_info.SetString("state", kBTPairedState);
550 state_info.SetString(
552 l10n_util::GetStringFUTF16(
553 IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD,
554 base::UTF8ToUTF16(keyboard_device_name_)));
556 state_info.SetString("state", kUSBConnectedState);
558 CallJS("setKeyboardDeviceState", state_info);
561 } // namespace chromeos