1 // Copyright 2017 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 "ash/login/ui/lock_debug_view.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <memory>
10 #include <utility>
11
12 #include "ash/detachable_base/detachable_base_pairing_status.h"
13 #include "ash/ime/ime_controller_impl.h"
14 #include "ash/login/login_screen_controller.h"
15 #include "ash/login/ui/lock_contents_view.h"
16 #include "ash/login/ui/lock_screen.h"
17 #include "ash/login/ui/login_data_dispatcher.h"
18 #include "ash/login/ui/login_detachable_base_model.h"
19 #include "ash/login/ui/non_accessible_view.h"
20 #include "ash/login/ui/views_utils.h"
21 #include "ash/public/cpp/kiosk_app_menu.h"
22 #include "ash/public/cpp/login_types.h"
23 #include "ash/shelf/login_shelf_view.h"
24 #include "ash/shelf/shelf.h"
25 #include "ash/shelf/shelf_widget.h"
26 #include "ash/shell.h"
27 #include "ash/style/ash_color_provider.h"
28 #include "ash/wallpaper/wallpaper_controller_impl.h"
29 #include "base/bind.h"
30 #include "base/memory/ptr_util.h"
31 #include "base/optional.h"
32 #include "base/stl_util.h"
33 #include "base/strings/string_number_conversions.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "ui/base/ime/chromeos/ime_keyboard.h"
36 #include "ui/views/controls/button/md_text_button.h"
37 #include "ui/views/controls/scroll_view.h"
38 #include "ui/views/controls/scrollbar/overlay_scroll_bar.h"
39 #include "ui/views/layout/box_layout.h"
40 #include "ui/views/view.h"
41
42 namespace ash {
43 namespace {
44
45 constexpr const char* kDebugUserNames[] = {
46 "Angelina Johnson", "Marcus Cohen", "Chris Wallace",
47 "Debbie Craig", "Stella Wong", "Stephanie Wade",
48 };
49
50 constexpr const char* kDebugPublicAccountNames[] = {
51 "Seattle Public Library",
52 "San Jose Public Library",
53 "Sunnyvale Public Library",
54 "Mountain View Public Library",
55 };
56
57 constexpr const char* kDebugDetachableBases[] = {"Base A", "Base B", "Base C"};
58
59 constexpr const char kDebugOsVersion[] =
60 "Chromium 64.0.3279.0 (Platform 10146.0.0 dev-channel peppy test)";
61 constexpr const char kDebugEnterpriseInfo[] = "Asset ID: 1111";
62 constexpr const char kDebugBluetoothName[] = "Bluetooth adapter";
63
64 constexpr const char kDebugKioskAppId[] = "asdf1234";
65 constexpr const char kDebugKioskAppName[] = "Test App Name";
66
67 constexpr const char kDebugDefaultLocaleCode[] = "en-GB";
68 constexpr const char kDebugDefaultLocaleTitle[] = "English";
69 constexpr const char kDebugEnterpriseDomain[] = "library.com";
70
71 // Additional state for a user that the debug UI needs to reference.
72 struct UserMetadata {
UserMetadataash::__anonea65d5820111::UserMetadata73 explicit UserMetadata(const UserInfo& user_info)
74 : account_id(user_info.account_id),
75 display_name(user_info.display_name),
76 type(user_info.type) {}
77
78 AccountId account_id;
79 std::string display_name;
80 bool enable_pin = false;
81 bool enable_tap_to_unlock = false;
82 bool enable_challenge_response = false; // Smart Card
83 bool enable_auth = true;
84 user_manager::UserType type = user_manager::USER_TYPE_REGULAR;
85 EasyUnlockIconId easy_unlock_id = EasyUnlockIconId::NONE;
86 FingerprintState fingerprint_state = FingerprintState::UNAVAILABLE;
87 };
88
DetachableBasePairingStatusToString(DetachableBasePairingStatus pairing_status)89 std::string DetachableBasePairingStatusToString(
90 DetachableBasePairingStatus pairing_status) {
91 switch (pairing_status) {
92 case DetachableBasePairingStatus::kNone:
93 return "No device";
94 case DetachableBasePairingStatus::kAuthenticated:
95 return "Authenticated";
96 case DetachableBasePairingStatus::kNotAuthenticated:
97 return "Not authenticated";
98 case DetachableBasePairingStatus::kInvalidDevice:
99 return "Invalid device";
100 }
101 return "Unknown";
102 }
103
104 // Update the user data based on |type| and |user_index|.
PopulateUserData(const LoginUserInfo & user,user_manager::UserType type,int user_index)105 LoginUserInfo PopulateUserData(const LoginUserInfo& user,
106 user_manager::UserType type,
107 int user_index) {
108 LoginUserInfo result = user;
109 result.basic_user_info.type = type;
110
111 bool is_public_account = type == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
112 // Set debug user names and email. Useful for the stub user, which does not
113 // have a name and email set.
114 result.basic_user_info.display_name =
115 is_public_account
116 ? kDebugPublicAccountNames[user_index %
117 base::size(kDebugPublicAccountNames)]
118 : kDebugUserNames[user_index % base::size(kDebugUserNames)];
119 result.basic_user_info.display_email =
120 result.basic_user_info.account_id.GetUserEmail();
121
122 if (is_public_account) {
123 result.public_account_info.emplace();
124 result.public_account_info->device_enterprise_manager =
125 kDebugEnterpriseDomain;
126 result.public_account_info->default_locale = kDebugDefaultLocaleCode;
127 result.public_account_info->show_expanded_view = true;
128
129 std::vector<LocaleItem> locales;
130 LocaleItem locale_item;
131 locale_item.language_code = kDebugDefaultLocaleCode;
132 locale_item.title = kDebugDefaultLocaleTitle;
133 result.public_account_info->available_locales.push_back(
134 std::move(locale_item));
135
136 // Request keyboard layouts for the default locale.
137 Shell::Get()
138 ->login_screen_controller()
139 ->RequestPublicSessionKeyboardLayouts(result.basic_user_info.account_id,
140 kDebugDefaultLocaleCode);
141 } else {
142 result.public_account_info.reset();
143 }
144
145 return result;
146 }
147
148 } // namespace
149
150 // Applies a series of user-defined transformations to a
151 // |LoginDataDispatcher| instance; this is used for debugging and
152 // development. The debug overlay uses this class to change what data is exposed
153 // to the UI.
154 class LockDebugView::DebugDataDispatcherTransformer
155 : public LoginDataDispatcher::Observer {
156 public:
DebugDataDispatcherTransformer(mojom::TrayActionState initial_lock_screen_note_state,LoginDataDispatcher * dispatcher,const base::RepeatingClosure & on_users_received,LockDebugView * lock_debug_view)157 DebugDataDispatcherTransformer(
158 mojom::TrayActionState initial_lock_screen_note_state,
159 LoginDataDispatcher* dispatcher,
160 const base::RepeatingClosure& on_users_received,
161 LockDebugView* lock_debug_view)
162 : root_dispatcher_(dispatcher),
163 lock_screen_note_state_(initial_lock_screen_note_state),
164 on_users_received_(on_users_received),
165 lock_debug_view_(lock_debug_view) {
166 root_dispatcher_->AddObserver(this);
167 }
~DebugDataDispatcherTransformer()168 ~DebugDataDispatcherTransformer() override {
169 root_dispatcher_->RemoveObserver(this);
170 }
171
debug_dispatcher()172 LoginDataDispatcher* debug_dispatcher() { return &debug_dispatcher_; }
173
174 // Changes the number of displayed users to |count|.
SetUserCount(int count)175 void SetUserCount(int count) { NotifyUsers(BuildUserList(count)); }
176
177 // Create user list.
BuildUserList(int count)178 std::vector<LoginUserInfo> BuildUserList(int count) {
179 DCHECK(!root_users_.empty());
180
181 count = std::max(count, 0);
182
183 // Trim any extra debug users.
184 if (debug_users_.size() > size_t{count})
185 debug_users_.erase(debug_users_.begin() + count, debug_users_.end());
186
187 // Build |users|, add any new users to |debug_users|.
188 std::vector<LoginUserInfo> users;
189 for (size_t i = 0; i < size_t{count}; ++i) {
190 users.push_back(root_users_[i % root_users_.size()]);
191 if (i >= root_users_.size()) {
192 users[i].basic_user_info.account_id = AccountId::FromUserEmailGaiaId(
193 users[i].basic_user_info.account_id.GetUserEmail() +
194 base::NumberToString(i),
195 users[i].basic_user_info.account_id.GetGaiaId() +
196 base::NumberToString(i));
197 }
198
199 // Setup user data based on the user type in debug_users_.
200 user_manager::UserType type = (i < debug_users_.size())
201 ? debug_users_[i].type
202 : users[i].basic_user_info.type;
203 users[i] = PopulateUserData(users[i], type, i);
204
205 if (i >= debug_users_.size())
206 debug_users_.push_back(UserMetadata(users[i].basic_user_info));
207 }
208
209 return users;
210 }
211
NotifyUsers(const std::vector<LoginUserInfo> & users)212 void NotifyUsers(const std::vector<LoginUserInfo>& users) {
213 // User notification resets PIN state.
214 for (UserMetadata& user : debug_users_)
215 user.enable_pin = false;
216
217 debug_dispatcher_.SetUserList(users);
218 }
219
GetUserCount() const220 int GetUserCount() const { return debug_users_.size(); }
221
GetDisplayNameForUserIndex(size_t user_index)222 base::string16 GetDisplayNameForUserIndex(size_t user_index) {
223 DCHECK(user_index >= 0 && user_index < debug_users_.size());
224 return base::UTF8ToUTF16(debug_users_[user_index].display_name);
225 }
226
GetAccountIdForUserIndex(size_t user_index)227 const AccountId& GetAccountIdForUserIndex(size_t user_index) {
228 DCHECK(user_index >= 0 && user_index < debug_users_.size());
229 UserMetadata* debug_user = &debug_users_[user_index];
230 return debug_user->account_id;
231 }
232
233 // Activates or deactivates PIN for the user at |user_index|.
TogglePinStateForUserIndex(size_t user_index)234 void TogglePinStateForUserIndex(size_t user_index) {
235 DCHECK(user_index >= 0 && user_index < debug_users_.size());
236 UserMetadata* debug_user = &debug_users_[user_index];
237 debug_user->enable_pin = !debug_user->enable_pin;
238 debug_dispatcher_.SetPinEnabledForUser(debug_user->account_id,
239 debug_user->enable_pin);
240 }
241
242 // Activates or deactivates challenge response for the user at
243 // |user_index|.
ToggleChallengeResponseStateForUserIndex(size_t user_index)244 void ToggleChallengeResponseStateForUserIndex(size_t user_index) {
245 DCHECK(user_index < debug_users_.size());
246 UserMetadata* debug_user = &debug_users_[user_index];
247 debug_user->enable_challenge_response =
248 !debug_user->enable_challenge_response;
249 debug_dispatcher_.SetChallengeResponseAuthEnabledForUser(
250 debug_user->account_id, debug_user->enable_challenge_response);
251 }
252
253 // Activates or deactivates tap unlock for the user at |user_index|.
ToggleTapStateForUserIndex(size_t user_index)254 void ToggleTapStateForUserIndex(size_t user_index) {
255 DCHECK(user_index >= 0 && user_index < debug_users_.size());
256 UserMetadata* debug_user = &debug_users_[user_index];
257 debug_user->enable_tap_to_unlock = !debug_user->enable_tap_to_unlock;
258 debug_dispatcher_.SetTapToUnlockEnabledForUser(
259 debug_user->account_id, debug_user->enable_tap_to_unlock);
260 }
261
262 // Enables click to auth for the user at |user_index|.
CycleEasyUnlockForUserIndex(size_t user_index)263 void CycleEasyUnlockForUserIndex(size_t user_index) {
264 DCHECK(user_index >= 0 && user_index < debug_users_.size());
265 UserMetadata* debug_user = &debug_users_[user_index];
266
267 // EasyUnlockIconId state transition.
268 auto get_next_id = [](EasyUnlockIconId id) {
269 switch (id) {
270 case EasyUnlockIconId::NONE:
271 return EasyUnlockIconId::SPINNER;
272 case EasyUnlockIconId::SPINNER:
273 return EasyUnlockIconId::LOCKED;
274 case EasyUnlockIconId::LOCKED:
275 return EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED;
276 case EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
277 return EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT;
278 case EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
279 return EasyUnlockIconId::HARDLOCKED;
280 case EasyUnlockIconId::HARDLOCKED:
281 return EasyUnlockIconId::UNLOCKED;
282 case EasyUnlockIconId::UNLOCKED:
283 return EasyUnlockIconId::NONE;
284 }
285 return EasyUnlockIconId::NONE;
286 };
287 debug_user->easy_unlock_id = get_next_id(debug_user->easy_unlock_id);
288
289 // Enable/disable click to unlock.
290 debug_user->enable_tap_to_unlock =
291 debug_user->easy_unlock_id == EasyUnlockIconId::UNLOCKED;
292
293 // Prepare icon that we will show.
294 EasyUnlockIconOptions icon;
295 icon.icon = debug_user->easy_unlock_id;
296 if (icon.icon == EasyUnlockIconId::SPINNER) {
297 icon.aria_label = base::ASCIIToUTF16("Icon is spinning");
298 } else if (icon.icon == EasyUnlockIconId::LOCKED ||
299 icon.icon == EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED) {
300 icon.autoshow_tooltip = true;
301 icon.tooltip = base::ASCIIToUTF16(
302 "This is a long message to trigger overflow. This should show up "
303 "automatically. icon_id=" +
304 base::NumberToString(static_cast<int>(icon.icon)));
305 } else {
306 icon.tooltip =
307 base::ASCIIToUTF16("This should not show up automatically.");
308 }
309
310 // Show icon and enable/disable click to unlock.
311 debug_dispatcher_.ShowEasyUnlockIcon(debug_user->account_id, icon);
312 debug_dispatcher_.SetTapToUnlockEnabledForUser(
313 debug_user->account_id, debug_user->enable_tap_to_unlock);
314 }
315
316 // Enables fingerprint auth for the user at |user_index|.
CycleFingerprintStateForUserIndex(size_t user_index)317 void CycleFingerprintStateForUserIndex(size_t user_index) {
318 DCHECK(user_index >= 0 && user_index < debug_users_.size());
319 UserMetadata* debug_user = &debug_users_[user_index];
320
321 debug_user->fingerprint_state = static_cast<FingerprintState>(
322 (static_cast<int>(debug_user->fingerprint_state) + 1) %
323 (static_cast<int>(FingerprintState::kMaxValue) + 1));
324 debug_dispatcher_.SetFingerprintState(debug_user->account_id,
325 debug_user->fingerprint_state);
326 }
AuthenticateFingerprintForUserIndex(size_t user_index,bool success)327 void AuthenticateFingerprintForUserIndex(size_t user_index, bool success) {
328 DCHECK(user_index >= 0 && user_index < debug_users_.size());
329 UserMetadata* debug_user = &debug_users_[user_index];
330 debug_dispatcher_.NotifyFingerprintAuthResult(debug_user->account_id,
331 success);
332 }
333
334 // Force online sign-in for the user at |user_index|.
ForceOnlineSignInForUserIndex(size_t user_index)335 void ForceOnlineSignInForUserIndex(size_t user_index) {
336 DCHECK(user_index >= 0 && user_index < debug_users_.size());
337 debug_dispatcher_.ForceOnlineSignInForUser(
338 debug_users_[user_index].account_id);
339 }
340
341 // Enables or disables user management for the user at |user_index|.
ToggleManagementForUserIndex(size_t user_index)342 void ToggleManagementForUserIndex(size_t user_index) {
343 DCHECK(user_index >= 0 && user_index < debug_users_.size());
344 lock_debug_view_->lock()->ToggleManagementForUserForDebug(
345 debug_users_[user_index].account_id);
346 }
347
348 // Updates |auth_disabled_reason_| with the next enum value in a cyclic
349 // manner.
UpdateAuthDisabledReason()350 void UpdateAuthDisabledReason() {
351 switch (auth_disabled_reason_) {
352 case AuthDisabledReason::kTimeLimitOverride:
353 auth_disabled_reason_ = AuthDisabledReason::kTimeUsageLimit;
354 break;
355 case AuthDisabledReason::kTimeUsageLimit:
356 auth_disabled_reason_ = AuthDisabledReason::kTimeWindowLimit;
357 break;
358 case AuthDisabledReason::kTimeWindowLimit:
359 auth_disabled_reason_ = AuthDisabledReason::kTimeLimitOverride;
360 break;
361 }
362 }
363
364 // Toggle the unlock allowed state for the user at |user_index|.
ToggleAuthEnabledForUserIndex(size_t user_index)365 void ToggleAuthEnabledForUserIndex(size_t user_index) {
366 DCHECK(user_index >= 0 && user_index < debug_users_.size());
367 UserMetadata& user = debug_users_[user_index];
368 user.enable_auth = !user.enable_auth;
369 if (user.enable_auth) {
370 debug_dispatcher_.EnableAuthForUser(user.account_id);
371 } else {
372 debug_dispatcher_.DisableAuthForUser(
373 user.account_id,
374 AuthDisabledData(auth_disabled_reason_,
375 base::Time::Now() +
376 base::TimeDelta::FromHours(user_index) +
377 base::TimeDelta::FromHours(8),
378 base::TimeDelta::FromMinutes(15),
379 true /*bool disable_lock_screen_media*/));
380 UpdateAuthDisabledReason();
381 }
382 }
383
384 // Convert user type to regular user or public account for the user at
385 // |user_index|.
TogglePublicAccountForUserIndex(size_t user_index)386 void TogglePublicAccountForUserIndex(size_t user_index) {
387 DCHECK(user_index >= 0 && user_index < debug_users_.size());
388 UserMetadata& user = debug_users_[user_index];
389 // Swap the type between regular and public account.
390 user.type = user.type == user_manager::USER_TYPE_REGULAR
391 ? user_manager::USER_TYPE_PUBLIC_ACCOUNT
392 : user_manager::USER_TYPE_REGULAR;
393
394 std::vector<LoginUserInfo> users = BuildUserList(debug_users_.size());
395 // Update display name and email in debug users.
396 debug_users_[user_index] = UserMetadata(users[user_index].basic_user_info);
397 NotifyUsers(std::move(users));
398 }
399
ToggleLockScreenNoteButton()400 void ToggleLockScreenNoteButton() {
401 if (lock_screen_note_state_ == mojom::TrayActionState::kAvailable) {
402 lock_screen_note_state_ = mojom::TrayActionState::kNotAvailable;
403 } else {
404 lock_screen_note_state_ = mojom::TrayActionState::kAvailable;
405 }
406
407 debug_dispatcher_.SetLockScreenNoteState(lock_screen_note_state_);
408 }
409
AddKioskApp(ShelfWidget * shelf_widget)410 void AddKioskApp(ShelfWidget* shelf_widget) {
411 KioskAppMenuEntry menu_item;
412 menu_item.app_id = kDebugKioskAppId;
413 menu_item.name = base::UTF8ToUTF16(kDebugKioskAppName);
414 kiosk_apps_.push_back(std::move(menu_item));
415 shelf_widget->login_shelf_view()->SetKioskApps(kiosk_apps_, {}, {});
416 }
417
RemoveKioskApp(ShelfWidget * shelf_widget)418 void RemoveKioskApp(ShelfWidget* shelf_widget) {
419 if (kiosk_apps_.empty())
420 return;
421 kiosk_apps_.pop_back();
422 shelf_widget->login_shelf_view()->SetKioskApps(kiosk_apps_, {}, {});
423 }
424
AddSystemInfo(const std::string & os_version,const std::string & enterprise_info,const std::string & bluetooth_name,bool adb_sideloading_enabled)425 void AddSystemInfo(const std::string& os_version,
426 const std::string& enterprise_info,
427 const std::string& bluetooth_name,
428 bool adb_sideloading_enabled) {
429 debug_dispatcher_.SetSystemInfo(true /*show*/, false /*enforced*/,
430 os_version, enterprise_info, bluetooth_name,
431 adb_sideloading_enabled);
432 }
433
UpdateWarningMessage(const base::string16 & message)434 void UpdateWarningMessage(const base::string16& message) {
435 debug_dispatcher_.UpdateWarningMessage(message);
436 }
437
438 // LoginDataDispatcher::Observer:
OnUsersChanged(const std::vector<LoginUserInfo> & users)439 void OnUsersChanged(const std::vector<LoginUserInfo>& users) override {
440 // Update root_users_ to new source data.
441 root_users_.clear();
442 for (auto& user : users)
443 root_users_.push_back(user);
444
445 // Rebuild debug users using new source data.
446 SetUserCount(root_users_.size());
447
448 on_users_received_.Run();
449 }
OnPinEnabledForUserChanged(const AccountId & user,bool enabled)450 void OnPinEnabledForUserChanged(const AccountId& user,
451 bool enabled) override {
452 // Forward notification only if the user is currently being shown.
453 for (size_t i = 0u; i < debug_users_.size(); ++i) {
454 if (debug_users_[i].account_id == user) {
455 debug_users_[i].enable_pin = enabled;
456 debug_dispatcher_.SetPinEnabledForUser(user, enabled);
457 break;
458 }
459 }
460 }
OnTapToUnlockEnabledForUserChanged(const AccountId & user,bool enabled)461 void OnTapToUnlockEnabledForUserChanged(const AccountId& user,
462 bool enabled) override {
463 // Forward notification only if the user is currently being shown.
464 for (size_t i = 0u; i < debug_users_.size(); ++i) {
465 if (debug_users_[i].account_id == user) {
466 debug_users_[i].enable_tap_to_unlock = enabled;
467 debug_dispatcher_.SetTapToUnlockEnabledForUser(user, enabled);
468 break;
469 }
470 }
471 }
OnLockScreenNoteStateChanged(mojom::TrayActionState state)472 void OnLockScreenNoteStateChanged(mojom::TrayActionState state) override {
473 lock_screen_note_state_ = state;
474 debug_dispatcher_.SetLockScreenNoteState(state);
475 }
OnShowEasyUnlockIcon(const AccountId & user,const EasyUnlockIconOptions & icon)476 void OnShowEasyUnlockIcon(const AccountId& user,
477 const EasyUnlockIconOptions& icon) override {
478 debug_dispatcher_.ShowEasyUnlockIcon(user, icon);
479 }
OnDetachableBasePairingStatusChanged(DetachableBasePairingStatus pairing_status)480 void OnDetachableBasePairingStatusChanged(
481 DetachableBasePairingStatus pairing_status) override {
482 debug_dispatcher_.SetDetachableBasePairingStatus(pairing_status);
483 }
484
OnPublicSessionKeyboardLayoutsChanged(const AccountId & account_id,const std::string & locale,const std::vector<InputMethodItem> & keyboard_layouts)485 void OnPublicSessionKeyboardLayoutsChanged(
486 const AccountId& account_id,
487 const std::string& locale,
488 const std::vector<InputMethodItem>& keyboard_layouts) override {
489 debug_dispatcher_.SetPublicSessionKeyboardLayouts(account_id, locale,
490 keyboard_layouts);
491 }
492
OnPublicSessionShowFullManagementDisclosureChanged(bool show_full_management_disclosure)493 void OnPublicSessionShowFullManagementDisclosureChanged(
494 bool show_full_management_disclosure) override {
495 debug_dispatcher_.SetPublicSessionShowFullManagementDisclosure(
496 show_full_management_disclosure);
497 }
498
499 private:
500 // The debug overlay UI takes ground-truth data from |root_dispatcher_|,
501 // applies a series of transformations to it, and exposes it to the UI via
502 // |debug_dispatcher_|.
503 LoginDataDispatcher* root_dispatcher_; // Unowned.
504 LoginDataDispatcher debug_dispatcher_;
505
506 // Original set of users from |root_dispatcher_|.
507 std::vector<LoginUserInfo> root_users_;
508
509 // Metadata for users that the UI is displaying.
510 std::vector<UserMetadata> debug_users_;
511
512 // The current lock screen note action state.
513 mojom::TrayActionState lock_screen_note_state_;
514
515 // List of kiosk apps loaded.
516 std::vector<KioskAppMenuEntry> kiosk_apps_;
517
518 // Called when a new user list has been received.
519 base::RepeatingClosure on_users_received_;
520
521 // Called for testing functions not belonging to the login data dispatcher.
522 // In such a case, we want to bypass the event handling mechanism and do
523 // direct calls to the lock screen. We need either an instance of
524 // LockDebugView or LockContentsView in order to do so.
525 LockDebugView* const lock_debug_view_;
526
527 // When auth is disabled, this property is used to define the reason, which
528 // customizes the UI accordingly.
529 AuthDisabledReason auth_disabled_reason_ =
530 AuthDisabledReason::kTimeLimitOverride;
531
532 DISALLOW_COPY_AND_ASSIGN(DebugDataDispatcherTransformer);
533 };
534
535 // In-memory wrapper around LoginDetachableBaseModel used by lock UI.
536 // It provides, methods to override the detachable base pairing state seen by
537 // the UI.
538 class LockDebugView::DebugLoginDetachableBaseModel
539 : public LoginDetachableBaseModel {
540 public:
541 static constexpr int kNullBaseId = -1;
542
543 DebugLoginDetachableBaseModel() = default;
544 ~DebugLoginDetachableBaseModel() override = default;
545
debugging_pairing_state() const546 bool debugging_pairing_state() const { return pairing_status_.has_value(); }
547
548 // Calculates the pairing status to which the model should be changed when
549 // button for cycling detachable base pairing statuses is clicked.
NextPairingStatus() const550 DetachableBasePairingStatus NextPairingStatus() const {
551 if (!pairing_status_.has_value())
552 return DetachableBasePairingStatus::kNone;
553
554 switch (*pairing_status_) {
555 case DetachableBasePairingStatus::kNone:
556 return DetachableBasePairingStatus::kAuthenticated;
557 case DetachableBasePairingStatus::kAuthenticated:
558 return DetachableBasePairingStatus::kNotAuthenticated;
559 case DetachableBasePairingStatus::kNotAuthenticated:
560 return DetachableBasePairingStatus::kInvalidDevice;
561 case DetachableBasePairingStatus::kInvalidDevice:
562 return DetachableBasePairingStatus::kNone;
563 }
564
565 return DetachableBasePairingStatus::kNone;
566 }
567
568 // Calculates the debugging detachable base ID that should become the paired
569 // base in the model when the button for cycling paired bases is clicked.
NextBaseId() const570 int NextBaseId() const {
571 return (base_id_ + 1) % base::size(kDebugDetachableBases);
572 }
573
574 // Gets the descripting text for currently paired base, if any.
BaseButtonText() const575 std::string BaseButtonText() const {
576 if (base_id_ < 0)
577 return "No base";
578 return kDebugDetachableBases[base_id_];
579 }
580
581 // Sets the model's pairing state - base pairing status, and the currently
582 // paired base ID. ID should be an index in |kDebugDetachableBases| array, and
583 // it should be set if pairing status is kAuthenticated. The base ID is
584 // ignored if pairing state is different than kAuthenticated.
SetPairingState(DetachableBasePairingStatus pairing_status,int base_id)585 void SetPairingState(DetachableBasePairingStatus pairing_status,
586 int base_id) {
587 pairing_status_ = pairing_status;
588 if (pairing_status == DetachableBasePairingStatus::kAuthenticated) {
589 CHECK_GE(base_id, 0);
590 CHECK_LT(base_id, static_cast<int>(base::size(kDebugDetachableBases)));
591 base_id_ = base_id;
592 } else {
593 base_id_ = kNullBaseId;
594 }
595
596 Shell::Get()
597 ->login_screen_controller()
598 ->data_dispatcher()
599 ->SetDetachableBasePairingStatus(pairing_status);
600 }
601
602 // Marks the paired base (as seen by the model) as the user's last used base.
603 // No-op if the current pairing status is different than kAuthenticated.
SetBaseLastUsedForUser(const AccountId & account_id)604 void SetBaseLastUsedForUser(const AccountId& account_id) {
605 if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
606 return;
607 DCHECK_GE(base_id_, 0);
608
609 last_used_bases_[account_id] = base_id_;
610 Shell::Get()
611 ->login_screen_controller()
612 ->data_dispatcher()
613 ->SetDetachableBasePairingStatus(*pairing_status_);
614 }
615
616 // Clears all in-memory pairing state.
ClearDebugPairingState()617 void ClearDebugPairingState() {
618 pairing_status_ = base::nullopt;
619 base_id_ = kNullBaseId;
620 last_used_bases_.clear();
621
622 Shell::Get()
623 ->login_screen_controller()
624 ->data_dispatcher()
625 ->SetDetachableBasePairingStatus(DetachableBasePairingStatus::kNone);
626 }
627
628 // LoginDetachableBaseModel:
GetPairingStatus()629 DetachableBasePairingStatus GetPairingStatus() override {
630 if (!pairing_status_.has_value())
631 return DetachableBasePairingStatus::kNone;
632 return *pairing_status_;
633 }
PairedBaseMatchesLastUsedByUser(const UserInfo & user_info)634 bool PairedBaseMatchesLastUsedByUser(const UserInfo& user_info) override {
635 if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
636 return false;
637
638 if (last_used_bases_.count(user_info.account_id) == 0)
639 return true;
640 return last_used_bases_[user_info.account_id] == base_id_;
641 }
SetPairedBaseAsLastUsedByUser(const UserInfo & user_info)642 bool SetPairedBaseAsLastUsedByUser(const UserInfo& user_info) override {
643 if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated)
644 return false;
645
646 last_used_bases_[user_info.account_id] = base_id_;
647 return true;
648 }
649
650 private:
651 // In-memory detachable base pairing state.
652 base::Optional<DetachableBasePairingStatus> pairing_status_;
653 int base_id_ = kNullBaseId;
654 // Maps user account to the last used detachable base ID (base ID being the
655 // base's index in kDebugDetachableBases array).
656 std::map<AccountId, int> last_used_bases_;
657
658 DISALLOW_COPY_AND_ASSIGN(DebugLoginDetachableBaseModel);
659 };
660
LockDebugView(mojom::TrayActionState initial_note_action_state,LockScreen::ScreenType screen_type)661 LockDebugView::LockDebugView(mojom::TrayActionState initial_note_action_state,
662 LockScreen::ScreenType screen_type)
663 : debug_data_dispatcher_(std::make_unique<DebugDataDispatcherTransformer>(
664 initial_note_action_state,
665 Shell::Get()->login_screen_controller()->data_dispatcher(),
666 base::BindRepeating(
667 &LockDebugView::UpdatePerUserActionContainerAndLayout,
668 base::Unretained(this)),
669 this)),
670 next_auth_error_type_(AuthErrorType::kFirstUnlockFailed) {
671 SetLayoutManager(std::make_unique<views::BoxLayout>(
672 views::BoxLayout::Orientation::kHorizontal));
673
674 auto debug_detachable_base_model =
675 std::make_unique<DebugLoginDetachableBaseModel>();
676 debug_detachable_base_model_ = debug_detachable_base_model.get();
677
678 lock_ = new LockContentsView(initial_note_action_state, screen_type,
679 debug_data_dispatcher_->debug_dispatcher(),
680 std::move(debug_detachable_base_model));
681 AddChildView(lock_);
682
683 container_ = new NonAccessibleView();
684 container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
685 views::BoxLayout::Orientation::kVertical));
686 AddChildView(container_);
687
688 auto* margin = new NonAccessibleView();
689 margin->SetPreferredSize(gfx::Size(10, 10));
690 container_->AddChildView(margin);
691
692 global_action_view_container_ = new NonAccessibleView();
693 global_action_view_container_->SetLayoutManager(
694 std::make_unique<views::BoxLayout>(
695 views::BoxLayout::Orientation::kVertical));
696
697 auto add_horizontal_container = [&]() {
698 auto* container = new NonAccessibleView();
699 container->SetLayoutManager(std::make_unique<views::BoxLayout>(
700 views::BoxLayout::Orientation::kHorizontal));
701 global_action_view_container_->AddChildView(container);
702 return container;
703 };
704
705 auto* change_users_container = add_horizontal_container();
706 AddButton("Add user",
707 base::BindRepeating(&LockDebugView::AddOrRemoveUsersButtonPressed,
708 base::Unretained(this), 1),
709 change_users_container);
710 AddButton("Add 10 users",
711 base::BindRepeating(&LockDebugView::AddOrRemoveUsersButtonPressed,
712 base::Unretained(this), 10),
713 change_users_container);
714 AddButton("Remove user",
715 base::BindRepeating(&LockDebugView::AddOrRemoveUsersButtonPressed,
716 base::Unretained(this), -1),
717 change_users_container);
718
719 auto* toggle_container = add_horizontal_container();
720 AddButton("Blur", base::BindRepeating([]() {
721 auto* const wallpaper_controller =
722 Shell::Get()->wallpaper_controller();
723 wallpaper_controller->UpdateWallpaperBlurForLockState(
724 !wallpaper_controller->IsWallpaperBlurredForLockState());
725 }),
726 toggle_container);
727 AddButton("Toggle note action",
728 base::BindRepeating(
729 &DebugDataDispatcherTransformer::ToggleLockScreenNoteButton,
730 base::Unretained(debug_data_dispatcher_.get())),
731 toggle_container);
732 AddButton("Toggle caps lock", base::BindRepeating([]() {
733 ImeControllerImpl* ime_controller =
734 Shell::Get()->ime_controller();
735 ime_controller->SetCapsLockEnabled(
736 !ime_controller->IsCapsLockEnabled());
737 }),
738 toggle_container);
739 global_action_add_system_info_ =
740 AddButton("Add system info",
741 base::BindRepeating(&LockDebugView::AddSystemInfoButtonPressed,
742 base::Unretained(this)),
743 toggle_container);
744 global_action_toggle_auth_ =
745 AddButton("Auth (allowed)",
746 base::BindRepeating(&LockDebugView::ToggleAuthButtonPressed,
747 base::Unretained(this)),
748 toggle_container);
749 AddButton("Cycle auth error",
750 base::BindRepeating(&LockDebugView::CycleAuthErrorMessage,
751 base::Unretained(this)),
752 toggle_container);
753 AddButton(
754 "Toggle warning banner",
755 base::BindRepeating(&LockDebugView::ToggleWarningBannerButtonPressed,
756 base::Unretained(this)),
757 toggle_container);
758 AddButton("Show parent access",
759 base::BindRepeating(&LockContentsView::ShowParentAccessDialog,
760 base::Unretained(lock_)),
761 toggle_container);
762
763 auto* kiosk_container = add_horizontal_container();
764 AddButton("Add kiosk app",
765 base::BindRepeating(&LockDebugView::AddKioskAppButtonPressed,
766 base::Unretained(this)),
767 kiosk_container);
768 AddButton("Remove kiosk app",
769 base::BindRepeating(&LockDebugView::RemoveKioskAppButtonPressed,
770 base::Unretained(this)),
771 kiosk_container);
772 AddButton("Show kiosk error",
773 base::BindRepeating(
774 &LoginScreenController::ShowKioskAppError,
775 base::Unretained(Shell::Get()->login_screen_controller()),
776 "Test error message."),
777 kiosk_container);
778
779 auto* managed_sessions_container = add_horizontal_container();
780 AddButton("Toggle managed session disclosure",
781 base::BindRepeating(
782 &LockDebugView::ToggleManagedSessionDisclosureButtonPressed,
783 base::Unretained(this)),
784 managed_sessions_container);
785
786 global_action_detachable_base_group_ = add_horizontal_container();
787 UpdateDetachableBaseColumn();
788
789 per_user_action_view_container_ = new NonAccessibleView();
790 per_user_action_view_container_->SetLayoutManager(
791 std::make_unique<views::BoxLayout>(
792 views::BoxLayout::Orientation::kVertical));
793 UpdatePerUserActionContainer();
794
795 auto make_scroll = [](views::View* content,
796 int height) -> std::unique_ptr<views::View> {
797 std::unique_ptr<views::ScrollView> scroll =
798 views::ScrollView::CreateScrollViewWithBorder();
799 scroll->SetPreferredSize(gfx::Size(600, height));
800 scroll->SetContents(base::WrapUnique(content));
801 scroll->SetBackgroundColor(base::nullopt);
802 scroll->SetVerticalScrollBar(
803 std::make_unique<views::OverlayScrollBar>(false));
804 scroll->SetHorizontalScrollBar(
805 std::make_unique<views::OverlayScrollBar>(true));
806 return scroll;
807 };
808 container_->AddChildView(make_scroll(global_action_view_container_, 110));
809 container_->AddChildView(make_scroll(per_user_action_view_container_, 100));
810
811 Layout();
812 }
813
~LockDebugView()814 LockDebugView::~LockDebugView() {
815 // Make sure debug_data_dispatcher_ lives longer than LockContentsView so
816 // pointer debug_dispatcher_ is always valid for LockContentsView.
817 delete lock_;
818 }
819
Layout()820 void LockDebugView::Layout() {
821 global_action_view_container_->SizeToPreferredSize();
822 per_user_action_view_container_->SizeToPreferredSize();
823
824 views::View::Layout();
825
826 lock_->SetBoundsRect(GetLocalBounds());
827 container_->SetPosition(gfx::Point());
828 container_->SizeToPreferredSize();
829 }
830
AddOrRemoveUsersButtonPressed(int delta)831 void LockDebugView::AddOrRemoveUsersButtonPressed(int delta) {
832 debug_data_dispatcher_->SetUserCount(
833 std::max(0, debug_data_dispatcher_->GetUserCount() + delta));
834 UpdatePerUserActionContainer();
835 Layout();
836 }
837
AddSystemInfoButtonPressed()838 void LockDebugView::AddSystemInfoButtonPressed() {
839 ++num_system_info_clicks_;
840 if (num_system_info_clicks_ >= 7)
841 global_action_add_system_info_->SetEnabled(false);
842
843 std::string os_version = num_system_info_clicks_ / 4 ? kDebugOsVersion : "";
844 std::string enterprise_info =
845 (num_system_info_clicks_ % 4) / 2 ? kDebugEnterpriseInfo : "";
846 std::string bluetooth_name =
847 num_system_info_clicks_ % 2 ? kDebugBluetoothName : "";
848 bool adb_sideloading_enabled = num_system_info_clicks_ % 3;
849 debug_data_dispatcher_->AddSystemInfo(
850 os_version, enterprise_info, bluetooth_name, adb_sideloading_enabled);
851 }
852
ToggleAuthButtonPressed()853 void LockDebugView::ToggleAuthButtonPressed() {
854 auto get_next_auth_state = [](LoginScreenController::ForceFailAuth auth) {
855 switch (auth) {
856 case LoginScreenController::ForceFailAuth::kOff:
857 return LoginScreenController::ForceFailAuth::kImmediate;
858 case LoginScreenController::ForceFailAuth::kImmediate:
859 return LoginScreenController::ForceFailAuth::kDelayed;
860 case LoginScreenController::ForceFailAuth::kDelayed:
861 return LoginScreenController::ForceFailAuth::kOff;
862 }
863 };
864 auto get_auth_label = [](LoginScreenController::ForceFailAuth auth) {
865 switch (auth) {
866 case LoginScreenController::ForceFailAuth::kOff:
867 return "Auth (allowed)";
868 case LoginScreenController::ForceFailAuth::kImmediate:
869 return "Auth (immediate fail)";
870 case LoginScreenController::ForceFailAuth::kDelayed:
871 return "Auth (delayed fail)";
872 }
873 };
874 force_fail_auth_ = get_next_auth_state(force_fail_auth_);
875 global_action_toggle_auth_->SetText(
876 base::ASCIIToUTF16(get_auth_label(force_fail_auth_)));
877 Layout();
878 Shell::Get()
879 ->login_screen_controller()
880 ->set_force_fail_auth_for_debug_overlay(force_fail_auth_);
881 }
882
AddKioskAppButtonPressed()883 void LockDebugView::AddKioskAppButtonPressed() {
884 debug_data_dispatcher_->AddKioskApp(
885 Shelf::ForWindow(GetWidget()->GetNativeWindow())->shelf_widget());
886 }
887
RemoveKioskAppButtonPressed()888 void LockDebugView::RemoveKioskAppButtonPressed() {
889 debug_data_dispatcher_->RemoveKioskApp(
890 Shelf::ForWindow(GetWidget()->GetNativeWindow())->shelf_widget());
891 }
892
ToggleDebugDetachableBaseButtonPressed()893 void LockDebugView::ToggleDebugDetachableBaseButtonPressed() {
894 if (debug_detachable_base_model_->debugging_pairing_state()) {
895 debug_detachable_base_model_->ClearDebugPairingState();
896 // In authenticated state, per user column has a button to mark the
897 // current base as last used for the user - it should get removed when the
898 // detachable base debugging gets disabled.
899 UpdatePerUserActionContainer();
900 } else {
901 debug_detachable_base_model_->SetPairingState(
902 DetachableBasePairingStatus::kNone,
903 DebugLoginDetachableBaseModel::kNullBaseId);
904 }
905 UpdateDetachableBaseColumn();
906 Layout();
907 }
908
CycleDetachableBaseStatusButtonPressed()909 void LockDebugView::CycleDetachableBaseStatusButtonPressed() {
910 debug_detachable_base_model_->SetPairingState(
911 debug_detachable_base_model_->NextPairingStatus(),
912 debug_detachable_base_model_->NextBaseId());
913 UpdatePerUserActionContainer();
914 UpdateDetachableBaseColumn();
915 Layout();
916 }
917
CycleDetachableBaseIdButtonPressed()918 void LockDebugView::CycleDetachableBaseIdButtonPressed() {
919 debug_detachable_base_model_->SetPairingState(
920 DetachableBasePairingStatus::kAuthenticated,
921 debug_detachable_base_model_->NextBaseId());
922 UpdateDetachableBaseColumn();
923 Layout();
924 }
925
ToggleWarningBannerButtonPressed()926 void LockDebugView::ToggleWarningBannerButtonPressed() {
927 debug_data_dispatcher_->UpdateWarningMessage(
928 is_warning_banner_shown_
929 ? base::string16()
930 : base::ASCIIToUTF16("A critical update is ready to install. Sign "
931 "in to get started."));
932 is_warning_banner_shown_ = !is_warning_banner_shown_;
933 }
934
ToggleManagedSessionDisclosureButtonPressed()935 void LockDebugView::ToggleManagedSessionDisclosureButtonPressed() {
936 is_managed_session_disclosure_shown_ = !is_managed_session_disclosure_shown_;
937 debug_data_dispatcher_->OnPublicSessionShowFullManagementDisclosureChanged(
938 is_managed_session_disclosure_shown_);
939 }
940
UseDetachableBaseButtonPressed(int index)941 void LockDebugView::UseDetachableBaseButtonPressed(int index) {
942 debug_detachable_base_model_->SetBaseLastUsedForUser(
943 debug_data_dispatcher_->GetAccountIdForUserIndex(index));
944 }
945
TogglePublicAccountButtonPressed(int index)946 void LockDebugView::TogglePublicAccountButtonPressed(int index) {
947 debug_data_dispatcher_->TogglePublicAccountForUserIndex(index);
948 UpdatePerUserActionContainer();
949 Layout();
950 }
951
CycleAuthErrorMessage()952 void LockDebugView::CycleAuthErrorMessage() {
953 switch (next_auth_error_type_) {
954 case AuthErrorType::kFirstUnlockFailed:
955 next_auth_error_type_ = AuthErrorType::kFirstUnlockFailedCapsLockOn;
956 Shell::Get()->ime_controller()->UpdateCapsLockState(
957 false /*caps_enabled*/);
958 debug_detachable_base_model_->SetPairingState(
959 DetachableBasePairingStatus::kNone,
960 DebugLoginDetachableBaseModel::kNullBaseId);
961 lock_->ShowAuthErrorMessageForDebug(1 /*unlock_attempt*/);
962 return;
963 case AuthErrorType::kFirstUnlockFailedCapsLockOn:
964 next_auth_error_type_ = AuthErrorType::kSecondUnlockFailed;
965 Shell::Get()->ime_controller()->UpdateCapsLockState(
966 true /*caps_enabled*/);
967 lock_->ShowAuthErrorMessageForDebug(1 /*unlock_attempt*/);
968 return;
969 case AuthErrorType::kSecondUnlockFailed:
970 next_auth_error_type_ = AuthErrorType::kSecondUnlockFailedCapsLockOn;
971 Shell::Get()->ime_controller()->UpdateCapsLockState(
972 false /*caps_enabled*/);
973 lock_->ShowAuthErrorMessageForDebug(2 /*unlock_attempt*/);
974 return;
975 case AuthErrorType::kSecondUnlockFailedCapsLockOn:
976 next_auth_error_type_ = AuthErrorType::kDetachableBaseFailed;
977 Shell::Get()->ime_controller()->UpdateCapsLockState(
978 true /*caps_enabled*/);
979 lock_->ShowAuthErrorMessageForDebug(2 /*unlock_attempt*/);
980 return;
981 case AuthErrorType::kDetachableBaseFailed:
982 next_auth_error_type_ = AuthErrorType::kFirstUnlockFailed;
983 debug_detachable_base_model_->SetPairingState(
984 DetachableBasePairingStatus::kNotAuthenticated,
985 DebugLoginDetachableBaseModel::kNullBaseId);
986 return;
987 default:
988 NOTREACHED();
989 }
990 }
991
UpdatePerUserActionContainer()992 void LockDebugView::UpdatePerUserActionContainer() {
993 per_user_action_view_container_->RemoveAllChildViews(
994 true /*delete_children*/);
995
996 int num_users = debug_data_dispatcher_->GetUserCount();
997 for (int i = 0; i < num_users; ++i) {
998 auto* row = new NonAccessibleView();
999 row->SetLayoutManager(std::make_unique<views::BoxLayout>(
1000 views::BoxLayout::Orientation::kHorizontal));
1001
1002 auto* name = new views::Label();
1003 name->SetText(debug_data_dispatcher_->GetDisplayNameForUserIndex(i));
1004 name->SetSubpixelRenderingEnabled(false);
1005 name->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
1006 AshColorProvider::ContentLayerType::kTextColorPrimary));
1007 name->SetAutoColorReadabilityEnabled(false);
1008 row->AddChildView(name);
1009
1010 AddButton("Toggle PIN",
1011 base::BindRepeating(
1012 &DebugDataDispatcherTransformer::TogglePinStateForUserIndex,
1013 base::Unretained(debug_data_dispatcher_.get()), i),
1014 row);
1015 AddButton(
1016 "Toggle Smart card",
1017 base::BindRepeating(&DebugDataDispatcherTransformer::
1018 ToggleChallengeResponseStateForUserIndex,
1019 base::Unretained(debug_data_dispatcher_.get()), i),
1020 row);
1021 AddButton("Toggle Tap",
1022 base::BindRepeating(
1023 &DebugDataDispatcherTransformer::ToggleTapStateForUserIndex,
1024 base::Unretained(debug_data_dispatcher_.get()), i),
1025 row);
1026 AddButton("Cycle easy unlock",
1027 base::BindRepeating(
1028 &DebugDataDispatcherTransformer::CycleEasyUnlockForUserIndex,
1029 base::Unretained(debug_data_dispatcher_.get()), i),
1030 row);
1031 AddButton(
1032 "Cycle fingerprint state",
1033 base::BindRepeating(
1034 &DebugDataDispatcherTransformer::CycleFingerprintStateForUserIndex,
1035 base::Unretained(debug_data_dispatcher_.get()), i),
1036 row);
1037 AddButton("Send fingerprint auth success",
1038 base::BindRepeating(
1039 &DebugDataDispatcherTransformer::
1040 AuthenticateFingerprintForUserIndex,
1041 base::Unretained(debug_data_dispatcher_.get()), i, true),
1042 row);
1043 AddButton("Send fingerprint auth fail",
1044 base::BindRepeating(
1045 &DebugDataDispatcherTransformer::
1046 AuthenticateFingerprintForUserIndex,
1047 base::Unretained(debug_data_dispatcher_.get()), i, false),
1048 row);
1049 AddButton(
1050 "Force online sign-in",
1051 base::BindRepeating(
1052 &DebugDataDispatcherTransformer::ForceOnlineSignInForUserIndex,
1053 base::Unretained(debug_data_dispatcher_.get()), i),
1054 row);
1055 AddButton("Toggle user is managed",
1056 base::BindRepeating(
1057 &DebugDataDispatcherTransformer::ToggleManagementForUserIndex,
1058 base::Unretained(debug_data_dispatcher_.get()), i),
1059 row);
1060 AddButton(
1061 "Toggle auth enabled",
1062 base::BindRepeating(
1063 &DebugDataDispatcherTransformer::ToggleAuthEnabledForUserIndex,
1064 base::Unretained(debug_data_dispatcher_.get()), i),
1065 row);
1066
1067 if (debug_detachable_base_model_->debugging_pairing_state() &&
1068 debug_detachable_base_model_->GetPairingStatus() ==
1069 DetachableBasePairingStatus::kAuthenticated) {
1070 AddButton(
1071 "Set base used",
1072 base::BindRepeating(&LockDebugView::UseDetachableBaseButtonPressed,
1073 base::Unretained(this), i),
1074 row);
1075 }
1076
1077 AddButton(
1078 "Toggle Public Account",
1079 base::BindRepeating(&LockDebugView::TogglePublicAccountButtonPressed,
1080 base::Unretained(this), i),
1081 row)
1082 ->set_tag(i);
1083
1084 per_user_action_view_container_->AddChildView(row);
1085 }
1086 }
1087
UpdatePerUserActionContainerAndLayout()1088 void LockDebugView::UpdatePerUserActionContainerAndLayout() {
1089 UpdatePerUserActionContainer();
1090 Layout();
1091 }
1092
UpdateDetachableBaseColumn()1093 void LockDebugView::UpdateDetachableBaseColumn() {
1094 global_action_detachable_base_group_->RemoveAllChildViews(
1095 true /*delete_children*/);
1096
1097 AddButton("Debug detachable base",
1098 base::BindRepeating(
1099 &LockDebugView::ToggleDebugDetachableBaseButtonPressed,
1100 base::Unretained(this)),
1101 global_action_detachable_base_group_);
1102 if (!debug_detachable_base_model_->debugging_pairing_state())
1103 return;
1104
1105 const std::string kPairingStatusText =
1106 "Pairing status: " +
1107 DetachableBasePairingStatusToString(
1108 debug_detachable_base_model_->GetPairingStatus());
1109 AddButton(kPairingStatusText,
1110 base::BindRepeating(
1111 &LockDebugView::CycleDetachableBaseStatusButtonPressed,
1112 base::Unretained(this)),
1113 global_action_detachable_base_group_);
1114
1115 views::LabelButton* cycle_detachable_base_id = AddButton(
1116 debug_detachable_base_model_->BaseButtonText(),
1117 base::BindRepeating(&LockDebugView::CycleDetachableBaseIdButtonPressed,
1118 base::Unretained(this)),
1119 global_action_detachable_base_group_);
1120 bool base_authenticated = debug_detachable_base_model_->GetPairingStatus() ==
1121 DetachableBasePairingStatus::kAuthenticated;
1122 cycle_detachable_base_id->SetEnabled(base_authenticated);
1123 }
1124
AddButton(const std::string & text,views::Button::PressedCallback callback,views::View * container)1125 views::LabelButton* LockDebugView::AddButton(
1126 const std::string& text,
1127 views::Button::PressedCallback callback,
1128 views::View* container) {
1129 // Creates a button with |text| that cannot be focused.
1130 auto button = std::make_unique<views::MdTextButton>(std::move(callback),
1131 base::ASCIIToUTF16(text));
1132 button->SetFocusBehavior(views::View::FocusBehavior::NEVER);
1133
1134 views::LabelButton* view = button.get();
1135 container->AddChildView(
1136 login_views_utils::WrapViewForPreferredSize(std::move(button)));
1137 return view;
1138 }
1139
1140 } // namespace ash
1141