Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / signin / easy_unlock_screenlock_state_handler.cc
blobe551c527153ac1c2bb6082519578f7b9d2cb924d
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/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"
16 #endif
18 using proximity_auth::ScreenlockState;
20 namespace {
22 proximity_auth::ScreenlockBridge::UserPodCustomIcon GetIconForState(
23 ScreenlockState state) {
24 switch (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;
48 NOTREACHED();
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) {
57 switch (state) {
58 case ScreenlockState::INACTIVE:
59 case ScreenlockState::BLUETOOTH_CONNECTING:
60 return 0;
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:
78 return
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;
84 NOTREACHED();
85 return 0;
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);
103 } // namespace
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)
140 return;
142 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())
147 return;
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) {
152 return;
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) {
160 ShowHardlockUI();
161 return;
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_);
171 return;
174 proximity_auth::ScreenlockBridge::UserPodCustomIconOptions icon_options;
175 icon_options.SetIcon(icon);
177 // Don't hardlock on trial run.
178 if (is_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_,
192 icon_options);
195 void EasyUnlockScreenlockStateHandler::SetHardlockState(
196 HardlockState new_state) {
197 if (hardlock_state_ == new_state)
198 return;
200 if (new_state == LOGIN_FAILED && hardlock_state_ != NO_HARDLOCK)
201 return;
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)
216 ShowHardlockUI();
219 void EasyUnlockScreenlockStateHandler::SetTrialRun() {
220 if (is_trial_run_)
221 return;
222 is_trial_run_ = true;
223 RefreshScreenlockState();
224 RecordEasyUnlockTrialRunEvent(EASY_UNLOCK_TRIAL_RUN_EVENT_LAUNCHED);
227 void EasyUnlockScreenlockStateHandler::RecordClickOnLockIcon() {
228 if (!is_trial_run_)
229 return;
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())
268 return;
270 // Do not override online signin.
271 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
272 existing_auth_type =
273 screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
274 if (existing_auth_type ==
275 proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN)
276 return;
278 if (existing_auth_type !=
279 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
280 screenlock_bridge_->lock_handler()->SetAuthType(
281 user_email_,
282 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
283 base::string16());
286 if (hardlock_state_ == NO_PAIRING) {
287 screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
288 hardlock_ui_shown_ = false;
289 return;
292 if (hardlock_ui_shown_)
293 return;
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);
303 } else {
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,
319 device_name);
320 } else if (hardlock_state_ == LOGIN_FAILED) {
321 tooltip = l10n_util::GetStringUTF16(
322 IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE);
323 } else {
324 LOG(ERROR) << "Unknown hardlock state " << hardlock_state_;
326 icon_options.SetTooltip(tooltip, true /* autoshow */);
328 screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
329 icon_options);
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;
339 } else {
340 resource_id = GetTooltipResourceId(state_);
341 if (TooltipContainsDeviceType(state_))
342 device_name = GetDeviceName();
345 if (!resource_id)
346 return;
348 base::string16 tooltip;
349 if (device_name.empty()) {
350 tooltip = l10n_util::GetStringUTF16(resource_id);
351 } else {
352 tooltip = l10n_util::GetStringFUTF16(resource_id, device_name);
355 if (tooltip.empty())
356 return;
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();
366 #else
367 // TODO(tbarzic): Figure out the name for non Chrome OS case.
368 return base::ASCIIToUTF16("Chrome");
369 #endif
372 void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
373 if (!is_trial_run_ && hardlock_state_ != NO_HARDLOCK)
374 return;
376 // Do not override online signin.
377 const proximity_auth::ScreenlockBridge::LockHandler::AuthType
378 existing_auth_type =
379 screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
380 DCHECK_NE(proximity_auth::ScreenlockBridge::LockHandler::ONLINE_SIGN_IN,
381 existing_auth_type);
383 if (state_ == ScreenlockState::AUTHENTICATED) {
384 if (existing_auth_type !=
385 proximity_auth::ScreenlockBridge::LockHandler::USER_CLICK) {
386 screenlock_bridge_->lock_handler()->SetAuthType(
387 user_email_,
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(
395 user_email_,
396 proximity_auth::ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
397 base::string16());