Upstream version 7.36.149.0
[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/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"
18
19 namespace {
20
21 const char kJsScreenPath[] = "login.HIDDetectionScreen";
22
23 // Variants of pairing state.
24 const char kRemotePinCode[] = "bluetoothRemotePinCode";
25 const char kRemotePasskey[] = "bluetoothRemotePasskey";
26
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";
34
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";
39
40 // Standard length of pincode for pairing BT keyboards.
41 const int kPincodeLength = 6;
42
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;
47 }
48
49 bool DeviceIsPointing(const device::InputServiceLinux::InputDeviceInfo& info) {
50   return info.is_mouse || info.is_touchpad || info.is_touchscreen ||
51          info.is_tablet;
52 }
53
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;
57 }
58
59 }  // namespace
60
61 namespace chromeos {
62
63 HIDDetectionScreenHandler::HIDDetectionScreenHandler()
64     : BaseScreenHandler(kJsScreenPath),
65       delegate_(NULL),
66       show_on_init_(false),
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) {
72 }
73
74 HIDDetectionScreenHandler::~HIDDetectionScreenHandler() {
75   if (adapter_.get())
76     adapter_->RemoveObserver(this);
77   input_service_proxy_.RemoveObserver(this);
78   if (delegate_)
79     delegate_->OnActorDestroyed(this);
80 }
81
82 void HIDDetectionScreenHandler::OnStartDiscoverySession(
83     scoped_ptr<device::BluetoothDiscoverySession> discovery_session) {
84   VLOG(1) << "BT Discovery session started";
85   discovery_session_ = discovery_session.Pass();
86   UpdateDevices();
87 }
88
89 void HIDDetectionScreenHandler::SetPoweredError() {
90   LOG(ERROR) << "Failed to power BT adapter";
91 }
92
93 void HIDDetectionScreenHandler::FindDevicesError() {
94   VLOG(1) << "Failed to start Bluetooth discovery.";
95 }
96
97 void HIDDetectionScreenHandler::Show() {
98   if (!page_is_ready()) {
99     show_on_init_ = true;
100     return;
101   }
102   input_service_proxy_.AddObserver(this);
103   skip_screen_if_devices_present_ = true;
104   UpdateDevices();
105   ShowScreen(OobeUI::kScreenHIDDetection, NULL);
106 }
107
108 void HIDDetectionScreenHandler::Hide() {
109   if (adapter_.get())
110     adapter_->RemoveObserver(this);
111   input_service_proxy_.RemoveObserver(this);
112 }
113
114 void HIDDetectionScreenHandler::SetDelegate(Delegate* delegate) {
115   delegate_ = delegate;
116   if (page_is_ready())
117     Initialize();
118 }
119
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);
136 }
137
138 void HIDDetectionScreenHandler::Initialize() {
139   if (!page_is_ready() || !delegate_)
140     return;
141
142   device::BluetoothAdapterFactory::GetAdapter(
143       base::Bind(&HIDDetectionScreenHandler::InitializeAdapter,
144                  weak_ptr_factory_.GetWeakPtr()));
145
146   if (show_on_init_) {
147     Show();
148     show_on_init_ = false;
149   }
150 }
151
152 void HIDDetectionScreenHandler::RegisterMessages() {
153   AddCallback(
154       "HIDDetectionOnContinue", &HIDDetectionScreenHandler::HandleOnContinue);
155 }
156
157 void HIDDetectionScreenHandler::HandleOnContinue() {
158   if (delegate_)
159     delegate_->OnExit();
160 }
161
162 void HIDDetectionScreenHandler::InitializeAdapter(
163     scoped_refptr<device::BluetoothAdapter> adapter) {
164   adapter_ = adapter;
165   CHECK(adapter_.get());
166
167   adapter_->AddObserver(this);
168   UpdateDevices();
169 }
170
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()));
177 }
178
179 void HIDDetectionScreenHandler::RequestPinCode(
180     device::BluetoothDevice* device) {
181   VLOG(1) << "RequestPinCode id = " << device->GetDeviceID()
182           << " name = " << device->GetName();
183   device->CancelPairing();
184 }
185
186 void HIDDetectionScreenHandler::RequestPasskey(
187     device::BluetoothDevice* device) {
188   VLOG(1) << "RequestPassKey id = " << device->GetDeviceID()
189           << " name = " << device->GetName();
190   device->CancelPairing();
191 }
192
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(&params);
203 }
204
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(&params);
218 }
219
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(&params);
227 }
228
229 void HIDDetectionScreenHandler::ConfirmPasskey(
230     device::BluetoothDevice* device, uint32 passkey) {
231   VLOG(1) << "Confirm Passkey";
232   device->CancelPairing();
233 }
234
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();
242 }
243
244 void HIDDetectionScreenHandler::AdapterPresentChanged(
245     device::BluetoothAdapter* adapter, bool present) {
246   if (present && switch_on_adapter_when_ready_) {
247     adapter_->SetPowered(
248         true,
249         base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
250                    weak_ptr_factory_.GetWeakPtr()),
251         base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
252                    weak_ptr_factory_.GetWeakPtr()));
253   }
254 }
255
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);
262   }
263 }
264
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);
271   }
272 }
273
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);
280 }
281
282 void HIDDetectionScreenHandler::DeviceChanged(
283     device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
284   VLOG(1) << "BT device changed id = " << device->GetDeviceID() << " name = " <<
285       device->GetName();
286   TryPairingAsPointingDevice(device);
287   TryPairingAsKeyboardDevice(device);
288 }
289
290 void HIDDetectionScreenHandler::DeviceRemoved(
291     device::BluetoothAdapter* adapter, device::BluetoothDevice* device) {
292   VLOG(1) << "BT device removed id = " << device->GetDeviceID() << " name = " <<
293       device->GetName();
294 }
295
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())
301     return;
302
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();
308   }
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);
314   }
315 }
316
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);
323     UpdateDevices();
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();
329     UpdateDevices();
330   }
331 }
332
333 void HIDDetectionScreenHandler::UpdateDevices() {
334   input_service_proxy_.GetDevices(
335       base::Bind(&HIDDetectionScreenHandler::OnGetInputDevicesList,
336                  base::Unretained(this)));
337 }
338
339 void HIDDetectionScreenHandler::UpdateBTDevices() {
340   if (!adapter_ || !adapter_->IsPresent() || !adapter_->IsPowered())
341     return;
342
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 =
347            bt_devices.begin();
348        it != bt_devices.end() &&
349            (keyboard_device_id_.empty() || pointing_device_id_.empty());
350        ++it) {
351     TryPairingAsPointingDevice(*it);
352     TryPairingAsKeyboardDevice(*it);
353   }
354 }
355
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());
361        ++it) {
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();
367     }
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);
373     }
374   }
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_) {
379     HandleOnContinue();
380   }
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()) &&
384       adapter_) {
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(
390           true,
391           base::Bind(&HIDDetectionScreenHandler::StartBTDiscoverySession,
392                      weak_ptr_factory_.GetWeakPtr()),
393           base::Bind(&HIDDetectionScreenHandler::SetPoweredError,
394                      weak_ptr_factory_.GetWeakPtr()));
395     } else {
396       UpdateBTDevices();
397     }
398   }
399 }
400
401 void HIDDetectionScreenHandler::ConnectBTDevice(
402     device::BluetoothDevice* device) {
403   if (!device->IsPairable() || device->IsPaired())
404     return;
405   device::BluetoothDevice::DeviceType device_type = device->GetDeviceType();
406
407   if (device_type == device::BluetoothDevice::DEVICE_MOUSE ||
408       device_type == device::BluetoothDevice::DEVICE_TABLET) {
409     if (mouse_is_pairing_)
410       return;
411     mouse_is_pairing_ = true;
412   } else if (device_type == device::BluetoothDevice::DEVICE_KEYBOARD) {
413     if (keyboard_is_pairing_)
414       return;
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_)
419       return;
420     mouse_is_pairing_ = true;
421     keyboard_is_pairing_ = true;
422   }
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));
429 }
430
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;
437 }
438
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;
449
450   if (pointing_device_id_.empty() || keyboard_device_id_.empty())
451     UpdateDevices();
452 }
453
454
455 void HIDDetectionScreenHandler::SendPointingDeviceNotification() {
456   std::string state;
457   if (pointing_device_id_.empty())
458     state = kSearchingState;
459   else if (pointing_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH)
460     state = kBTPairedState;
461   else
462     state = kUSBConnectedState;
463   CallJS("setPointingDeviceState", state);
464 }
465
466 void HIDDetectionScreenHandler::SendKeyboardDeviceNotification(
467     base::DictionaryValue* params) {
468   base::DictionaryValue state_info;
469   if (params)
470     state_info.MergeDictionary(params);
471
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);
476   }
477
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(
483           kLabelArgName,
484           l10n_util::GetStringFUTF16(
485               IDS_HID_DETECTION_BLUETOOTH_REMOTE_PIN_CODE_REQUEST,
486               device_name));
487     }
488   } else if (keyboard_device_connect_type_ == InputDeviceInfo::TYPE_BLUETOOTH) {
489     state_info.SetString("state", kBTPairedState);
490     state_info.SetString(
491         kLabelArgName,
492         l10n_util::GetStringFUTF16(
493             IDS_HID_DETECTION_PAIRED_BLUETOOTH_KEYBOARD,
494             base::UTF8ToUTF16(keyboard_device_name_)));
495   } else {
496     state_info.SetString("state", kUSBConnectedState);
497   }
498   CallJS("setKeyboardDeviceState", state_info);
499 }
500
501 }  // namespace chromeos