user_image_screen.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright (c) 2012 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/screens/user_image_screen.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/compiler_specific.h"
12#include "base/location.h"
13#include "base/logging.h"
14#include "base/message_loop/message_loop_proxy.h"
15#include "base/metrics/histogram.h"
16#include "base/timer/timer.h"
17#include "base/values.h"
18#include "chrome/browser/chrome_notification_types.h"
19#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
20#include "chrome/browser/chromeos/camera_presence_notifier.h"
21#include "chrome/browser/chromeos/login/login_utils.h"
22#include "chrome/browser/chromeos/login/screen_manager.h"
23#include "chrome/browser/chromeos/login/screens/screen_observer.h"
24#include "chrome/browser/chromeos/login/users/avatar/user_image_manager.h"
25#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
26#include "chrome/browser/chromeos/login/wizard_controller.h"
27#include "chrome/browser/chromeos/profiles/profile_helper.h"
28#include "chrome/browser/policy/profile_policy_connector.h"
29#include "chrome/browser/policy/profile_policy_connector_factory.h"
30#include "chrome/browser/profiles/profile.h"
31#include "chrome/common/url_constants.h"
32#include "components/policy/core/common/policy_map.h"
33#include "components/policy/core/common/policy_namespace.h"
34#include "components/policy/core/common/policy_service.h"
35#include "components/user_manager/user.h"
36#include "components/user_manager/user_image/default_user_images.h"
37#include "components/user_manager/user_image/user_image.h"
38#include "components/user_manager/user_manager.h"
39#include "content/public/browser/browser_thread.h"
40#include "content/public/browser/notification_service.h"
41#include "policy/policy_constants.h"
42#include "third_party/skia/include/core/SkBitmap.h"
43#include "ui/base/webui/web_ui_util.h"
44#include "ui/gfx/image/image_skia.h"
45
46using content::BrowserThread;
47
48namespace chromeos {
49
50namespace {
51
52// Time histogram suffix for profile image download.
53const char kProfileDownloadReason[] = "OOBE";
54
55// Maximum amount of time to wait for the user image to sync.
56// The screen is shown iff sync failed or time limit exceeded.
57const int kSyncTimeoutSeconds = 10;
58
59}  // namespace
60
61// static
62UserImageScreen* UserImageScreen::Get(ScreenManager* manager) {
63  return static_cast<UserImageScreen*>(
64      manager->GetScreen(WizardController::kUserImageScreenName));
65}
66
67UserImageScreen::UserImageScreen(ScreenObserver* screen_observer,
68                                 UserImageScreenActor* actor)
69    : WizardScreen(screen_observer),
70      actor_(actor),
71      accept_photo_after_decoding_(false),
72      selected_image_(user_manager::User::USER_IMAGE_INVALID),
73      profile_picture_data_url_(url::kAboutBlankURL),
74      profile_picture_absent_(false),
75      is_screen_ready_(false),
76      user_has_selected_image_(false) {
77  actor_->SetDelegate(this);
78  notification_registrar_.Add(this,
79                              chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED,
80                              content::NotificationService::AllSources());
81  notification_registrar_.Add(this,
82                              chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED,
83                              content::NotificationService::AllSources());
84  notification_registrar_.Add(this,
85                              chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
86                              content::NotificationService::AllSources());
87}
88
89UserImageScreen::~UserImageScreen() {
90  CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
91  if (actor_)
92    actor_->SetDelegate(NULL);
93  if (image_decoder_.get())
94    image_decoder_->set_delegate(NULL);
95}
96
97void UserImageScreen::OnScreenReady() {
98  is_screen_ready_ = true;
99  if (!IsWaitingForSync())
100    HideCurtain();
101}
102
103void UserImageScreen::OnPhotoTaken(const std::string& raw_data) {
104  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105  user_photo_ = gfx::ImageSkia();
106  if (image_decoder_.get())
107    image_decoder_->set_delegate(NULL);
108  image_decoder_ = new ImageDecoder(this, raw_data,
109                                    ImageDecoder::DEFAULT_CODEC);
110  scoped_refptr<base::MessageLoopProxy> task_runner =
111      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
112  image_decoder_->Start(task_runner);
113}
114
115void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) {
116  if (actor_)
117    actor_->SetCameraPresent(is_camera_present);
118}
119
120void UserImageScreen::HideCurtain() {
121  if (actor_)
122    actor_->HideCurtain();
123}
124
125void UserImageScreen::OnImageDecoded(const ImageDecoder* decoder,
126                                     const SkBitmap& decoded_image) {
127  DCHECK_EQ(image_decoder_.get(), decoder);
128  user_photo_ = gfx::ImageSkia::CreateFrom1xBitmap(decoded_image);
129  if (accept_photo_after_decoding_)
130    OnImageAccepted();
131}
132
133void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
134  NOTREACHED() << "Failed to decode PNG image from WebUI";
135}
136
137void UserImageScreen::OnInitialSync(bool local_image_updated) {
138  DCHECK(sync_timer_);
139  if (!local_image_updated) {
140    sync_timer_.reset();
141    GetSyncObserver()->RemoveObserver(this);
142    if (is_screen_ready_)
143      HideCurtain();
144    return;
145  }
146  ExitScreen();
147}
148
149void UserImageScreen::OnSyncTimeout() {
150  sync_timer_.reset();
151  GetSyncObserver()->RemoveObserver(this);
152  if (is_screen_ready_)
153    HideCurtain();
154}
155
156bool UserImageScreen::IsWaitingForSync() const {
157  return sync_timer_.get() && sync_timer_->IsRunning();
158}
159
160void UserImageScreen::OnUserImagePolicyChanged(const base::Value* previous,
161                                               const base::Value* current) {
162  if (current) {
163    base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE,
164                                                  policy_registrar_.release());
165    ExitScreen();
166  }
167}
168
169void UserImageScreen::OnImageSelected(const std::string& image_type,
170                                      const std::string& image_url,
171                                      bool is_user_selection) {
172  if (is_user_selection) {
173    user_has_selected_image_ = true;
174  }
175  if (image_url.empty())
176    return;
177  int user_image_index = user_manager::User::USER_IMAGE_INVALID;
178  if (image_type == "default" &&
179      user_manager::IsDefaultImageUrl(image_url, &user_image_index)) {
180    selected_image_ = user_image_index;
181  } else if (image_type == "camera") {
182    selected_image_ = user_manager::User::USER_IMAGE_EXTERNAL;
183  } else if (image_type == "profile") {
184    selected_image_ = user_manager::User::USER_IMAGE_PROFILE;
185  } else {
186    NOTREACHED() << "Unexpected image type: " << image_type;
187  }
188}
189
190void UserImageScreen::OnImageAccepted() {
191  UserImageManager* image_manager = GetUserImageManager();
192  int uma_index = 0;
193  switch (selected_image_) {
194    case user_manager::User::USER_IMAGE_EXTERNAL:
195      // Photo decoding may not have been finished yet.
196      if (user_photo_.isNull()) {
197        accept_photo_after_decoding_ = true;
198        return;
199      }
200      image_manager->SaveUserImage(
201          user_manager::UserImage::CreateAndEncode(user_photo_));
202      uma_index = user_manager::kHistogramImageFromCamera;
203      break;
204    case user_manager::User::USER_IMAGE_PROFILE:
205      image_manager->SaveUserImageFromProfileImage();
206      uma_index = user_manager::kHistogramImageFromProfile;
207      break;
208    default:
209      DCHECK(selected_image_ >= 0 &&
210             selected_image_ < user_manager::kDefaultImagesCount);
211      image_manager->SaveUserDefaultImageIndex(selected_image_);
212      uma_index = user_manager::GetDefaultImageHistogramValue(selected_image_);
213      break;
214  }
215  if (user_has_selected_image_) {
216    UMA_HISTOGRAM_ENUMERATION("UserImage.FirstTimeChoice",
217                              uma_index,
218                              user_manager::kHistogramImagesCount);
219  }
220  ExitScreen();
221}
222
223
224void UserImageScreen::PrepareToShow() {
225  if (actor_)
226    actor_->PrepareToShow();
227}
228
229const user_manager::User* UserImageScreen::GetUser() {
230  return user_manager::UserManager::Get()->GetLoggedInUser();
231}
232
233UserImageManager* UserImageScreen::GetUserImageManager() {
234  return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email());
235}
236
237UserImageSyncObserver* UserImageScreen::GetSyncObserver() {
238  return GetUserImageManager()->GetSyncObserver();
239}
240
241void UserImageScreen::Show() {
242  if (!actor_)
243    return;
244
245  DCHECK(!policy_registrar_);
246  if (Profile* profile = ProfileHelper::Get()->GetProfileByUser(GetUser())) {
247    policy::PolicyService* policy_service =
248        policy::ProfilePolicyConnectorFactory::GetForProfile(profile)->
249            policy_service();
250    if (policy_service->GetPolicies(
251            policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME,
252                                    std::string()))
253            .Get(policy::key::kUserAvatarImage)) {
254      // If the user image is managed by policy, skip the screen because the
255      // user is not allowed to override a policy-set image.
256      ExitScreen();
257      return;
258    }
259
260    // Listen for policy changes. If at any point, the user image becomes
261    // managed by policy, the screen will close.
262    policy_registrar_.reset(new policy::PolicyChangeRegistrar(
263        policy_service,
264        policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
265    policy_registrar_->Observe(
266        policy::key::kUserAvatarImage,
267        base::Bind(&UserImageScreen::OnUserImagePolicyChanged,
268                   base::Unretained(this)));
269  } else {
270    NOTREACHED();
271  }
272
273  if (GetUser()->CanSyncImage()) {
274    if (UserImageSyncObserver* sync_observer = GetSyncObserver()) {
275      // We have synced image already.
276      if (sync_observer->is_synced()) {
277        ExitScreen();
278        return;
279      }
280      sync_observer->AddObserver(this);
281      sync_timer_.reset(new base::Timer(
282            FROM_HERE,
283            base::TimeDelta::FromSeconds(kSyncTimeoutSeconds),
284            base::Bind(&UserImageScreen::OnSyncTimeout, base::Unretained(this)),
285            false));
286      sync_timer_->Reset();
287    }
288  }
289  CameraPresenceNotifier::GetInstance()->AddObserver(this);
290  actor_->Show();
291
292  selected_image_ = GetUser()->image_index();
293  actor_->SelectImage(selected_image_);
294
295  // Start fetching the profile image.
296  GetUserImageManager()->DownloadProfileImage(kProfileDownloadReason);
297}
298
299void UserImageScreen::Hide() {
300  CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
301  notification_registrar_.RemoveAll();
302  if (actor_)
303    actor_->Hide();
304}
305
306std::string UserImageScreen::GetName() const {
307  return WizardController::kUserImageScreenName;
308}
309
310void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
311  if (actor_ == actor)
312    actor_ = NULL;
313}
314
315void UserImageScreen::Observe(int type,
316                              const content::NotificationSource& source,
317                              const content::NotificationDetails& details) {
318  switch (type) {
319    case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATED: {
320      // We've got a new profile image.
321      profile_picture_data_url_ = webui::GetBitmapDataUrl(
322          *content::Details<const gfx::ImageSkia>(details).ptr()->bitmap());
323      if (actor_)
324        actor_->SendProfileImage(profile_picture_data_url_);
325      break;
326    }
327    case chrome::NOTIFICATION_PROFILE_IMAGE_UPDATE_FAILED: {
328      // User has a default profile image or fetching profile image has failed.
329      profile_picture_absent_ = true;
330      if (actor_)
331        actor_->OnProfileImageAbsent();
332      break;
333    }
334    case chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED: {
335      if (actor_)
336        actor_->SelectImage(GetUser()->image_index());
337      break;
338    }
339    default:
340      NOTREACHED();
341  }
342}
343
344bool UserImageScreen::profile_picture_absent() {
345  return profile_picture_absent_;
346}
347
348int UserImageScreen::selected_image() {
349  return selected_image_;
350}
351
352std::string UserImageScreen::profile_picture_data_url() {
353  return profile_picture_data_url_;
354}
355
356void UserImageScreen::ExitScreen() {
357  policy_registrar_.reset();
358  sync_timer_.reset();
359  if (UserImageSyncObserver* sync_observer = GetSyncObserver())
360    sync_observer->RemoveObserver(this);
361  get_screen_observer()->OnExit(ScreenObserver::USER_IMAGE_SELECTED);
362}
363
364}  // namespace chromeos
365