93925ae6b3c34294ceaa8233a0243f92fc542311
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / chromeos / login / hid_detection_screen_handler.cc
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/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"
22
23 namespace {
24
25 const char kJsScreenPath[] = "login.HIDDetectionScreen";
26
27 // Variants of pairing state.
28 const char kRemotePinCode[] = "bluetoothRemotePinCode";
29 const char kRemotePasskey[] = "bluetoothRemotePasskey";
30
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";
38
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";
43
44 // Standard length of pincode for pairing BT keyboards.
45 const int kPincodeLength = 6;
46
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;
51 }
52
53 bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo& info) {
54   return info.is_mouse || info.is_touchpad || info.is_touchscreen ||
55          info.is_tablet;
56 }
57
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;
61 }
62
63 }  // namespace
64
65 namespace chromeos {
66
67 HIDDetectionScreenHandler::HIDDetectionScreenHandler(
68     CoreOobeActor* core_oobe_actor)
69     : BaseScreenHandler(kJsScreenPath),
70       delegate_(NULL),
71       core_oobe_actor_(core_oobe_actor),
72       show_on_init_(false),
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) {
78 }
79
80 HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
81   if (adapter_.get())
82     adapter_->RemoveObserver(this);
83   input_service_proxy_.RemoveObserver(this);
84   if (delegate_)
85     delegate_->OnActorDestroyed(this);
86 }
87
88 void HIDDetectionScreenHandler::OnStartDiscoverySession(
89     scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
90   VLOG(1) << "BT Discovery session started";
91   discovery_session_ = discovery_session.Pass();
92   UpdateDevices();
93 }
94
95 void HIDDetectionScreenHandler::SetPoweredError() {
96   LOG(ERROR) << "Failed to power BT adapter";
97 }
98
99 void HIDDetectionScreenHandler::FindDevicesError() {
100   VLOG(1) << "Failed to start Bluetooth discovery.";
101 }
102
103 void HIDDetectionScreenHandler::Show() {
104   if (!page_is_ready()) {
105     show_on_init_ = true;
106     return;
107   }
108   core_oobe_actor_->InitDemoModeDetection();
109   input_service_proxy_.AddObserver(this);
110   first_time_screen_show_ = true;
111   GetDevicesFirstTime();
112   ShowScreen(OobeUI::kScreenHIDDetection, NULL);
113 }
114
115 void HIDDetectionScreenHandler::Hide() {
116   if (adapter_.get())
117     adapter_->RemoveObserver(this);
118   input_service_proxy_.RemoveObserver(this);
119 }
120
121 void HIDDetectionScreenHandler::SetDelegate(Delegate* delegate) {
122   delegate_ = delegate;
123   if (page_is_ready())
124     Initialize();
125 }
126
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);
143 }
144
145 void HIDDetectionScreenHandler::Initialize() {
146   if (!page_is_ready() || !delegate_)
147     return;
148
149   device::BluetoothAdapterFactory::GetAdapter(
150       base::Bind(&HIDDetectionScreenHandler::InitializeAdapter,
151                  weak_ptr_factory_.GetWeakPtr()));
152
153   if (show_on_init_) {
154     Show();
155     show_on_init_ = false;
156   }
157 }
158
159 void HIDDetectionScreenHandler::RegisterMessages() {
160   AddCallback(
161       "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
162 }
163
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;
172     else
173       scenario_type = POINTING_DEVICE_ONLY_DETECTED;
174
175     UMA_HISTOGRAM_ENUMERATION(
176         "HIDDetection.OOBEDevicesDetectedOnContinuePressed",
177         scenario_type,
178         CONTINUE_SCENARIO_TYPE_SIZE);
179   }
180   core_oobe_actor_->StopDemoModeDetection();
181   if (delegate_)
182     delegate_->OnExit();
183 }
184
185 void HIDDetectionScreenHandler::InitializeAdapter(
186     scoped_refptr<device::BluetoothAdapter> adapter) {
187   adapter_ = adapter;
188   CHECK(adapter_.get());
189
190   adapter_->AddObserver(this);
191   UpdateDevices();
192 }
193
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()));
200 }
201
202 void HIDDetectionScreenHandler::RequestPinCode(
203     device::BluetoothDevice* device) {
204   VLOG(1) << "RequestPinCode id = " << device->GetDeviceID()
205           << " name = " << device->GetName();
206   device->CancelPairing();
207 }
208
209 void HIDDetectionScreenHandler::RequestPasskey(
210     device::BluetoothDevice* device) {
211   VLOG(1) << "RequestPassKey id = " << device->GetDeviceID()
212           << " name = " << device->GetName();
213   device->CancelPairing();
214 }
215
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(&params);
226 }
227
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(&params);
241 }
242
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(&params);
250 }
251
252 void HIDDetectionScreenHandler::ConfirmPasskey(
253     device::BluetoothDevice* device, uint32 passkey) {
254   VLOG(1) << "Confirm Passkey";
255   device->CancelPairing();
256 }
257
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();
265 }
266
267 void HIDDetectionScreenHandler::AdapterPresentChanged(
268     device::BluetoothAdapter* adapter, bool present) {
269   if (present && switch_on_adapter_when_ready_) {
270     adapter_->SetPowered(
271         true,
272         base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
273                    weak_ptr_factory_.GetWeakPtr()),
274         base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
275                    weak_ptr_factory_.GetWeakPtr()));
276   }
277 }
278
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);
285   }
286 }
287
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);
294   }
295 }
296
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);
303 }
304
305 void HIDDetectionScreenHandler::DeviceChanged(
306     device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
307   VLOG(1) << "BT device changed id = " << device->GetDeviceID() << " name = " <<
308       device->GetName();
309   TryPairingAsPointingDevice(device);
310   TryPairingAsKeyboardDevice(device);
311 }
312
313 void HIDDetectionScreenHandler::DeviceRemoved(
314     device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
315   VLOG(1) << "BT device removed id = " << device->GetDeviceID() << " name = " <<
316       device->GetName();
317 }
318
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())
324     return;
325
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();
331   }
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);
337   }
338 }
339
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);
346     UpdateDevices();
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();
352     UpdateDevices();
353   }
354 }
355
356 // static
357 void HIDDetectionScreenHandler::RegisterPrefs(PrefRegistrySimple* registry) {
358   registry->RegisterIntegerPref(prefs::kTimesHIDDialogShown, 0);
359 }
360
361 void HIDDetectionScreenHandler::GetDevicesFirstTime() {
362   input_service_proxy_.GetDevices(
363       base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesListFirstTime,
364                  weak_ptr_factory_.GetWeakPtr()));
365 }
366
367 void HIDDetectionScreenHandler::UpdateDevices() {
368   input_service_proxy_.GetDevices(
369       base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesList,
370                  weak_ptr_factory_.GetWeakPtr()));
371 }
372
373 void HIDDetectionScreenHandler::UpdateBTDevices() {
374   if (!adapter_ || !adapter_->IsPresent() || !adapter_->IsPowered())
375     return;
376
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 =
381            bt_devices.begin();
382        it != bt_devices.end() &&
383            (keyboard_device_id_.empty() || pointing_device_id_.empty());
384        ++it) {
385     TryPairingAsPointingDevice(*it);
386     TryPairingAsKeyboardDevice(*it);
387   }
388 }
389
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());
395        ++it) {
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();
401     }
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);
407     }
408   }
409 }
410
411 void HIDDetectionScreenHandler::TryInitiateBTDevicesUpdate() {
412   if ((pointing_device_id_.empty() || keyboard_device_id_.empty()) &&
413       adapter_) {
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(
419           true,
420           base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
421                      weak_ptr_factory_.GetWeakPtr()),
422           base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
423                      weak_ptr_factory_.GetWeakPtr()));
424     } else {
425       UpdateBTDevices();
426     }
427   }
428 }
429
430 void HIDDetectionScreenHandler::OnGetInputDevicesListFirstTime(
431     const std::vector<InputDeviceInfo>& devices) {
432   ProcessConnectedDevicesList(devices);
433
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) {
440     HandleOnContinue();
441     return;
442   }
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;
449
450   TryInitiateBTDevicesUpdate();
451 }
452
453 void HIDDetectionScreenHandler::OnGetInputDevicesList(
454     const std::vector<InputDeviceInfo>& devices) {
455   ProcessConnectedDevicesList(devices);
456   TryInitiateBTDevicesUpdate();
457 }
458
459 void HIDDetectionScreenHandler::ConnectBTDevice(
460     device::BluetoothDevice* device) {
461   if (!device->IsPairable() || device->IsPaired())
462     return;
463   device::BluetoothDevice::DeviceType device_type = device->GetDeviceType();
464
465   if (device_type == device::BluetoothDevice::DEVICE_MOUSE ||
466       device_type == device::BluetoothDevice::DEVICE_TABLET) {
467     if (mouse_is_pairing_)
468       return;
469     mouse_is_pairing_ = true;
470   } else if (device_type == device::BluetoothDevice::DEVICE_KEYBOARD) {
471     if (keyboard_is_pairing_)
472       return;
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_)
477       return;
478     mouse_is_pairing_ = true;
479     keyboard_is_pairing_ = true;
480   }
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));
487 }
488
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;
495 }
496
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);
508   }
509
510   if (pointing_device_id_.empty() || keyboard_device_id_.empty())
511     UpdateDevices();
512 }
513
514
515 void HIDDetectionScreenHandler::SendPointingDeviceNotification() {
516   std::string state;
517   if (pointing_device_id_.empty())
518     state = kSearchingState;
519   else if (pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH)
520     state = kBTPairedState;
521   else
522     state = kUSBConnectedState;
523   CallJS("setPointingDeviceState", state);
524 }
525
526 void HIDDetectionScreenHandler::SendKeyboardDeviceNotification(
527     base::DictionaryValue* params) {
528   base::DictionaryValue state_info;
529   if (params)
530     state_info.MergeDictionary(params);
531
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);
536   }
537
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(
543           kLabelArgName,
544           l10n_util::GetStringFUTF16(
545               IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST,
546               device_name));
547     }
548   } else if (keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) {
549     state_info.SetString("state", kBTPairedState);
550     state_info.SetString(
551         kLabelArgName,
552         l10n_util::GetStringFUTF16(
553             IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD,
554             base::UTF8ToUTF16(keyboard_device_name_)));
555   } else {
556     state_info.SetString("state", kUSBConnectedState);
557   }
558   CallJS("setKeyboardDeviceState", state_info);
559 }
560
561 }  // namespace chromeos