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