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/strings/string16.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
14 #include "device/bluetooth/bluetooth_adapter_factory.h"
15 #include "grit/chromium_strings.h"
16 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
21 const char kJsScreenPath[] = "login.HIDDetectionScreen";
23 // Variants of pairing state.
24 const char kRemotePinCode[] = "bluetoothRemotePinCode";
25 const char kRemotePasskey[] = "bluetoothRemotePasskey";
27 // Possible ui-states for device-blocks.
28 const char kSearchingState[] = "searching";
29 const char kUSBConnectedState[] = "connected";
30 const char kBTPairedState[] = "paired";
31 const char kBTPairingState[] = "pairing";
32 // Special state for notifications that don't switch ui-state, but add info.
33 const char kBTUpdateState[] = "update";
35 // Names of possible arguments used for ui update.
36 const char kPincodeArgName[] = "pincode";
37 const char kDeviceNameArgName[] = "name";
38 const char kLabelArgName[] = "keyboard-label";
40 // Standard length of pincode for pairing BT keyboards.
41 const int kPincodeLength = 6;
43 bool DeviceIsPointing(device::BluetoothDevice::DeviceType device_type) {
44 return device_type == device::BluetoothDevice::DEVICE_MOUSE ||
45 device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO ||
46 device_type == device::BluetoothDevice::DEVICE_TABLET;
49 bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo& info) {
50 return info.is_mouse || info.is_touchpad || info.is_touchscreen ||
54 bool DeviceIsKeyboard(device::BluetoothDevice::DeviceType device_type) {
55 return device_type == device::BluetoothDevice::DEVICE_KEYBOARD ||
56 device_type == device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO;
63 HIDDetectionScreenHandler::HIDDetectionScreenHandler()
64 : BaseScreenHandler(kJsScreenPath),
67 mouse_is_pairing_(false),
68 keyboard_is_pairing_(false),
69 switch_on_adapter_when_ready_(false),
70 skip_screen_if_devices_present_(true),
71 weak_ptr_factory_(this) {
74 HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
76 adapter_->RemoveObserver(this);
77 input_service_proxy_.RemoveObserver(this);
79 delegate_->OnActorDestroyed(this);
82 void HIDDetectionScreenHandler::OnStartDiscoverySession(
83 scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
84 VLOG(1) << "BT Discovery session started";
85 discovery_session_ = discovery_session.Pass();
89 void HIDDetectionScreenHandler::SetPoweredError() {
90 LOG(ERROR) << "Failed to power BT adapter";
93 void HIDDetectionScreenHandler::FindDevicesError() {
94 VLOG(1) << "Failed to start Bluetooth discovery.";
97 void HIDDetectionScreenHandler::Show() {
98 if (!page_is_ready()) {
102 input_service_proxy_.AddObserver(this);
103 skip_screen_if_devices_present_ = true;
105 ShowScreen(OobeUI::kScreenHIDDetection, NULL);
108 void HIDDetectionScreenHandler::Hide() {
110 adapter_->RemoveObserver(this);
111 input_service_proxy_.RemoveObserver(this);
114 void HIDDetectionScreenHandler::SetDelegate(Delegate* delegate) {
115 delegate_ = delegate;
120 void HIDDetectionScreenHandler::DeclareLocalizedValues(
121 LocalizedValuesBuilder* builder) {
122 builder->Add("hidDetectionContinue", IDS_HID_DETECTION_CONTINUE_BUTTON);
123 builder->Add("hidDetectionInvitation", IDS_HID_DETECTION_INVITATION_TEXT);
124 builder->Add("hidDetectionPrerequisites",
125 IDS_HID_DETECTION_PRECONDITION_TEXT);
126 builder->Add("hidDetectionMouseSearching", IDS_HID_DETECTION_SEARCHING_MOUSE);
127 builder->Add("hidDetectionKeyboardSearching",
128 IDS_HID_DETECTION_SEARCHING_KEYBOARD);
129 builder->Add("hidDetectionUSBMouseConnected",
130 IDS_HID_DETECTION_CONNECTED_USB_MOUSE);
131 builder->Add("hidDetectionUSBKeyboardConnected",
132 IDS_HID_DETECTION_CONNECTED_USB_KEYBOARD);
133 builder->Add("hidDetectionBTMousePaired",
134 IDS_HID_DETECTION_PAIRED_BLUETOOTH_MOUSE);
135 builder->Add("hidDetectionBTEnterKey", IDS_HID_DETECTION_BLUETOOTH_ENTER_KEY);
138 void HIDDetectionScreenHandler::Initialize() {
139 if (!page_is_ready() || !delegate_)
142 device::BluetoothAdapterFactory::GetAdapter(
143 base::Bind(&HIDDetectionScreenHandler::InitializeAdapter,
144 weak_ptr_factory_.GetWeakPtr()));
148 show_on_init_ = false;
152 void HIDDetectionScreenHandler::RegisterMessages() {
154 "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
157 void HIDDetectionScreenHandler::HandleOnContinue() {
162 void HIDDetectionScreenHandler::InitializeAdapter(
163 scoped_refptr<device::BluetoothAdapter> adapter) {
165 CHECK(adapter_.get());
167 adapter_->AddObserver(this);
171 void HIDDetectionScreenHandler::StartBTDiscoverySession() {
172 adapter_->StartDiscoverySession(
173 base::Bind(&HIDDetectionScreenHandler::OnStartDiscoverySession,
174 weak_ptr_factory_.GetWeakPtr()),
175 base::Bind(&HIDDetectionScreenHandler::FindDevicesError,
176 weak_ptr_factory_.GetWeakPtr()));
179 void HIDDetectionScreenHandler::RequestPinCode(
180 device::BluetoothDevice* device) {
181 VLOG(1) << "RequestPinCode id = " << device->GetDeviceID()
182 << " name = " << device->GetName();
183 device->CancelPairing();
186 void HIDDetectionScreenHandler::RequestPasskey(
187 device::BluetoothDevice* device) {
188 VLOG(1) << "RequestPassKey id = " << device->GetDeviceID()
189 << " name = " << device->GetName();
190 device->CancelPairing();
193 void HIDDetectionScreenHandler::DisplayPinCode(device::BluetoothDevice* device,
194 const std::string& pincode) {
195 VLOG(1) << "DisplayPinCode id = " << device->GetDeviceID()
196 << " name = " << device->GetName();
197 base::DictionaryValue params;
198 params.SetString("state", kBTPairingState);
199 params.SetString("pairing-state", kRemotePinCode);
200 params.SetString("pincode", pincode);
201 params.SetString(kDeviceNameArgName, device->GetName());
202 SendKeyboardDeviceNotification(¶ms);
205 void HIDDetectionScreenHandler::DisplayPasskey(
206 device::BluetoothDevice* device, uint32 passkey) {
207 VLOG(1) << "DisplayPassKey id = " << device->GetDeviceID()
208 << " name = " << device->GetName();
209 base::DictionaryValue params;
210 params.SetString("state", kBTPairingState);
211 params.SetString("pairing-state", kRemotePasskey);
212 params.SetInteger("passkey", passkey);
213 std::string pincode = base::UintToString(passkey);
214 pincode = std::string(kPincodeLength - pincode.length(), '0').append(pincode);
215 params.SetString("pincode", pincode);
216 params.SetString(kDeviceNameArgName, device->GetName());
217 SendKeyboardDeviceNotification(¶ms);
220 void HIDDetectionScreenHandler::KeysEntered(
221 device::BluetoothDevice* device, uint32 entered) {
222 VLOG(1) << "Keys entered";
223 base::DictionaryValue params;
224 params.SetString("state", kBTUpdateState);
225 params.SetInteger("keysEntered", entered);
226 SendKeyboardDeviceNotification(¶ms);
229 void HIDDetectionScreenHandler::ConfirmPasskey(
230 device::BluetoothDevice* device, uint32 passkey) {
231 VLOG(1) << "Confirm Passkey";
232 device->CancelPairing();
235 void HIDDetectionScreenHandler::AuthorizePairing(
236 device::BluetoothDevice* device) {
237 // There is never any circumstance where this will be called, since the
238 // HID detection screen handler will only be used for outgoing pairing
239 // requests, but play it safe.
240 VLOG(1) << "Authorize pairing";
241 device->ConfirmPairing();
244 void HIDDetectionScreenHandler::AdapterPresentChanged(
245 device::BluetoothAdapter* adapter, bool present) {
246 if (present && switch_on_adapter_when_ready_) {
247 adapter_->SetPowered(
249 base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
250 weak_ptr_factory_.GetWeakPtr()),
251 base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
252 weak_ptr_factory_.GetWeakPtr()));
256 void HIDDetectionScreenHandler::TryPairingAsPointingDevice(
257 device::BluetoothDevice* device) {
258 if (pointing_device_id_.empty() &&
259 DeviceIsPointing(device->GetDeviceType()) &&
260 device->IsPairable() && !device->IsPaired() && !mouse_is_pairing_) {
261 ConnectBTDevice(device);
265 void HIDDetectionScreenHandler::TryPairingAsKeyboardDevice(
266 device::BluetoothDevice* device) {
267 if (keyboard_device_id_.empty() &&
268 DeviceIsKeyboard(device->GetDeviceType()) &&
269 device->IsPairable() && !device->IsPaired() && !keyboard_is_pairing_) {
270 ConnectBTDevice(device);
274 void HIDDetectionScreenHandler::DeviceAdded(
275 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
276 VLOG(1) << "BT input device added id = " << device->GetDeviceID() <<
277 " name = " << device->GetName();
278 TryPairingAsPointingDevice(device);
279 TryPairingAsKeyboardDevice(device);
282 void HIDDetectionScreenHandler::DeviceChanged(
283 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
284 VLOG(1) << "BT device changed id = " << device->GetDeviceID() << " name = " <<
286 TryPairingAsPointingDevice(device);
287 TryPairingAsKeyboardDevice(device);
290 void HIDDetectionScreenHandler::DeviceRemoved(
291 device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
292 VLOG(1) << "BT device removed id = " << device->GetDeviceID() << " name = " <<
296 void HIDDetectionScreenHandler::OnInputDeviceAdded(
297 const InputDeviceInfo& info) {
298 VLOG(1) << "Input device added id = " << info.id << " name = " << info.name;
299 // TODO(merkulova): deal with all available device types, e.g. joystick.
300 if (!keyboard_device_id_.empty() && !pointing_device_id_.empty())
303 if (pointing_device_id_.empty() && DeviceIsPointing(info)) {
304 pointing_device_id_ = info.id;
305 pointing_device_name_ = info.name;
306 pointing_device_connect_type_ = info.type;
307 SendPointingDeviceNotification();
309 if (keyboard_device_id_.empty() && info.is_keyboard) {
310 keyboard_device_id_ = info.id;
311 keyboard_device_name_ = info.name;
312 keyboard_device_connect_type_ = info.type;
313 SendKeyboardDeviceNotification(NULL);
317 void HIDDetectionScreenHandler::OnInputDeviceRemoved(const std::string& id) {
318 if (id == keyboard_device_id_) {
319 keyboard_device_id_.clear();
320 keyboard_device_name_.clear();
321 keyboard_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
322 SendKeyboardDeviceNotification(NULL);
324 } else if (id == pointing_device_id_) {
325 pointing_device_id_.clear();
326 pointing_device_name_.clear();
327 pointing_device_connect_type_ = InputDeviceInfo::TYPE_UNKNOWN;
328 SendPointingDeviceNotification();
333 void HIDDetectionScreenHandler::UpdateDevices() {
334 input_service_proxy_.GetDevices(
335 base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesList,
336 base::Unretained(this)));
339 void HIDDetectionScreenHandler::UpdateBTDevices() {
340 if (!adapter_ || !adapter_->IsPresent() || !adapter_->IsPowered())
343 // If no connected devices found as pointing device and keyboard, we try to
344 // connect some type-suitable active bluetooth device.
345 std::vector<device::BluetoothDevice*> bt_devices = adapter_->GetDevices();
346 for (std::vector<device::BluetoothDevice*>::const_iterator it =
348 it != bt_devices.end() &&
349 (keyboard_device_id_.empty() || pointing_device_id_.empty());
351 TryPairingAsPointingDevice(*it);
352 TryPairingAsKeyboardDevice(*it);
356 void HIDDetectionScreenHandler::OnGetInputDevicesList(
357 const std::vector<InputDeviceInfo>& devices) {
358 for (std::vector<InputDeviceInfo>::const_iterator it = devices.begin();
359 it != devices.end() &&
360 (pointing_device_id_.empty() || keyboard_device_id_.empty());
362 if (pointing_device_id_.empty() && DeviceIsPointing(*it)) {
363 pointing_device_id_ = it->id;
364 pointing_device_name_ = it->name;
365 pointing_device_connect_type_ = it->type;
366 SendPointingDeviceNotification();
368 if (keyboard_device_id_.empty() && it->is_keyboard) {
369 keyboard_device_id_ = it->id;
370 keyboard_device_name_ = it->name;
371 keyboard_device_connect_type_ = it->type;
372 SendKeyboardDeviceNotification(NULL);
375 // Skip screen if both devices are present and skip was requested.
376 if (!pointing_device_id_.empty() &&
377 !keyboard_device_id_.empty() &&
378 skip_screen_if_devices_present_) {
381 // Skip requested only once on dialog show.
382 skip_screen_if_devices_present_ = false;
383 if ((pointing_device_id_.empty() || keyboard_device_id_.empty()) &&
385 if (!adapter_->IsPresent()) {
386 // Switch on BT adapter later when it's available.
387 switch_on_adapter_when_ready_ = true;
388 } else if (!adapter_->IsPowered()) {
389 adapter_->SetPowered(
391 base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
392 weak_ptr_factory_.GetWeakPtr()),
393 base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
394 weak_ptr_factory_.GetWeakPtr()));
401 void HIDDetectionScreenHandler::ConnectBTDevice(
402 device::BluetoothDevice* device) {
403 if (!device->IsPairable() || device->IsPaired())
405 device::BluetoothDevice::DeviceType device_type = device->GetDeviceType();
407 if (device_type == device::BluetoothDevice::DEVICE_MOUSE ||
408 device_type == device::BluetoothDevice::DEVICE_TABLET) {
409 if (mouse_is_pairing_)
411 mouse_is_pairing_ = true;
412 } else if (device_type == device::BluetoothDevice::DEVICE_KEYBOARD) {
413 if (keyboard_is_pairing_)
415 keyboard_is_pairing_ = true;
416 } else if (device_type ==
417 device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO) {
418 if (mouse_is_pairing_ || keyboard_is_pairing_)
420 mouse_is_pairing_ = true;
421 keyboard_is_pairing_ = true;
423 device->Connect(this,
424 base::Bind(&HIDDetectionScreenHandler::BTConnected,
425 weak_ptr_factory_.GetWeakPtr(), device_type),
426 base::Bind(&HIDDetectionScreenHandler::BTConnectError,
427 weak_ptr_factory_.GetWeakPtr(),
428 device->GetAddress(), device_type));
431 void HIDDetectionScreenHandler::BTConnected(
432 device::BluetoothDevice::DeviceType device_type) {
433 if (DeviceIsPointing(device_type))
434 mouse_is_pairing_ = false;
435 if (DeviceIsKeyboard(device_type))
436 keyboard_is_pairing_ = false;
439 void HIDDetectionScreenHandler::BTConnectError(
440 const std::string& address,
441 device::BluetoothDevice::DeviceType device_type,
442 device::BluetoothDevice::ConnectErrorCode error_code) {
443 LOG(WARNING) << "BTConnectError while connecting " << address
444 << " error code = " << error_code;
445 if (DeviceIsPointing(device_type))
446 mouse_is_pairing_ = false;
447 if (DeviceIsKeyboard(device_type))
448 keyboard_is_pairing_ = false;
450 if (pointing_device_id_.empty() || keyboard_device_id_.empty())
455 void HIDDetectionScreenHandler::SendPointingDeviceNotification() {
457 if (pointing_device_id_.empty())
458 state = kSearchingState;
459 else if (pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH)
460 state = kBTPairedState;
462 state = kUSBConnectedState;
463 CallJS("setPointingDeviceState", state);
466 void HIDDetectionScreenHandler::SendKeyboardDeviceNotification(
467 base::DictionaryValue* params) {
468 base::DictionaryValue state_info;
470 state_info.MergeDictionary(params);
472 base::string16 device_name;
473 if (!state_info.GetString(kDeviceNameArgName, &device_name)) {
474 device_name = l10n_util::GetStringUTF16(
475 IDS_HID_DETECTION_DEFAULT_KEYBOARD_NAME);
478 if (keyboard_device_id_.empty()) {
479 if (!state_info.HasKey("state")) {
480 state_info.SetString("state", kSearchingState);
481 } else if (state_info.HasKey(kPincodeArgName)) {
482 state_info.SetString(
484 l10n_util::GetStringFUTF16(
485 IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST,
488 } else if (keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) {
489 state_info.SetString("state", kBTPairedState);
490 state_info.SetString(
492 l10n_util::GetStringFUTF16(
493 IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD,
494 base::UTF8ToUTF16(keyboard_device_name_)));
496 state_info.SetString("state", kUSBConnectedState);
498 CallJS("setKeyboardDeviceState", state_info);
501 } // namespace chromeos