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/signin/easy_unlock_metrics.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "ui/base/l10n/l10n_util.h"
14 #if defined(OS_CHROMEOS)
15 #include "ash/system/chromeos/devicetype_utils.h"
18 using proximity_auth::ScreenlockState
;
22 proximity_auth::ScreenlockBridge::UserPodCustomIcon
GetIconForState(
23 ScreenlockState state
) {
25 case ScreenlockState::NO_BLUETOOTH
:
26 case ScreenlockState::NO_PHONE
:
27 case ScreenlockState::PHONE_NOT_AUTHENTICATED
:
28 case ScreenlockState::PHONE_LOCKED
:
29 case ScreenlockState::PHONE_NOT_LOCKABLE
:
30 case ScreenlockState::PHONE_UNSUPPORTED
:
31 case ScreenlockState::RSSI_TOO_LOW
:
32 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED
;
33 case ScreenlockState::TX_POWER_TOO_HIGH
:
34 case ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH
:
35 // TODO(isherman): This icon is currently identical to the regular locked
36 // icon. Once the reduced proximity range flag is removed, consider
37 // deleting the redundant icon.
38 return proximity_auth::ScreenlockBridge::
39 USER_POD_CUSTOM_ICON_LOCKED_WITH_PROXIMITY_HINT
;
40 case ScreenlockState::BLUETOOTH_CONNECTING
:
41 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_SPINNER
;
42 case ScreenlockState::AUTHENTICATED
:
43 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_UNLOCKED
;
44 case ScreenlockState::INACTIVE
:
45 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE
;
49 return proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE
;
52 bool HardlockOnClick(ScreenlockState state
) {
53 return state
!= ScreenlockState::INACTIVE
;
56 size_t GetTooltipResourceId(ScreenlockState state
) {
58 case ScreenlockState::INACTIVE
:
59 case ScreenlockState::BLUETOOTH_CONNECTING
:
61 case ScreenlockState::NO_BLUETOOTH
:
62 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_BLUETOOTH
;
63 case ScreenlockState::NO_PHONE
:
64 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_PHONE
;
65 case ScreenlockState::PHONE_NOT_AUTHENTICATED
:
66 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_AUTHENTICATED
;
67 case ScreenlockState::PHONE_LOCKED
:
68 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED
;
69 case ScreenlockState::PHONE_NOT_LOCKABLE
:
70 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_UNLOCKABLE
;
71 case ScreenlockState::PHONE_UNSUPPORTED
:
72 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_UNSUPPORTED_ANDROID_VERSION
;
73 case ScreenlockState::RSSI_TOO_LOW
:
74 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_RSSI_TOO_LOW
;
75 case ScreenlockState::TX_POWER_TOO_HIGH
:
76 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TX_POWER_TOO_HIGH
;
77 case ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH
:
79 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED_AND_TX_POWER_TOO_HIGH
;
80 case ScreenlockState::AUTHENTICATED
:
81 return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS
;
88 bool TooltipContainsDeviceType(ScreenlockState state
) {
89 return (state
== ScreenlockState::AUTHENTICATED
||
90 state
== ScreenlockState::PHONE_NOT_LOCKABLE
||
91 state
== ScreenlockState::NO_BLUETOOTH
||
92 state
== ScreenlockState::PHONE_UNSUPPORTED
||
93 state
== ScreenlockState::TX_POWER_TOO_HIGH
||
94 state
== ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH
);
97 // Returns true iff the |state| corresponds to a locked remote device.
98 bool IsLockedState(ScreenlockState state
) {
99 return (state
== ScreenlockState::PHONE_LOCKED
||
100 state
== ScreenlockState::PHONE_LOCKED_AND_TX_POWER_TOO_HIGH
);
105 EasyUnlockScreenlockStateHandler::EasyUnlockScreenlockStateHandler(
106 const std::string
& user_email
,
107 HardlockState initial_hardlock_state
,
108 proximity_auth::ScreenlockBridge
* screenlock_bridge
)
109 : state_(ScreenlockState::INACTIVE
),
110 user_email_(user_email
),
111 screenlock_bridge_(screenlock_bridge
),
112 hardlock_state_(initial_hardlock_state
),
113 hardlock_ui_shown_(false),
114 is_trial_run_(false),
115 did_see_locked_phone_(false) {
116 DCHECK(screenlock_bridge_
);
117 screenlock_bridge_
->AddObserver(this);
120 EasyUnlockScreenlockStateHandler::~EasyUnlockScreenlockStateHandler() {
121 screenlock_bridge_
->RemoveObserver(this);
122 // Make sure the screenlock state set by this gets cleared.
123 ChangeState(ScreenlockState::INACTIVE
);
126 bool EasyUnlockScreenlockStateHandler::IsActive() const {
127 return state_
!= ScreenlockState::INACTIVE
;
130 bool EasyUnlockScreenlockStateHandler::InStateValidOnRemoteAuthFailure() const {
131 // Note that NO_PHONE is not valid in this case because the phone may close
132 // the connection if the auth challenge sent to it is invalid. This case
133 // should be handled as authentication failure.
134 return (state_
== ScreenlockState::NO_BLUETOOTH
||
135 state_
== ScreenlockState::PHONE_LOCKED
);
138 void EasyUnlockScreenlockStateHandler::ChangeState(ScreenlockState new_state
) {
139 if (state_
== new_state
)
144 // If lock screen is not active or it forces offline password, just cache the
145 // current state. The screenlock state will get refreshed in |ScreenDidLock|.
146 if (!screenlock_bridge_
->IsLocked())
149 // Do nothing when auth type is online.
150 if (screenlock_bridge_
->lock_handler()->GetAuthType(user_email_
) ==
151 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
) {
155 if (IsLockedState(state_
))
156 did_see_locked_phone_
= true;
158 // No hardlock UI for trial run.
159 if (!is_trial_run_
&& hardlock_state_
!= NO_HARDLOCK
) {
164 UpdateScreenlockAuthType();
166 proximity_auth::ScreenlockBridge::UserPodCustomIcon icon
=
167 GetIconForState(state_
);
169 if (icon
== proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE
) {
170 screenlock_bridge_
->lock_handler()->HideUserPodCustomIcon(user_email_
);
174 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options
;
175 icon_options
.SetIcon(icon
);
177 // Don't hardlock on trial run.
179 icon_options
.SetTrialRun();
180 else if (HardlockOnClick(state_
))
181 icon_options
.SetHardlockOnClick();
183 UpdateTooltipOptions(&icon_options
);
185 // For states without tooltips, we still need to set an accessibility label.
186 if (state_
== ScreenlockState::BLUETOOTH_CONNECTING
) {
187 icon_options
.SetAriaLabel(
188 l10n_util::GetStringUTF16(IDS_SMART_LOCK_SPINNER_ACCESSIBILITY_LABEL
));
191 screenlock_bridge_
->lock_handler()->ShowUserPodCustomIcon(user_email_
,
195 void EasyUnlockScreenlockStateHandler::SetHardlockState(
196 HardlockState new_state
) {
197 if (hardlock_state_
== new_state
)
200 if (new_state
== LOGIN_FAILED
&& hardlock_state_
!= NO_HARDLOCK
)
203 hardlock_state_
= new_state
;
205 // If hardlock_state_ was set to NO_HARDLOCK, this means the screen is about
206 // to get unlocked. No need to update it in this case.
207 if (hardlock_state_
!= NO_HARDLOCK
) {
208 hardlock_ui_shown_
= false;
210 RefreshScreenlockState();
214 void EasyUnlockScreenlockStateHandler::MaybeShowHardlockUI() {
215 if (hardlock_state_
!= NO_HARDLOCK
)
219 void EasyUnlockScreenlockStateHandler::SetTrialRun() {
222 is_trial_run_
= true;
223 RefreshScreenlockState();
224 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED
);
227 void EasyUnlockScreenlockStateHandler::RecordClickOnLockIcon() {
230 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_CLICKED_LOCK_ICON
);
233 void EasyUnlockScreenlockStateHandler::OnScreenDidLock(
234 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type
) {
235 did_see_locked_phone_
= IsLockedState(state_
);
236 RefreshScreenlockState();
239 void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock(
240 proximity_auth::ScreenlockBridge::LockHandler::ScreenType screen_type
) {
241 if (hardlock_state_
== LOGIN_FAILED
)
242 hardlock_state_
= NO_HARDLOCK
;
243 hardlock_ui_shown_
= false;
244 is_trial_run_
= false;
246 // Upon a successful unlock event, record whether the user's phone was locked
247 // at any point while the lock screen was up.
248 if (state_
== ScreenlockState::AUTHENTICATED
)
249 RecordEasyUnlockDidUserManuallyUnlockPhone(did_see_locked_phone_
);
250 did_see_locked_phone_
= false;
253 void EasyUnlockScreenlockStateHandler::OnFocusedUserChanged(
254 const std::string
& user_id
) {
257 void EasyUnlockScreenlockStateHandler::RefreshScreenlockState() {
258 ScreenlockState last_state
= state_
;
259 // This should force updating screenlock state.
260 state_
= ScreenlockState::INACTIVE
;
261 ChangeState(last_state
);
264 void EasyUnlockScreenlockStateHandler::ShowHardlockUI() {
265 DCHECK(hardlock_state_
!= NO_HARDLOCK
);
267 if (!screenlock_bridge_
->IsLocked())
270 // Do not override online signin.
271 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
273 screenlock_bridge_
->lock_handler()->GetAuthType(user_email_
);
274 if (existing_auth_type
==
275 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
)
278 if (existing_auth_type
!=
279 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
) {
280 screenlock_bridge_
->lock_handler()->SetAuthType(
282 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
,
286 if (hardlock_state_
== NO_PAIRING
) {
287 screenlock_bridge_
->lock_handler()->HideUserPodCustomIcon(user_email_
);
288 hardlock_ui_shown_
= false;
292 if (hardlock_ui_shown_
)
295 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options
;
296 if (hardlock_state_
== LOGIN_FAILED
) {
297 icon_options
.SetIcon(
298 proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED
);
299 } else if (hardlock_state_
== PAIRING_CHANGED
||
300 hardlock_state_
== PAIRING_ADDED
) {
301 icon_options
.SetIcon(proximity_auth::ScreenlockBridge::
302 USER_POD_CUSTOM_ICON_LOCKED_TO_BE_ACTIVATED
);
304 icon_options
.SetIcon(
305 proximity_auth::ScreenlockBridge::USER_POD_CUSTOM_ICON_HARDLOCKED
);
308 base::string16 device_name
= GetDeviceName();
309 base::string16 tooltip
;
310 if (hardlock_state_
== USER_HARDLOCK
) {
311 tooltip
= l10n_util::GetStringFUTF16(
312 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_USER
, device_name
);
313 } else if (hardlock_state_
== PAIRING_CHANGED
) {
314 tooltip
= l10n_util::GetStringUTF16(
315 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_CHANGED
);
316 } else if (hardlock_state_
== PAIRING_ADDED
) {
317 tooltip
= l10n_util::GetStringFUTF16(
318 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED
, device_name
,
320 } else if (hardlock_state_
== LOGIN_FAILED
) {
321 tooltip
= l10n_util::GetStringUTF16(
322 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE
);
324 LOG(ERROR
) << "Unknown hardlock state " << hardlock_state_
;
326 icon_options
.SetTooltip(tooltip
, true /* autoshow */);
328 screenlock_bridge_
->lock_handler()->ShowUserPodCustomIcon(user_email_
,
330 hardlock_ui_shown_
= true;
333 void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
334 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions
* icon_options
) {
335 size_t resource_id
= 0;
336 base::string16 device_name
;
337 if (is_trial_run_
&& state_
== ScreenlockState::AUTHENTICATED
) {
338 resource_id
= IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_INITIAL_AUTHENTICATED
;
340 resource_id
= GetTooltipResourceId(state_
);
341 if (TooltipContainsDeviceType(state_
))
342 device_name
= GetDeviceName();
348 base::string16 tooltip
;
349 if (device_name
.empty()) {
350 tooltip
= l10n_util::GetStringUTF16(resource_id
);
352 tooltip
= l10n_util::GetStringFUTF16(resource_id
, device_name
);
358 bool autoshow_tooltip
=
359 is_trial_run_
|| state_
!= ScreenlockState::AUTHENTICATED
;
360 icon_options
->SetTooltip(tooltip
, autoshow_tooltip
);
363 base::string16
EasyUnlockScreenlockStateHandler::GetDeviceName() {
364 #if defined(OS_CHROMEOS)
365 return ash::GetChromeOSDeviceName();
367 // TODO(tbarzic): Figure out the name for non Chrome OS case.
368 return base::ASCIIToUTF16("Chrome");
372 void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
373 if (!is_trial_run_
&& hardlock_state_
!= NO_HARDLOCK
)
376 // Do not override online signin.
377 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
379 screenlock_bridge_
->lock_handler()->GetAuthType(user_email_
);
380 DCHECK_NE(proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN
,
383 if (state_
== ScreenlockState::AUTHENTICATED
) {
384 if (existing_auth_type
!=
385 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK
) {
386 screenlock_bridge_
->lock_handler()->SetAuthType(
388 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK
,
389 l10n_util::GetStringUTF16(
390 IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE
));
392 } else if (existing_auth_type
!=
393 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
) {
394 screenlock_bridge_
->lock_handler()->SetAuthType(
396 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD
,