[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / chrome / browser / signin / easy_unlock_screenlock_state_handler.cc
blob32f953773b325d3991f815e9f7230bd73f98f154
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/signin/easy_unlock_screenlock_state_handler.h"
7 #include "base/bind.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/chromeos/chromeos_utils.h"
11 #include "chrome/browser/signin/easy_unlock_metrics.h"
12 #include "chrome/grit/generated_resources.h"
13 #include "ui/base/l10n/l10n_util.h"
15 using proximity_auth::ScreenlockState;
17 namespace {
19 proximity_auth::ScreenlockBridge::UserPodCustomIcon GetIconForState(
20 ScreenlockState state) {
21 switch (state) {
22 case ScreenlockState::NO_BLUETOOTH:
23 case ScreenlockState::NO_PHONE:
24 case ScreenlockState::PHONE_NOT_AUTHENTICATED:
25 case ScreenlockState::PHONE_LOCKED:
26 case ScreenlockState::PHONE_NOT_LOCKABLE:
27 case ScreenlockState::PHONE_UNSUPPORTED:
28 case ScreenlockState::RSSI_TOO_LOW:
29 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED;
30 case ScreenlockState::TX_POWER_TOO_HIGH:
31 case ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH:
32 // TODO(isherman): This icon is currently identical to the regular locked
33 // icon. Once the reduced proximity range flag is removed, consider
34 // deleting the redundant icon.
35 return proximity_auth::ScreenlockBridge::
36 USER_POD_CUSTOM_ICON_LOCKED_WITH_PROXIMITY_HINT;
37 case ScreenlockState::BLUETOOTH_CONNECTING:
38 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_SPINNER;
39 case ScreenlockState::AUTHENTICATED:
40 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_UNLOCKED;
41 case ScreenlockState::INACTIVE:
42 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE;
45 NOTREACHED();
46 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE;
49 bool HardlockOnClick(ScreenlockState state) {
50 return state != ScreenlockState::INACTIVE;
53 size_t GetTooltipResourceId(ScreenlockState state) {
54 switch (state) {
55 case ScreenlockState::INACTIVE:
56 case ScreenlockState::BLUETOOTH_CONNECTING:
57 return 0;
58 case ScreenlockState::NO_BLUETOOTH:
59 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_BLUETOOTH;
60 case ScreenlockState::NO_PHONE:
61 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_PHONE;
62 case ScreenlockState::PHONE_NOT_AUTHENTICATED:
63 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_AUTHENTICATED;
64 case ScreenlockState::PHONE_LOCKED:
65 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED;
66 case ScreenlockState::PHONE_NOT_LOCKABLE:
67 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_UNLOCKABLE;
68 case ScreenlockState::PHONE_UNSUPPORTED:
69 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_UNSUPPORTED_ANDROID_VERSION;
70 case ScreenlockState::RSSI_TOO_LOW:
71 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_RSSI_TOO_LOW;
72 case ScreenlockState::TX_POWER_TOO_HIGH:
73 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TX_POWER_TOO_HIGH;
74 case ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH:
75 return
76 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED_AND_TX_POWER_TOO_HIGH;
77 case ScreenlockState::AUTHENTICATED:
78 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS;
81 NOTREACHED();
82 return 0;
85 bool TooltipContainsDeviceType(ScreenlockState state) {
86 return (state == ScreenlockState::AUTHENTICATED ||
87 state == ScreenlockState::PHONE_NOT_LOCKABLE ||
88 state == ScreenlockState::NO_BLUETOOTH ||
89 state == ScreenlockState::PHONE_UNSUPPORTED ||
90 state == ScreenlockState::TX_POWER_TOO_HIGH ||
91 state == ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH);
94 // Returns true iff the |state| corresponds to a locked remote device.
95 bool IsLockedState(ScreenlockState state) {
96 return (state == ScreenlockState::PHONE_LOCKED ||
97 state == ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH);
100 } // namespace
102 EasyUnlockScreenlockStateHandler::EasyUnlockScreenlockStateHandler(
103 const std::string& user_email,
104 HardlockState initial_hardlock_state,
105 proximity_auth::ScreenlockBridge* screenlock_bridge)
106 : state_(ScreenlockState::INACTIVE),
107 user_email_(user_email),
108 screenlock_bridge_(screenlock_bridge),
109 hardlock_state_(initial_hardlock_state),
110 hardlock_ui_shown_(false),
111 is_trial_run_(false),
112 did_see_locked_phone_(false) {
113 DCHECK(screenlock_bridge_);
114 screenlock_bridge_->AddObserver(this);
117 EasyUnlockScreenlockStateHandler::~EasyUnlockScreenlockStateHandler() {
118 screenlock_bridge_->RemoveObserver(this);
119 // Make sure the screenlock state set by this gets cleared.
120 ChangeState(ScreenlockState::INACTIVE);
123 bool EasyUnlockScreenlockStateHandler::IsActive() const {
124 return state_ != ScreenlockState::INACTIVE;
127 bool EasyUnlockScreenlockStateHandler::InStateValidOnRemoteAuthFailure() const {
128 // Note that NO_PHONE is not valid in this case because the phone may close
129 // the connection if the auth challenge sent to it is invalid. This case
130 // should be handled as authentication failure.
131 return (state_ == ScreenlockState::NO_BLUETOOTH ||
132 state_ == ScreenlockState::PHONE_LOCKED);
135 void EasyUnlockScreenlockStateHandler::ChangeState(ScreenlockState new_state) {
136 if (state_ == new_state)
137 return;
139 state_ = new_state;
141 // If lock screen is not active or it forces offline password, just cache the
142 // current state. The screenlock state will get refreshed in |ScreenDidLock|.
143 if (!screenlock_bridge_->IsLocked())
144 return;
146 // Do nothing when auth type is online.
147 if (screenlock_bridge_->lock_handler()->GetAuthType(user_email_) ==
148 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN) {
149 return;
152 if (IsLockedState(state_))
153 did_see_locked_phone_ = true;
155 // No hardlock UI for trial run.
156 if (!is_trial_run_ && hardlock_state_ != NO_HARDLOCK) {
157 ShowHardlockUI();
158 return;
161 UpdateScreenlockAuthType();
163 proximity_auth::ScreenlockBridge::UserPodCustomIcon icon =
164 GetIconForState(state_);
166 if (icon == proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE) {
167 screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
168 return;
171 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options;
172 icon_options.SetIcon(icon);
174 // Don't hardlock on trial run.
175 if (is_trial_run_)
176 icon_options.SetTrialRun();
177 else if (HardlockOnClick(state_))
178 icon_options.SetHardlockOnClick();
180 UpdateTooltipOptions(&icon_options);
182 // For states without tooltips, we still need to set an accessibility label.
183 if (state_ == ScreenlockState::BLUETOOTH_CONNECTING) {
184 icon_options.SetAriaLabel(
185 l10n_util::GetStringUTF16(IDS_SMART_LOCK_SPINNER_ACCESSIBILITY_LABEL));
188 screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
189 icon_options);
192 void EasyUnlockScreenlockStateHandler::SetHardlockState(
193 HardlockState new_state) {
194 if (hardlock_state_ == new_state)
195 return;
197 if (new_state == LOGIN_FAILED && hardlock_state_ != NO_HARDLOCK)
198 return;
200 hardlock_state_ = new_state;
202 // If hardlock_state_ was set to NO_HARDLOCK, this means the screen is about
203 // to get unlocked. No need to update it in this case.
204 if (hardlock_state_ != NO_HARDLOCK) {
205 hardlock_ui_shown_ = false;
207 RefreshScreenlockState();
211 void EasyUnlockScreenlockStateHandler::MaybeShowHardlockUI() {
212 if (hardlock_state_ != NO_HARDLOCK)
213 ShowHardlockUI();
216 void EasyUnlockScreenlockStateHandler::SetTrialRun() {
217 if (is_trial_run_)
218 return;
219 is_trial_run_ = true;
220 RefreshScreenlockState();
221 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED);
224 void EasyUnlockScreenlockStateHandler::RecordClickOnLockIcon() {
225 if (!is_trial_run_)
226 return;
227 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_CLICKED_LOCK_ICON);
230 void EasyUnlockScreenlockStateHandler::OnScreenDidLock(
231 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type) {
232 did_see_locked_phone_ = IsLockedState(state_);
233 RefreshScreenlockState();
236 void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock(
237 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type) {
238 if (hardlock_state_ == LOGIN_FAILED)
239 hardlock_state_ = NO_HARDLOCK;
240 hardlock_ui_shown_ = false;
241 is_trial_run_ = false;
243 // Upon a successful unlock event, record whether the user's phone was locked
244 // at any point while the lock screen was up.
245 if (state_ == ScreenlockState::AUTHENTICATED)
246 RecordEasyUnlockDidUserManuallyUnlockPhone(did_see_locked_phone_);
247 did_see_locked_phone_ = false;
250 void EasyUnlockScreenlockStateHandler::OnFocusedUserChanged(
251 const std::string& user_id) {
254 void EasyUnlockScreenlockStateHandler::RefreshScreenlockState() {
255 ScreenlockState last_state = state_;
256 // This should force updating screenlock state.
257 state_ = ScreenlockState::INACTIVE;
258 ChangeState(last_state);
261 void EasyUnlockScreenlockStateHandler::ShowHardlockUI() {
262 DCHECK(hardlock_state_ != NO_HARDLOCK);
264 if (!screenlock_bridge_->IsLocked())
265 return;
267 // Do not override online signin.
268 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
269 existing_auth_type =
270 screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
271 if (existing_auth_type ==
272 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN)
273 return;
275 if (existing_auth_type !=
276 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
277 screenlock_bridge_->lock_handler()->SetAuthType(
278 user_email_,
279 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
280 base::string16());
283 if (hardlock_state_ == NO_PAIRING) {
284 screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
285 hardlock_ui_shown_ = false;
286 return;
289 if (hardlock_ui_shown_)
290 return;
292 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options;
293 if (hardlock_state_ == LOGIN_FAILED) {
294 icon_options.SetIcon(
295 proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED);
296 } else if (hardlock_state_ == PAIRING_CHANGED ||
297 hardlock_state_ == PAIRING_ADDED) {
298 icon_options.SetIcon(proximity_auth::ScreenlockBridge::
299 USER_POD_CUSTOM_ICON_LOCKED_TO_BE_ACTIVATED);
300 } else {
301 icon_options.SetIcon(
302 proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_HARDLOCKED);
305 base::string16 device_name = GetDeviceName();
306 base::string16 tooltip;
307 if (hardlock_state_ == USER_HARDLOCK) {
308 tooltip = l10n_util::GetStringFUTF16(
309 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_USER, device_name);
310 } else if (hardlock_state_ == PAIRING_CHANGED) {
311 tooltip = l10n_util::GetStringUTF16(
312 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_CHANGED);
313 } else if (hardlock_state_ == PAIRING_ADDED) {
314 tooltip = l10n_util::GetStringFUTF16(
315 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED, device_name,
316 device_name);
317 } else if (hardlock_state_ == LOGIN_FAILED) {
318 tooltip = l10n_util::GetStringUTF16(
319 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE);
320 } else {
321 LOG(ERROR) << "Unknown hardlock state " << hardlock_state_;
323 icon_options.SetTooltip(tooltip, true /* autoshow */);
325 screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
326 icon_options);
327 hardlock_ui_shown_ = true;
330 void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
331 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
332 size_t resource_id = 0;
333 base::string16 device_name;
334 if (is_trial_run_ && state_ == ScreenlockState::AUTHENTICATED) {
335 resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_INITIAL_AUTHENTICATED;
336 } else {
337 resource_id = GetTooltipResourceId(state_);
338 if (TooltipContainsDeviceType(state_))
339 device_name = GetDeviceName();
342 if (!resource_id)
343 return;
345 base::string16 tooltip;
346 if (device_name.empty()) {
347 tooltip = l10n_util::GetStringUTF16(resource_id);
348 } else {
349 tooltip = l10n_util::GetStringFUTF16(resource_id, device_name);
352 if (tooltip.empty())
353 return;
355 bool autoshow_tooltip =
356 is_trial_run_ || state_ != ScreenlockState::AUTHENTICATED;
357 icon_options->SetTooltip(tooltip, autoshow_tooltip);
360 base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() {
361 #if defined(OS_CHROMEOS)
362 return chromeos::GetChromeDeviceType();
363 #else
364 // TODO(tbarzic): Figure out the name for non Chrome OS case.
365 return base::ASCIIToUTF16("Chrome");
366 #endif
369 void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
370 if (!is_trial_run_ && hardlock_state_ != NO_HARDLOCK)
371 return;
373 // Do not override online signin.
374 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
375 existing_auth_type =
376 screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
377 DCHECK_NE(proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN,
378 existing_auth_type);
380 if (state_ == ScreenlockState::AUTHENTICATED) {
381 if (existing_auth_type !=
382 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK) {
383 screenlock_bridge_->lock_handler()->SetAuthType(
384 user_email_,
385 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK,
386 l10n_util::GetStringUTF16(
387 IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE));
389 } else if (existing_auth_type !=
390 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
391 screenlock_bridge_->lock_handler()->SetAuthType(
392 user_email_,
393 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
394 base::string16());