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"
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
;
19 proximity_auth::ScreenlockBridge::UserPodCustomIcon
GetIconForState(
20 ScreenlockState 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
;
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
) {
55 case ScreenlockState::INACTIVE
:
56 case ScreenlockState::BLUETOOTH_CONNECTING
:
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
:
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
;
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
);
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
)
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())
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
) {
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
) {
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_
);
171 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options
;
172 icon_options
.SetIcon(icon
);
174 // Don't hardlock on 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_
,
192 void EasyUnlockScreenlockStateHandler::SetHardlockState(
193 HardlockState new_state
) {
194 if (hardlock_state_
== new_state
)
197 if (new_state
== LOGIN_FAILED
&& hardlock_state_
!= NO_HARDLOCK
)
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
)
216 void EasyUnlockScreenlockStateHandler::SetTrialRun() {
219 is_trial_run_
= true;
220 RefreshScreenlockState();
221 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED
);
224 void EasyUnlockScreenlockStateHandler::RecordClickOnLockIcon() {
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())
267 // Do not override online signin.
268 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
270 screenlock_bridge_
->lock_handler()->GetAuthType(user_email_
);
271 if (existing_auth_type
==
272 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
)
275 if (existing_auth_type
!=
276 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
) {
277 screenlock_bridge_
->lock_handler()->SetAuthType(
279 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
,
283 if (hardlock_state_
== NO_PAIRING
) {
284 screenlock_bridge_
->lock_handler()->HideUserPodCustomIcon(user_email_
);
285 hardlock_ui_shown_
= false;
289 if (hardlock_ui_shown_
)
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
);
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
,
317 } else if (hardlock_state_
== LOGIN_FAILED
) {
318 tooltip
= l10n_util::GetStringUTF16(
319 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE
);
321 LOG(ERROR
) << "Unknown hardlock state " << hardlock_state_
;
323 icon_options
.SetTooltip(tooltip
, true /* autoshow */);
325 screenlock_bridge_
->lock_handler()->ShowUserPodCustomIcon(user_email_
,
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
;
337 resource_id
= GetTooltipResourceId(state_
);
338 if (TooltipContainsDeviceType(state_
))
339 device_name
= GetDeviceName();
345 base::string16 tooltip
;
346 if (device_name
.empty()) {
347 tooltip
= l10n_util::GetStringUTF16(resource_id
);
349 tooltip
= l10n_util::GetStringFUTF16(resource_id
, device_name
);
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();
364 // TODO(tbarzic): Figure out the name for non Chrome OS case.
365 return base::ASCIIToUTF16("Chrome");
369 void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
370 if (!is_trial_run_
&& hardlock_state_
!= NO_HARDLOCK
)
373 // Do not override online signin.
374 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
376 screenlock_bridge_
->lock_handler()->GetAuthType(user_email_
);
377 DCHECK_NE(proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
,
380 if (state_
== ScreenlockState::AUTHENTICATED
) {
381 if (existing_auth_type
!=
382 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK
) {
383 screenlock_bridge_
->lock_handler()->SetAuthType(
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(
393 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
,