1 // Copyright (c) 2011 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 "chrome/browser/chromeos/login/user_manager.h"
6
7 #include "base/command_line.h"
8 #include "base/compiler_specific.h"
9 #include "base/file_path.h"
10 #include "base/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/path_service.h"
14 #include "base/string_util.h"
15 #include "base/stringprintf.h"
16 #include "base/time.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "crypto/nss_util.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/cros/cros_library.h"
22 #include "chrome/browser/chromeos/cros/cryptohome_library.h"
23 #include "chrome/browser/chromeos/cros/input_method_library.h"
24 #include "chrome/browser/chromeos/login/default_user_images.h"
25 #include "chrome/browser/chromeos/login/login_display.h"
26 #include "chrome/browser/chromeos/login/ownership_service.h"
27 #include "chrome/browser/chromeos/user_cros_settings_provider.h"
28 #include "chrome/browser/chromeos/wm_ipc.h"
29 #include "chrome/browser/defaults.h"
30 #include "chrome/browser/prefs/pref_service.h"
31 #include "chrome/browser/prefs/scoped_user_pref_update.h"
32 #include "chrome/common/chrome_paths.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "content/browser/browser_thread.h"
35 #include "content/common/notification_service.h"
36 #include "content/common/notification_type.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/codec/png_codec.h"
39
40 namespace chromeos {
41
42 namespace {
43
44 // A vector pref of the users who have logged into the device.
45 const char kLoggedInUsers[] = "LoggedInUsers";
46 // A dictionary that maps usernames to file paths to their images.
47 const char kUserImages[] = "UserImages";
48
49 // Incognito user is represented by an empty string (since some code already
50 // depends on that and it's hard to figure out what).
51 const char kGuestUser[] = "";
52
53 base::LazyInstance<UserManager> g_user_manager(base::LINKER_INITIALIZED);
54
55 // Stores path to the image in local state. Runs on UI thread.
SavePathToLocalState(const std::string & username,const std::string & image_path)56 void SavePathToLocalState(const std::string& username,
57 const std::string& image_path) {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 PrefService* local_state = g_browser_process->local_state();
60 DictionaryPrefUpdate images_update(local_state, kUserImages);
61 images_update->SetWithoutPathExpansion(username, new StringValue(image_path));
62 DVLOG(1) << "Saving path to user image in Local State.";
63 local_state->SavePersistentPrefs();
64 }
65
66 // Saves image to file with specified path. Runs on FILE thread.
67 // Posts task for saving image path to local state on UI thread.
SaveImageToFile(const SkBitmap & image,const FilePath & image_path,const std::string & username)68 void SaveImageToFile(const SkBitmap& image,
69 const FilePath& image_path,
70 const std::string& username) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
72 std::vector<unsigned char> encoded_image;
73 if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &encoded_image)) {
74 LOG(ERROR) << "Failed to PNG encode the image.";
75 return;
76 }
77
78 if (file_util::WriteFile(image_path,
79 reinterpret_cast<char*>(&encoded_image[0]),
80 encoded_image.size()) == -1) {
81 LOG(ERROR) << "Failed to save image to file.";
82 return;
83 }
84
85 BrowserThread::PostTask(
86 BrowserThread::UI,
87 FROM_HERE,
88 NewRunnableFunction(&SavePathToLocalState,
89 username, image_path.value()));
90 }
91
92 // Deletes user's image file. Runs on FILE thread.
DeleteUserImage(const FilePath & image_path)93 void DeleteUserImage(const FilePath& image_path) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
95 if (!file_util::Delete(image_path, false)) {
96 LOG(ERROR) << "Failed to remove user image.";
97 return;
98 }
99 }
100
101 // Updates current user ownership on UI thread.
UpdateOwnership(bool is_owner)102 void UpdateOwnership(bool is_owner) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
104
105 g_user_manager.Get().set_current_user_is_owner(is_owner);
106 NotificationService::current()->Notify(NotificationType::OWNERSHIP_CHECKED,
107 NotificationService::AllSources(),
108 NotificationService::NoDetails());
109 if (is_owner) {
110 // Also update cached value.
111 UserCrosSettingsProvider::UpdateCachedOwner(
112 g_user_manager.Get().logged_in_user().email());
113 }
114 }
115
116 // Checks current user's ownership on file thread.
CheckOwnership()117 void CheckOwnership() {
118 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
119 bool is_owner = OwnershipService::GetSharedInstance()->CurrentUserIsOwner();
120 VLOG(1) << "Current user " << (is_owner ? "is owner" : "is not owner");
121
122 g_user_manager.Get().set_current_user_is_owner(is_owner);
123
124 // UserManager should be accessed only on UI thread.
125 BrowserThread::PostTask(
126 BrowserThread::UI,
127 FROM_HERE,
128 NewRunnableFunction(&UpdateOwnership, is_owner));
129 }
130
131 // Used to handle the asynchronous response of deleting a cryptohome directory.
132 class RemoveAttempt : public CryptohomeLibrary::Delegate {
133 public:
134 // Creates new remove attempt for the given user. Note, |delegate| can
135 // be NULL.
RemoveAttempt(const std::string & user_email,chromeos::RemoveUserDelegate * delegate)136 RemoveAttempt(const std::string& user_email,
137 chromeos::RemoveUserDelegate* delegate)
138 : user_email_(user_email),
139 delegate_(delegate),
140 method_factory_(this) {
141 RemoveUser();
142 }
143
RemoveUser()144 void RemoveUser() {
145 // Owner is not allowed to be removed from the device.
146 // Must not proceed without signature verification.
147 UserCrosSettingsProvider user_settings;
148 bool trusted_owner_available = user_settings.RequestTrustedOwner(
149 method_factory_.NewRunnableMethod(&RemoveAttempt::RemoveUser));
150 if (!trusted_owner_available) {
151 // Value of owner email is still not verified.
152 // Another attempt will be invoked after verification completion.
153 return;
154 }
155 if (user_email_ == UserCrosSettingsProvider::cached_owner()) {
156 // Owner is not allowed to be removed from the device. Probably on
157 // the stack, so deffer the deletion.
158 MessageLoop::current()->DeleteSoon(FROM_HERE, this);
159 return;
160 }
161
162 if (delegate_)
163 delegate_->OnBeforeUserRemoved(user_email_);
164
165 chromeos::UserManager::Get()->RemoveUserFromList(user_email_);
166 RemoveUserCryptohome();
167
168 if (delegate_)
169 delegate_->OnUserRemoved(user_email_);
170 }
171
RemoveUserCryptohome()172 void RemoveUserCryptohome() {
173 if (CrosLibrary::Get()->EnsureLoaded()) {
174 CrosLibrary::Get()->GetCryptohomeLibrary()->AsyncRemove(user_email_,
175 this);
176 }
177 }
178
OnComplete(bool success,int return_code)179 void OnComplete(bool success, int return_code) {
180 // Log the error, but there's not much we can do.
181 if (!success) {
182 VLOG(1) << "Removal of cryptohome for " << user_email_
183 << " failed, return code: " << return_code;
184 }
185 delete this;
186 }
187
188 private:
189 std::string user_email_;
190 chromeos::RemoveUserDelegate* delegate_;
191
192 // Factory of callbacks.
193 ScopedRunnableMethodFactory<RemoveAttempt> method_factory_;
194
195 DISALLOW_COPY_AND_ASSIGN(RemoveAttempt);
196 };
197
198 } // namespace
199
User()200 UserManager::User::User() {
201 image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
202 kDefaultImageResources[0]);
203 }
204
~User()205 UserManager::User::~User() {}
206
GetDisplayName() const207 std::string UserManager::User::GetDisplayName() const {
208 size_t i = email_.find('@');
209 if (i == 0 || i == std::string::npos) {
210 return email_;
211 }
212 return email_.substr(0, i);
213 }
214
GetNameTooltip() const215 std::string UserManager::User::GetNameTooltip() const {
216 const std::string& user_email = email();
217 size_t at_pos = user_email.rfind('@');
218 if (at_pos == std::string::npos) {
219 NOTREACHED();
220 return std::string();
221 }
222 size_t domain_start = at_pos + 1;
223 std::string domain = user_email.substr(domain_start,
224 user_email.length() - domain_start);
225 return base::StringPrintf("%s (%s)",
226 GetDisplayName().c_str(),
227 domain.c_str());
228 }
229
230 // static
Get()231 UserManager* UserManager::Get() {
232 // Not thread-safe.
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234 return &g_user_manager.Get();
235 }
236
237 // static
RegisterPrefs(PrefService * local_state)238 void UserManager::RegisterPrefs(PrefService* local_state) {
239 local_state->RegisterListPref(kLoggedInUsers);
240 local_state->RegisterDictionaryPref(kUserImages);
241 }
242
GetUsers() const243 std::vector<UserManager::User> UserManager::GetUsers() const {
244 std::vector<User> users;
245 if (!g_browser_process)
246 return users;
247
248 PrefService* local_state = g_browser_process->local_state();
249 const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
250 const DictionaryValue* prefs_images = local_state->GetDictionary(kUserImages);
251
252 if (prefs_users) {
253 for (ListValue::const_iterator it = prefs_users->begin();
254 it != prefs_users->end();
255 ++it) {
256 std::string email;
257 if ((*it)->GetAsString(&email)) {
258 User user;
259 user.set_email(email);
260 UserImages::const_iterator image_it = user_images_.find(email);
261 std::string image_path;
262 if (image_it == user_images_.end()) {
263 if (prefs_images &&
264 prefs_images->GetStringWithoutPathExpansion(email, &image_path)) {
265 int default_image_id = kDefaultImagesCount;
266 if (IsDefaultImagePath(image_path, &default_image_id)) {
267 DCHECK(default_image_id < kDefaultImagesCount);
268 int resource_id = kDefaultImageResources[default_image_id];
269 user.set_image(
270 *ResourceBundle::GetSharedInstance().GetBitmapNamed(
271 resource_id));
272 user_images_[email] = user.image();
273 } else {
274 // Insert the default image so we don't send another request if
275 // GetUsers is called twice.
276 user_images_[email] = user.image();
277 image_loader_->Start(email, image_path, false);
278 }
279 }
280 } else {
281 user.set_image(image_it->second);
282 }
283 users.push_back(user);
284 }
285 }
286 }
287 return users;
288 }
289
OffTheRecordUserLoggedIn()290 void UserManager::OffTheRecordUserLoggedIn() {
291 user_is_logged_in_ = true;
292 logged_in_user_ = User();
293 logged_in_user_.set_email(kGuestUser);
294 NotifyOnLogin();
295 }
296
UserLoggedIn(const std::string & email)297 void UserManager::UserLoggedIn(const std::string& email) {
298 if (email == kGuestUser) {
299 OffTheRecordUserLoggedIn();
300 return;
301 }
302
303 if (!IsKnownUser(email)) {
304 current_user_is_new_ = true;
305 browser_defaults::skip_restore = true;
306 }
307
308 // Get a copy of the current users.
309 std::vector<User> users = GetUsers();
310
311 // Clear the prefs view of the users.
312 PrefService* prefs = g_browser_process->local_state();
313 ListPrefUpdate prefs_users_update(prefs, kLoggedInUsers);
314 prefs_users_update->Clear();
315
316 user_is_logged_in_ = true;
317 logged_in_user_ = User();
318 logged_in_user_.set_email(email);
319
320 // Make sure this user is first.
321 prefs_users_update->Append(Value::CreateStringValue(email));
322 for (std::vector<User>::iterator it = users.begin();
323 it != users.end();
324 ++it) {
325 std::string user_email = it->email();
326 // Skip the most recent user.
327 if (email != user_email) {
328 prefs_users_update->Append(Value::CreateStringValue(user_email));
329 } else {
330 logged_in_user_ = *it;
331 }
332 }
333 prefs->SavePersistentPrefs();
334 NotifyOnLogin();
335 if (current_user_is_new_)
336 SetDefaultUserImage(email);
337 }
338
RemoveUser(const std::string & email,RemoveUserDelegate * delegate)339 void UserManager::RemoveUser(const std::string& email,
340 RemoveUserDelegate* delegate) {
341 // Get a copy of the current users.
342 std::vector<User> users = GetUsers();
343
344 // Sanity check: we must not remove single user. This check may seem
345 // redundant at a first sight because this single user must be an owner and
346 // we perform special check later in order not to remove an owner. However
347 // due to non-instant nature of ownership assignment this later check may
348 // sometimes fail. See http://crosbug.com/12723
349 if (users.size() < 2)
350 return;
351
352 bool user_found = false;
353 for (size_t i = 0; !user_found && i < users.size(); ++i)
354 user_found = (email == users[i].email());
355 if (!user_found)
356 return;
357
358 // |RemoveAttempt| deletes itself when done.
359 new RemoveAttempt(email, delegate);
360 }
361
RemoveUserFromList(const std::string & email)362 void UserManager::RemoveUserFromList(const std::string& email) {
363 // Get a copy of the current users.
364 std::vector<User> users = GetUsers();
365
366 // Clear the prefs view of the users.
367 PrefService* prefs = g_browser_process->local_state();
368 ListPrefUpdate prefs_users_update(prefs, kLoggedInUsers);
369 prefs_users_update->Clear();
370
371 for (std::vector<User>::iterator it = users.begin();
372 it != users.end();
373 ++it) {
374 std::string user_email = it->email();
375 // Skip user that we would like to delete.
376 if (email != user_email)
377 prefs_users_update->Append(Value::CreateStringValue(user_email));
378 }
379
380 DictionaryPrefUpdate prefs_images_update(prefs, kUserImages);
381 std::string image_path_string;
382 prefs_images_update->GetStringWithoutPathExpansion(email, &image_path_string);
383 prefs_images_update->RemoveWithoutPathExpansion(email, NULL);
384
385 prefs->SavePersistentPrefs();
386
387 int default_image_id = kDefaultImagesCount;
388 if (!IsDefaultImagePath(image_path_string, &default_image_id)) {
389 FilePath image_path(image_path_string);
390 BrowserThread::PostTask(
391 BrowserThread::FILE,
392 FROM_HERE,
393 NewRunnableFunction(&DeleteUserImage,
394 image_path));
395 }
396 }
397
IsKnownUser(const std::string & email)398 bool UserManager::IsKnownUser(const std::string& email) {
399 std::vector<User> users = GetUsers();
400 for (std::vector<User>::iterator it = users.begin();
401 it < users.end();
402 ++it) {
403 if (it->email() == email)
404 return true;
405 }
406
407 return false;
408 }
409
logged_in_user() const410 const UserManager::User& UserManager::logged_in_user() const {
411 return logged_in_user_;
412 }
413
SetLoggedInUserImage(const SkBitmap & image)414 void UserManager::SetLoggedInUserImage(const SkBitmap& image) {
415 if (logged_in_user_.email().empty())
416 return;
417 OnImageLoaded(logged_in_user_.email(), image, false);
418 }
419
LoadLoggedInUserImage(const FilePath & path)420 void UserManager::LoadLoggedInUserImage(const FilePath& path) {
421 if (logged_in_user_.email().empty())
422 return;
423 image_loader_->Start(logged_in_user_.email(), path.value(), true);
424 }
425
SaveUserImage(const std::string & username,const SkBitmap & image)426 void UserManager::SaveUserImage(const std::string& username,
427 const SkBitmap& image) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429 FilePath image_path = GetImagePathForUser(username);
430 DVLOG(1) << "Saving user image to " << image_path.value();
431
432 BrowserThread::PostTask(
433 BrowserThread::FILE,
434 FROM_HERE,
435 NewRunnableFunction(&SaveImageToFile,
436 image, image_path, username));
437 }
438
SaveUserImagePath(const std::string & username,const std::string & image_path)439 void UserManager::SaveUserImagePath(const std::string& username,
440 const std::string& image_path) {
441 SavePathToLocalState(username, image_path);
442 }
443
SetDefaultUserImage(const std::string & username)444 void UserManager::SetDefaultUserImage(const std::string& username) {
445 if (!g_browser_process)
446 return;
447
448 PrefService* local_state = g_browser_process->local_state();
449 DCHECK(local_state);
450 const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
451 DCHECK(prefs_users);
452 const DictionaryValue* prefs_images =
453 local_state->GetDictionary(kUserImages);
454 DCHECK(prefs_images);
455
456 // We want to distribute default images between users uniformly so that if
457 // there're more users with red image, we won't add red one for sure.
458 // Thus we count how many default images of each color are used and choose
459 // the first color with minimal usage.
460 std::vector<int> colors_count(kDefaultImagesCount, 0);
461 for (ListValue::const_iterator it = prefs_users->begin();
462 it != prefs_users->end();
463 ++it) {
464 std::string email;
465 if ((*it)->GetAsString(&email)) {
466 std::string image_path;
467 int default_image_id = kDefaultImagesCount;
468 if (prefs_images->GetStringWithoutPathExpansion(email, &image_path) &&
469 IsDefaultImagePath(image_path, &default_image_id)) {
470 DCHECK(default_image_id < kDefaultImagesCount);
471 ++colors_count[default_image_id];
472 }
473 }
474 }
475 std::vector<int>::const_iterator min_it =
476 std::min_element(colors_count.begin(), colors_count.end());
477 int selected_id = min_it - colors_count.begin();
478 std::string user_image_path =
479 GetDefaultImagePath(selected_id);
480 int resource_id = kDefaultImageResources[selected_id];
481 SkBitmap user_image = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
482 resource_id);
483
484 SavePathToLocalState(username, user_image_path);
485 SetLoggedInUserImage(user_image);
486 }
487
GetUserDefaultImageIndex(const std::string & username)488 int UserManager::GetUserDefaultImageIndex(const std::string& username) {
489 if (!g_browser_process)
490 return -1;
491
492 PrefService* local_state = g_browser_process->local_state();
493 const DictionaryValue* prefs_images = local_state->GetDictionary(kUserImages);
494
495 if (!prefs_images)
496 return -1;
497
498 std::string image_path;
499 if (!prefs_images->GetStringWithoutPathExpansion(username, &image_path))
500 return -1;
501
502 int image_id = kDefaultImagesCount;
503 if (!IsDefaultImagePath(image_path, &image_id))
504 return -1;
505 return image_id;
506 }
507
OnImageLoaded(const std::string & username,const SkBitmap & image,bool should_save_image)508 void UserManager::OnImageLoaded(const std::string& username,
509 const SkBitmap& image,
510 bool should_save_image) {
511 DVLOG(1) << "Loaded image for " << username;
512 user_images_[username] = image;
513 User user;
514 user.set_email(username);
515 user.set_image(image);
516 if (logged_in_user_.email() == username)
517 logged_in_user_.set_image(image);
518 if (should_save_image)
519 SaveUserImage(username, image);
520 NotificationService::current()->Notify(
521 NotificationType::LOGIN_USER_IMAGE_CHANGED,
522 Source<UserManager>(this),
523 Details<const User>(&user));
524 }
525
IsLoggedInAsGuest() const526 bool UserManager::IsLoggedInAsGuest() const {
527 return logged_in_user().email() == kGuestUser;
528 }
529
530 // Private constructor and destructor. Do nothing.
UserManager()531 UserManager::UserManager()
532 : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))),
533 current_user_is_owner_(false),
534 current_user_is_new_(false),
535 user_is_logged_in_(false) {
536 registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
537 NotificationService::AllSources());
538 }
539
~UserManager()540 UserManager::~UserManager() {
541 image_loader_->set_delegate(NULL);
542 }
543
GetImagePathForUser(const std::string & username)544 FilePath UserManager::GetImagePathForUser(const std::string& username) {
545 std::string filename = username + ".png";
546 FilePath user_data_dir;
547 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
548 return user_data_dir.AppendASCII(filename);
549 }
550
551 class RealTPMTokenInfoDelegate : public crypto::TPMTokenInfoDelegate {
552 public:
553 RealTPMTokenInfoDelegate();
554 virtual ~RealTPMTokenInfoDelegate();
555 virtual bool IsTokenReady() const;
556 virtual void GetTokenInfo(std::string* token_name,
557 std::string* user_pin) const;
558 };
559
RealTPMTokenInfoDelegate()560 RealTPMTokenInfoDelegate::RealTPMTokenInfoDelegate() {}
~RealTPMTokenInfoDelegate()561 RealTPMTokenInfoDelegate::~RealTPMTokenInfoDelegate() {}
562
IsTokenReady() const563 bool RealTPMTokenInfoDelegate::IsTokenReady() const {
564 return CrosLibrary::Get()->GetCryptohomeLibrary()->Pkcs11IsTpmTokenReady();
565 }
566
GetTokenInfo(std::string * token_name,std::string * user_pin) const567 void RealTPMTokenInfoDelegate::GetTokenInfo(std::string* token_name,
568 std::string* user_pin) const {
569 std::string local_token_name;
570 std::string local_user_pin;
571 CrosLibrary::Get()->GetCryptohomeLibrary()->Pkcs11GetTpmTokenInfo(
572 &local_token_name, &local_user_pin);
573 if (token_name)
574 *token_name = local_token_name;
575 if (user_pin)
576 *user_pin = local_user_pin;
577 }
578
NotifyOnLogin()579 void UserManager::NotifyOnLogin() {
580 NotificationService::current()->Notify(
581 NotificationType::LOGIN_USER_CHANGED,
582 Source<UserManager>(this),
583 Details<const User>(&logged_in_user_));
584
585 chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
586 SetDeferImeStartup(false);
587 // Shut down the IME so that it will reload the user's settings.
588 chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
589 StopInputMethodDaemon();
590 // Let the window manager know that we're logged in now.
591 WmIpc::instance()->SetLoggedInProperty(true);
592 // Ensure we've opened the real user's key/certificate database.
593 crypto::OpenPersistentNSSDB();
594
595 // Only load the Opencryptoki library into NSS if we have this switch.
596 // TODO(gspencer): Remove this switch once cryptohomed work is finished:
597 // http://crosbug.com/12295 and http://crosbug.com/12304
598 if (CommandLine::ForCurrentProcess()->HasSwitch(
599 switches::kLoadOpencryptoki)) {
600 crypto::EnableTPMTokenForNSS(new RealTPMTokenInfoDelegate());
601 }
602
603 // Schedules current user ownership check on file thread.
604 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
605 NewRunnableFunction(&CheckOwnership));
606 }
607
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)608 void UserManager::Observe(NotificationType type,
609 const NotificationSource& source,
610 const NotificationDetails& details) {
611 if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
612 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
613 NewRunnableFunction(&CheckOwnership));
614 }
615 }
616
current_user_is_owner() const617 bool UserManager::current_user_is_owner() const {
618 base::AutoLock lk(current_user_is_owner_lock_);
619 return current_user_is_owner_;
620 }
621
set_current_user_is_owner(bool current_user_is_owner)622 void UserManager::set_current_user_is_owner(bool current_user_is_owner) {
623 base::AutoLock lk(current_user_is_owner_lock_);
624 current_user_is_owner_ = current_user_is_owner;
625 }
626
627 } // namespace chromeos
628