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/chromeos/login/screens/hid_detection_screen.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/login/screens/base_screen_delegate.h"
12 #include "chrome/browser/chromeos/login/screens/hid_detection_view.h"
13 #include "chrome/browser/chromeos/login/wizard_controller.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "device/bluetooth/bluetooth_adapter_factory.h"
16 #include "ui/base/l10n/l10n_util.h"
20 // Possible ui-states for device-blocks.
21 const char kSearchingState
[] = "searching";
22 const char kUSBConnectedState
[] = "connected";
23 const char kBTPairedState
[] = "paired";
24 const char kBTPairingState
[] = "pairing";
26 // Standard length of pincode for pairing BT keyboards.
27 const int kPincodeLength
= 6;
29 bool DeviceIsPointing(device::BluetoothDevice::DeviceType device_type
) {
30 return device_type
== device::BluetoothDevice::DEVICE_MOUSE
||
31 device_type
== device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO
||
32 device_type
== device::BluetoothDevice::DEVICE_TABLET
;
35 bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo
& info
) {
36 return info
.is_mouse
|| info
.is_touchpad
|| info
.is_touchscreen
||
40 bool DeviceIsKeyboard(device::BluetoothDevice::DeviceType device_type
) {
41 return device_type
== device::BluetoothDevice::DEVICE_KEYBOARD
||
42 device_type
== device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO
;
49 HIDDetectionScreen::HIDDetectionScreen(BaseScreenDelegate
* base_screen_delegate
,
50 HIDDetectionView
* view
)
51 : HIDDetectionModel(base_screen_delegate
),
53 mouse_is_pairing_(false),
54 pointing_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN
),
55 keyboard_is_pairing_(false),
56 keyboard_device_connect_type_(InputDeviceInfo::TYPE_UNKNOWN
),
57 switch_on_adapter_when_ready_(false),
59 weak_ptr_factory_(this) {
66 HIDDetectionScreen::~HIDDetectionScreen() {
67 adapter_initially_powered_
.reset();
68 input_service_proxy_
.RemoveObserver(this);
71 if (discovery_session_
.get())
72 discovery_session_
->Stop(base::Bind(&base::DoNothing
),
73 base::Bind(&base::DoNothing
));
75 adapter_
->RemoveObserver(this);
78 void HIDDetectionScreen::PrepareToShow() {
80 view_
->PrepareToShow();
83 void HIDDetectionScreen::Show() {
85 GetContextEditor().SetBoolean(kContextKeyNumKeysEnteredExpected
, false);
86 SendPointingDeviceNotification();
87 SendKeyboardDeviceNotification();
89 input_service_proxy_
.AddObserver(this);
96 void HIDDetectionScreen::Hide() {
98 input_service_proxy_
.RemoveObserver(this);
99 if (discovery_session_
.get())
100 discovery_session_
->Stop(base::Bind(&base::DoNothing
),
101 base::Bind(&base::DoNothing
));
106 void HIDDetectionScreen::Initialize(::login::ScreenContext
* context
) {
107 HIDDetectionModel::Initialize(context
);
109 device::BluetoothAdapterFactory::GetAdapter(
110 base::Bind(&HIDDetectionScreen::InitializeAdapter
,
111 weak_ptr_factory_
.GetWeakPtr()));
114 void HIDDetectionScreen::OnContinueButtonClicked() {
116 ContinueScenarioType scenario_type
;
117 if (!pointing_device_id_
.empty() && !keyboard_device_id_
.empty())
118 scenario_type
= All_DEVICES_DETECTED
;
119 else if (pointing_device_id_
.empty())
120 scenario_type
= KEYBOARD_DEVICE_ONLY_DETECTED
;
122 scenario_type
= POINTING_DEVICE_ONLY_DETECTED
;
124 UMA_HISTOGRAM_ENUMERATION(
125 "HIDDetection.OOBEDevicesDetectedOnContinuePressed",
127 CONTINUE_SCENARIO_TYPE_SIZE
);
129 // Switch off BT adapter if it was off before the screen and no BT device
131 bool adapter_is_powered
=
132 adapter_
.get() && adapter_
->IsPresent() && adapter_
->IsPowered();
134 pointing_device_connect_type_
== InputDeviceInfo::TYPE_BLUETOOTH
||
135 keyboard_device_connect_type_
== InputDeviceInfo::TYPE_BLUETOOTH
;
136 bool need_switching_off
= adapter_initially_powered_
&&
137 !(*adapter_initially_powered_
);
138 if (adapter_is_powered
&& !use_bluetooth
&& need_switching_off
) {
139 VLOG(1) << "Switching off BT adapter after HID OOBE screen as unused.";
140 adapter_
->SetPowered(
142 base::Bind(&base::DoNothing
),
143 base::Bind(&HIDDetectionScreen::SetPoweredOffError
,
144 weak_ptr_factory_
.GetWeakPtr()));
147 Finish(BaseScreenDelegate::HID_DETECTION_COMPLETED
);
150 void HIDDetectionScreen::CheckIsScreenRequired(
151 const base::Callback
<void(bool)>& on_check_done
) {
152 input_service_proxy_
.GetDevices(
153 base::Bind(&HIDDetectionScreen::OnGetInputDevicesListForCheck
,
154 weak_ptr_factory_
.GetWeakPtr(),
158 void HIDDetectionScreen::OnViewDestroyed(HIDDetectionView
* view
) {
163 void HIDDetectionScreen::RequestPinCode(device::BluetoothDevice
* device
) {
164 VLOG(1) << "RequestPinCode id = " << device
->GetDeviceID()
165 << " name = " << device
->GetName();
166 device
->CancelPairing();
169 void HIDDetectionScreen::RequestPasskey(device::BluetoothDevice
* device
) {
170 VLOG(1) << "RequestPassKey id = " << device
->GetDeviceID()
171 << " name = " << device
->GetName();
172 device
->CancelPairing();
175 void HIDDetectionScreen::DisplayPinCode(device::BluetoothDevice
* device
,
176 const std::string
& pincode
) {
177 VLOG(1) << "DisplayPinCode id = " << device
->GetDeviceID()
178 << " name = " << device
->GetName();
179 GetContextEditor().SetString(kContextKeyPinCode
, pincode
);
180 SetKeyboardDeviceName_(base::UTF16ToUTF8(device
->GetName()));
181 SendKeyboardDeviceNotification();
184 void HIDDetectionScreen::DisplayPasskey(
185 device::BluetoothDevice
* device
, uint32 passkey
) {
186 VLOG(1) << "DisplayPassKey id = " << device
->GetDeviceID()
187 << " name = " << device
->GetName();
188 std::string pincode
= base::UintToString(passkey
);
189 pincode
= std::string(kPincodeLength
- pincode
.length(), '0').append(pincode
);
190 // No differences in UI for passkey and pincode authentication calls.
191 DisplayPinCode(device
, pincode
);
194 void HIDDetectionScreen::KeysEntered(
195 device::BluetoothDevice
* device
, uint32 entered
) {
196 VLOG(1) << "Number of keys entered " << entered
;
198 .SetBoolean(kContextKeyNumKeysEnteredExpected
, true)
199 .SetInteger(kContextKeyNumKeysEnteredPinCode
, entered
);
200 SendKeyboardDeviceNotification();
203 void HIDDetectionScreen::ConfirmPasskey(
204 device::BluetoothDevice
* device
, uint32 passkey
) {
205 VLOG(1) << "Confirm Passkey";
206 device
->CancelPairing();
209 void HIDDetectionScreen::AuthorizePairing(device::BluetoothDevice
* device
) {
210 // There is never any circumstance where this will be called, since the
211 // HID detection screen will only be used for outgoing pairing
212 // requests, but play it safe.
213 VLOG(1) << "Authorize pairing";
214 device
->ConfirmPairing();
217 void HIDDetectionScreen::AdapterPresentChanged(
218 device::BluetoothAdapter
* adapter
, bool present
) {
219 if (present
&& switch_on_adapter_when_ready_
) {
220 VLOG(1) << "Switching on BT adapter on HID OOBE screen.";
221 adapter_initially_powered_
.reset(new bool(adapter_
->IsPowered()));
222 adapter_
->SetPowered(
224 base::Bind(&HIDDetectionScreen::StartBTDiscoverySession
,
225 weak_ptr_factory_
.GetWeakPtr()),
226 base::Bind(&HIDDetectionScreen::SetPoweredError
,
227 weak_ptr_factory_
.GetWeakPtr()));
231 void HIDDetectionScreen::TryPairingAsPointingDevice(
232 device::BluetoothDevice
* device
) {
233 if (pointing_device_id_
.empty() &&
234 DeviceIsPointing(device
->GetDeviceType()) &&
235 device
->IsPairable() &&
236 !(device
->IsConnected() && device
->IsPaired()) &&
237 !mouse_is_pairing_
) {
238 ConnectBTDevice(device
);
242 void HIDDetectionScreen::TryPairingAsKeyboardDevice(
243 device::BluetoothDevice
* device
) {
244 if (keyboard_device_id_
.empty() &&
245 DeviceIsKeyboard(device
->GetDeviceType()) &&
246 device
->IsPairable() &&
247 !(device
->IsConnected() && device
->IsPaired()) &&
248 !keyboard_is_pairing_
) {
249 ConnectBTDevice(device
);
253 void HIDDetectionScreen::ConnectBTDevice(device::BluetoothDevice
* device
) {
254 bool device_busy
= (device
->IsConnected() && device
->IsPaired()) ||
255 device
->IsConnecting();
256 if (!device
->IsPairable() || device_busy
)
258 device::BluetoothDevice::DeviceType device_type
= device
->GetDeviceType();
260 if (device_type
== device::BluetoothDevice::DEVICE_MOUSE
||
261 device_type
== device::BluetoothDevice::DEVICE_TABLET
) {
262 if (mouse_is_pairing_
)
264 mouse_is_pairing_
= true;
265 } else if (device_type
== device::BluetoothDevice::DEVICE_KEYBOARD
) {
266 if (keyboard_is_pairing_
)
268 keyboard_is_pairing_
= true;
269 } else if (device_type
==
270 device::BluetoothDevice::DEVICE_KEYBOARD_MOUSE_COMBO
) {
271 if (mouse_is_pairing_
&& keyboard_is_pairing_
)
273 mouse_is_pairing_
= true;
274 keyboard_is_pairing_
= true;
276 device
->Connect(this,
277 base::Bind(&HIDDetectionScreen::BTConnected
,
278 weak_ptr_factory_
.GetWeakPtr(), device_type
),
279 base::Bind(&HIDDetectionScreen::BTConnectError
,
280 weak_ptr_factory_
.GetWeakPtr(),
281 device
->GetAddress(), device_type
));
284 void HIDDetectionScreen::BTConnected(
285 device::BluetoothDevice::DeviceType device_type
) {
286 if (DeviceIsPointing(device_type
))
287 mouse_is_pairing_
= false;
288 if (DeviceIsKeyboard(device_type
)) {
289 keyboard_is_pairing_
= false;
291 .SetBoolean(kContextKeyNumKeysEnteredExpected
, false)
292 .SetString(kContextKeyPinCode
, "");
293 SendKeyboardDeviceNotification();
297 void HIDDetectionScreen::BTConnectError(
298 const std::string
& address
,
299 device::BluetoothDevice::DeviceType device_type
,
300 device::BluetoothDevice::ConnectErrorCode error_code
) {
301 LOG(WARNING
) << "BTConnectError while connecting " << address
302 << " error code = " << error_code
;
303 if (DeviceIsPointing(device_type
))
304 mouse_is_pairing_
= false;
305 if (DeviceIsKeyboard(device_type
)) {
306 keyboard_is_pairing_
= false;
308 .SetInteger(kContextKeyNumKeysEnteredExpected
, false)
309 .SetString(kContextKeyPinCode
, "");
310 SendKeyboardDeviceNotification();
313 if (pointing_device_id_
.empty() || keyboard_device_id_
.empty())
317 void HIDDetectionScreen::SendPointingDeviceNotification() {
319 if (pointing_device_id_
.empty())
320 state
= kSearchingState
;
321 else if (pointing_device_connect_type_
== InputDeviceInfo::TYPE_BLUETOOTH
)
322 state
= kBTPairedState
;
324 state
= kUSBConnectedState
;
325 GetContextEditor().SetString(kContextKeyMouseState
, state
)
327 kContextKeyContinueButtonEnabled
,
328 !(pointing_device_id_
.empty() && keyboard_device_id_
.empty()));
331 void HIDDetectionScreen::SendKeyboardDeviceNotification() {
332 ContextEditor editor
= GetContextEditor();
333 editor
.SetString(kContextKeyKeyboardLabel
, "");
334 if (keyboard_device_id_
.empty()) {
335 if (keyboard_is_pairing_
) {
336 editor
.SetString(kContextKeyKeyboardState
, kBTPairingState
)
338 kContextKeyKeyboardLabel
,
339 l10n_util::GetStringFUTF8(
340 IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST
,
341 base::UTF8ToUTF16(keyboard_device_name_
)));
343 editor
.SetString(kContextKeyKeyboardState
, kSearchingState
);
346 if (keyboard_device_connect_type_
== InputDeviceInfo::TYPE_BLUETOOTH
) {
347 editor
.SetString(kContextKeyKeyboardState
, kBTPairedState
)
349 kContextKeyKeyboardLabel
,
350 l10n_util::GetStringFUTF16(
351 IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD
,
352 base::UTF8ToUTF16(keyboard_device_name_
)));
354 editor
.SetString(kContextKeyKeyboardState
, kUSBConnectedState
);
357 editor
.SetString(kContextKeyKeyboardDeviceName
, keyboard_device_name_
)
359 kContextKeyContinueButtonEnabled
,
360 !(pointing_device_id_
.empty() && keyboard_device_id_
.empty()));
363 void HIDDetectionScreen::SetKeyboardDeviceName_(std::string name
) {
364 if (!(keyboard_device_id_
.empty()) && name
.empty())
365 name
= l10n_util::GetStringUTF8(IDS_HID_DETECTION_DEFAULT_KEYBOARD_NAME
);
366 keyboard_device_name_
= name
;
369 void HIDDetectionScreen::DeviceAdded(
370 device::BluetoothAdapter
* adapter
, device::BluetoothDevice
* device
) {
371 VLOG(1) << "BT input device added id = " << device
->GetDeviceID() <<
372 " name = " << device
->GetName();
373 TryPairingAsPointingDevice(device
);
374 TryPairingAsKeyboardDevice(device
);
377 void HIDDetectionScreen::DeviceChanged(
378 device::BluetoothAdapter
* adapter
, device::BluetoothDevice
* device
) {
379 VLOG(1) << "BT device changed id = " << device
->GetDeviceID() << " name = " <<
381 TryPairingAsPointingDevice(device
);
382 TryPairingAsKeyboardDevice(device
);
385 void HIDDetectionScreen::DeviceRemoved(
386 device::BluetoothAdapter
* adapter
, device::BluetoothDevice
* device
) {
387 VLOG(1) << "BT device removed id = " << device
->GetDeviceID() << " name = " <<
391 void HIDDetectionScreen::OnInputDeviceAdded(
392 const InputDeviceInfo
& info
) {
393 VLOG(1) << "Input device added id = " << info
.id
<< " name = " << info
.name
;
394 // TODO(merkulova): deal with all available device types, e.g. joystick.
395 if (!keyboard_device_id_
.empty() && !pointing_device_id_
.empty())
398 if (pointing_device_id_
.empty() && DeviceIsPointing(info
)) {
399 pointing_device_id_
= info
.id
;
400 GetContextEditor().SetString(kContextKeyMouseDeviceName
, info
.name
);
401 pointing_device_connect_type_
= info
.type
;
402 SendPointingDeviceNotification();
404 if (keyboard_device_id_
.empty() && info
.is_keyboard
) {
405 keyboard_device_id_
= info
.id
;
406 keyboard_device_connect_type_
= info
.type
;
407 SetKeyboardDeviceName_(info
.name
);
408 SendKeyboardDeviceNotification();
412 void HIDDetectionScreen::OnInputDeviceRemoved(const std::string
& id
) {
413 if (id
== keyboard_device_id_
) {
414 keyboard_device_id_
.clear();
415 keyboard_device_connect_type_
= InputDeviceInfo::TYPE_UNKNOWN
;
416 SendKeyboardDeviceNotification();
419 if (id
== pointing_device_id_
) {
420 pointing_device_id_
.clear();
421 pointing_device_connect_type_
= InputDeviceInfo::TYPE_UNKNOWN
;
422 SendPointingDeviceNotification();
427 void HIDDetectionScreen::InitializeAdapter(
428 scoped_refptr
<device::BluetoothAdapter
> adapter
) {
430 CHECK(adapter_
.get());
432 adapter_
->AddObserver(this);
436 void HIDDetectionScreen::StartBTDiscoverySession() {
437 adapter_
->StartDiscoverySession(
438 base::Bind(&HIDDetectionScreen::OnStartDiscoverySession
,
439 weak_ptr_factory_
.GetWeakPtr()),
440 base::Bind(&HIDDetectionScreen::FindDevicesError
,
441 weak_ptr_factory_
.GetWeakPtr()));
444 void HIDDetectionScreen::ProcessConnectedDevicesList(
445 const std::vector
<InputDeviceInfo
>& devices
) {
446 for (std::vector
<InputDeviceInfo
>::const_iterator it
= devices
.begin();
447 it
!= devices
.end() &&
448 (pointing_device_id_
.empty() || keyboard_device_id_
.empty());
450 if (pointing_device_id_
.empty() && DeviceIsPointing(*it
)) {
451 pointing_device_id_
= it
->id
;
452 GetContextEditor().SetString(kContextKeyMouseDeviceName
, it
->name
);
453 pointing_device_connect_type_
= it
->type
;
454 SendPointingDeviceNotification();
456 if (keyboard_device_id_
.empty() && it
->is_keyboard
) {
457 keyboard_device_id_
= it
->id
;
458 SetKeyboardDeviceName_(it
->name
);
459 keyboard_device_connect_type_
= it
->type
;
460 SendKeyboardDeviceNotification();
465 void HIDDetectionScreen::TryInitiateBTDevicesUpdate() {
466 if ((pointing_device_id_
.empty() || keyboard_device_id_
.empty()) &&
468 if (!adapter_
->IsPresent()) {
469 // Switch on BT adapter later when it's available.
470 switch_on_adapter_when_ready_
= true;
471 } else if (!adapter_
->IsPowered()) {
472 VLOG(1) << "Switching on BT adapter on HID OOBE screen.";
473 adapter_initially_powered_
.reset(new bool(false));
474 adapter_
->SetPowered(
476 base::Bind(&HIDDetectionScreen::StartBTDiscoverySession
,
477 weak_ptr_factory_
.GetWeakPtr()),
478 base::Bind(&HIDDetectionScreen::SetPoweredError
,
479 weak_ptr_factory_
.GetWeakPtr()));
486 void HIDDetectionScreen::OnGetInputDevicesListForCheck(
487 const base::Callback
<void(bool)>& on_check_done
,
488 const std::vector
<InputDeviceInfo
>& devices
) {
489 ProcessConnectedDevicesList(devices
);
491 // Screen is not required if both devices are present.
492 bool all_devices_autodetected
= !pointing_device_id_
.empty() &&
493 !keyboard_device_id_
.empty();
494 UMA_HISTOGRAM_BOOLEAN("HIDDetection.OOBEDialogShown",
495 !all_devices_autodetected
);
497 on_check_done
.Run(!all_devices_autodetected
);
500 void HIDDetectionScreen::OnGetInputDevicesList(
501 const std::vector
<InputDeviceInfo
>& devices
) {
502 ProcessConnectedDevicesList(devices
);
503 TryInitiateBTDevicesUpdate();
506 void HIDDetectionScreen::UpdateDevices() {
507 input_service_proxy_
.GetDevices(
508 base::Bind(&HIDDetectionScreen::OnGetInputDevicesList
,
509 weak_ptr_factory_
.GetWeakPtr()));
512 void HIDDetectionScreen::UpdateBTDevices() {
513 if (!adapter_
.get() || !adapter_
->IsPresent() || !adapter_
->IsPowered())
516 // If no connected devices found as pointing device and keyboard, we try to
517 // connect some type-suitable active bluetooth device.
518 std::vector
<device::BluetoothDevice
*> bt_devices
= adapter_
->GetDevices();
519 for (std::vector
<device::BluetoothDevice
*>::const_iterator it
=
521 it
!= bt_devices
.end() &&
522 (keyboard_device_id_
.empty() || pointing_device_id_
.empty());
524 TryPairingAsPointingDevice(*it
);
525 TryPairingAsKeyboardDevice(*it
);
529 void HIDDetectionScreen::OnStartDiscoverySession(
530 scoped_ptr
<device::BluetoothDiscoverySession
> discovery_session
) {
531 VLOG(1) << "BT Discovery session started";
532 discovery_session_
= discovery_session
.Pass();
536 void HIDDetectionScreen::SetPoweredError() {
537 LOG(ERROR
) << "Failed to power BT adapter";
540 void HIDDetectionScreen::SetPoweredOffError() {
541 LOG(ERROR
) << "Failed to power off BT adapter";
544 void HIDDetectionScreen::FindDevicesError() {
545 VLOG(1) << "Failed to start Bluetooth discovery.";
549 } // namespace chromeos