user_manager.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 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 "app/resource_bundle.h"
8#include "base/compiler_specific.h"
9#include "base/file_path.h"
10#include "base/file_util.h"
11#include "base/logging.h"
12#include "base/nss_util.h"
13#include "base/path_service.h"
14#include "base/string_util.h"
15#include "base/time.h"
16#include "base/utf_string_conversions.h"
17#include "base/values.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/chrome_thread.h"
20#include "chrome/browser/chromeos/cros/cros_library.h"
21#include "chrome/browser/chromeos/cros/input_method_library.h"
22#include "chrome/browser/chromeos/login/ownership_service.h"
23#include "chrome/browser/chromeos/wm_ipc.h"
24#include "chrome/browser/prefs/pref_service.h"
25#include "chrome/common/chrome_paths.h"
26#include "chrome/common/notification_service.h"
27#include "chrome/common/notification_type.h"
28#include "gfx/codec/png_codec.h"
29#include "grit/theme_resources.h"
30
31namespace chromeos {
32
33namespace {
34
35// A vector pref of the users who have logged into the device.
36const char kLoggedInUsers[] = "LoggedInUsers";
37// A dictionary that maps usernames to file paths to their images.
38const char kUserImages[] = "UserImages";
39
40// Incognito user is represented by an empty string (since some code already
41// depends on that and it's hard to figure out what).
42const char kIncognitoUser[] = "";
43
44// The one true UserManager.
45static UserManager* user_manager_ = NULL;
46
47// Stores path to the image in local state. Runs on UI thread.
48void save_path_to_local_state(const std::string& username,
49                              const std::string& image_path) {
50  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
51  PrefService* local_state = g_browser_process->local_state();
52  DictionaryValue* images =
53      local_state->GetMutableDictionary(kUserImages);
54  images->SetWithoutPathExpansion(username, new StringValue(image_path));
55  LOG(INFO) << "Saving path to user image in Local State.";
56  local_state->SavePersistentPrefs();
57}
58
59// Saves image to file with specified path. Runs on FILE thread.
60// Posts task for saving image path to local state on UI thread.
61void save_image_to_file(const SkBitmap& image,
62                        const FilePath& image_path,
63                        const std::string& username) {
64  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
65  std::vector<unsigned char> encoded_image;
66  if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &encoded_image)) {
67    LOG(ERROR) << "Failed to PNG encode the image.";
68    return;
69  }
70
71  if (file_util::WriteFile(image_path,
72                           reinterpret_cast<char*>(&encoded_image[0]),
73                           encoded_image.size()) == -1) {
74    LOG(ERROR) << "Failed to save image to file.";
75    return;
76  }
77
78  ChromeThread::PostTask(
79      ChromeThread::UI,
80      FROM_HERE,
81      NewRunnableFunction(&save_path_to_local_state,
82                          username, image_path.value()));
83}
84
85// Checks current user's ownership on file thread.
86void CheckOwnership() {
87  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
88
89  UserManager::Get()->set_current_user_is_owner(
90      OwnershipService::GetSharedInstance()->CurrentUserIsOwner());
91}
92
93}  // namespace
94
95UserManager::User::User() {
96  image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
97      IDR_LOGIN_DEFAULT_USER);
98}
99
100std::string UserManager::User::GetDisplayName() const {
101  size_t i = email_.find('@');
102  if (i == 0 || i == std::string::npos) {
103    return email_;
104  }
105  return email_.substr(0, i);
106}
107
108// static
109UserManager* UserManager::Get() {
110  if (!user_manager_)
111    user_manager_ = new UserManager();
112  return user_manager_;
113}
114
115// static
116void UserManager::RegisterPrefs(PrefService* local_state) {
117  local_state->RegisterListPref(kLoggedInUsers);
118  local_state->RegisterDictionaryPref(kUserImages);
119}
120
121std::vector<UserManager::User> UserManager::GetUsers() const {
122  std::vector<User> users;
123  if (!g_browser_process)
124    return users;
125
126  PrefService* local_state = g_browser_process->local_state();
127  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
128  const DictionaryValue* prefs_images =
129      local_state->GetDictionary(kUserImages);
130
131  if (prefs_users) {
132    for (ListValue::const_iterator it = prefs_users->begin();
133         it < prefs_users->end();
134         ++it) {
135      std::string email;
136      if ((*it)->GetAsString(&email)) {
137        User user;
138        user.set_email(email);
139        UserImages::const_iterator image_it = user_images_.find(email);
140        std::string image_path;
141        if (image_it == user_images_.end()) {
142          if (prefs_images &&
143              prefs_images->GetStringWithoutPathExpansion(email, &image_path)) {
144            // Insert the default image so we don't send another request if
145            // GetUsers is called twice.
146            user_images_[email] = user.image();
147            image_loader_->Start(email, image_path);
148          }
149        } else {
150          user.set_image(image_it->second);
151        }
152        users.push_back(user);
153      }
154    }
155  }
156  return users;
157}
158
159void UserManager::OffTheRecordUserLoggedIn() {
160  logged_in_user_ = User();
161  logged_in_user_.set_email(kIncognitoUser);
162  NotifyOnLogin();
163}
164
165void UserManager::UserLoggedIn(const std::string& email) {
166  if (email == kIncognitoUser) {
167    OffTheRecordUserLoggedIn();
168    return;
169  }
170
171  // Get a copy of the current users.
172  std::vector<User> users = GetUsers();
173
174  // Clear the prefs view of the users.
175  PrefService* prefs = g_browser_process->local_state();
176  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
177  prefs_users->Clear();
178
179  logged_in_user_.set_email(email);
180
181  // Make sure this user is first.
182  prefs_users->Append(Value::CreateStringValue(email));
183  for (std::vector<User>::iterator it = users.begin();
184       it < users.end();
185       ++it) {
186    std::string user_email = it->email();
187    // Skip the most recent user.
188    if (email != user_email) {
189      prefs_users->Append(Value::CreateStringValue(user_email));
190    } else {
191      logged_in_user_ = *it;
192    }
193  }
194  prefs->SavePersistentPrefs();
195  NotifyOnLogin();
196}
197
198void UserManager::RemoveUser(const std::string& email) {
199  // Get a copy of the current users.
200  std::vector<User> users = GetUsers();
201
202  // Clear the prefs view of the users.
203  PrefService* prefs = g_browser_process->local_state();
204  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
205  prefs_users->Clear();
206
207  for (std::vector<User>::iterator it = users.begin();
208       it < users.end();
209       ++it) {
210    std::string user_email = it->email();
211    // Skip user that we would like to delete.
212    if (email != user_email)
213      prefs_users->Append(Value::CreateStringValue(user_email));
214  }
215  prefs->SavePersistentPrefs();
216}
217
218bool UserManager::IsKnownUser(const std::string& email) {
219  std::vector<User> users = GetUsers();
220  for (std::vector<User>::iterator it = users.begin();
221       it < users.end();
222       ++it) {
223    if (it->email() == email)
224      return true;
225  }
226
227  return false;
228}
229
230void UserManager::SetLoggedInUserImage(const SkBitmap& image) {
231  if (logged_in_user_.email().empty())
232    return;
233  logged_in_user_.set_image(image);
234  OnImageLoaded(logged_in_user_.email(), image);
235}
236
237void UserManager::SaveUserImage(const std::string& username,
238                                const SkBitmap& image) {
239  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
240  std::string filename = username + ".png";
241  FilePath user_data_dir;
242  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
243  FilePath image_path = user_data_dir.AppendASCII(filename);
244  LOG(INFO) << "Saving user image to " << image_path.value();
245
246  ChromeThread::PostTask(
247      ChromeThread::FILE,
248      FROM_HERE,
249      NewRunnableFunction(&save_image_to_file,
250                          image, image_path, username));
251}
252
253void UserManager::OnImageLoaded(const std::string& username,
254                                const SkBitmap& image) {
255  LOG(INFO) << "Loaded image for " << username;
256  user_images_[username] = image;
257  User user;
258  user.set_email(username);
259  user.set_image(image);
260  NotificationService::current()->Notify(
261      NotificationType::LOGIN_USER_IMAGE_CHANGED,
262      Source<UserManager>(this),
263      Details<const User>(&user));
264}
265
266// Private constructor and destructor. Do nothing.
267UserManager::UserManager()
268    : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))),
269      current_user_is_owner_(false) {
270  registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
271      NotificationService::AllSources());
272}
273
274UserManager::~UserManager() {
275  image_loader_->set_delegate(NULL);
276}
277
278void UserManager::NotifyOnLogin() {
279  NotificationService::current()->Notify(
280      NotificationType::LOGIN_USER_CHANGED,
281      Source<UserManager>(this),
282      Details<const User>(&logged_in_user_));
283
284  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
285      SetDeferImeStartup(false);
286  // Shut down the IME so that it will reload the user's settings.
287  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
288      StopInputMethodProcesses();
289  // Let the window manager know that we're logged in now.
290  WmIpc::instance()->SetLoggedInProperty(true);
291  // Ensure we've opened the real user's key/certificate database.
292  base::OpenPersistentNSSDB();
293
294  // Schedules current user ownership check on file thread.
295  ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
296      NewRunnableFunction(&CheckOwnership));
297}
298
299void UserManager::Observe(NotificationType type,
300                          const NotificationSource& source,
301                          const NotificationDetails& details) {
302  if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
303    ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
304        NewRunnableFunction(&CheckOwnership));
305  }
306}
307
308}  // namespace chromeos
309