• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
46 using content::BrowserThread;
47 
48 namespace chromeos {
49 
50 namespace {
51 
52 // Time histogram suffix for profile image download.
53 const 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.
57 const int kSyncTimeoutSeconds = 10;
58 
59 }  // namespace
60 
61 // static
Get(ScreenManager * manager)62 UserImageScreen* UserImageScreen::Get(ScreenManager* manager) {
63   return static_cast<UserImageScreen*>(
64       manager->GetScreen(WizardController::kUserImageScreenName));
65 }
66 
UserImageScreen(ScreenObserver * screen_observer,UserImageScreenActor * actor)67 UserImageScreen::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 
~UserImageScreen()89 UserImageScreen::~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 
OnScreenReady()97 void UserImageScreen::OnScreenReady() {
98   is_screen_ready_ = true;
99   if (!IsWaitingForSync())
100     HideCurtain();
101 }
102 
OnPhotoTaken(const std::string & raw_data)103 void 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 
OnCameraPresenceCheckDone(bool is_camera_present)115 void UserImageScreen::OnCameraPresenceCheckDone(bool is_camera_present) {
116   if (actor_)
117     actor_->SetCameraPresent(is_camera_present);
118 }
119 
HideCurtain()120 void UserImageScreen::HideCurtain() {
121   if (actor_)
122     actor_->HideCurtain();
123 }
124 
OnImageDecoded(const ImageDecoder * decoder,const SkBitmap & decoded_image)125 void 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 
OnDecodeImageFailed(const ImageDecoder * decoder)133 void UserImageScreen::OnDecodeImageFailed(const ImageDecoder* decoder) {
134   NOTREACHED() << "Failed to decode PNG image from WebUI";
135 }
136 
OnInitialSync(bool local_image_updated)137 void 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 
OnSyncTimeout()149 void UserImageScreen::OnSyncTimeout() {
150   sync_timer_.reset();
151   GetSyncObserver()->RemoveObserver(this);
152   if (is_screen_ready_)
153     HideCurtain();
154 }
155 
IsWaitingForSync() const156 bool UserImageScreen::IsWaitingForSync() const {
157   return sync_timer_.get() && sync_timer_->IsRunning();
158 }
159 
OnUserImagePolicyChanged(const base::Value * previous,const base::Value * current)160 void 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 
OnImageSelected(const std::string & image_type,const std::string & image_url,bool is_user_selection)169 void 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 
OnImageAccepted()190 void 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 
PrepareToShow()224 void UserImageScreen::PrepareToShow() {
225   if (actor_)
226     actor_->PrepareToShow();
227 }
228 
GetUser()229 const user_manager::User* UserImageScreen::GetUser() {
230   return user_manager::UserManager::Get()->GetLoggedInUser();
231 }
232 
GetUserImageManager()233 UserImageManager* UserImageScreen::GetUserImageManager() {
234   return ChromeUserManager::Get()->GetUserImageManager(GetUser()->email());
235 }
236 
GetSyncObserver()237 UserImageSyncObserver* UserImageScreen::GetSyncObserver() {
238   return GetUserImageManager()->GetSyncObserver();
239 }
240 
Show()241 void 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 
Hide()299 void UserImageScreen::Hide() {
300   CameraPresenceNotifier::GetInstance()->RemoveObserver(this);
301   notification_registrar_.RemoveAll();
302   if (actor_)
303     actor_->Hide();
304 }
305 
GetName() const306 std::string UserImageScreen::GetName() const {
307   return WizardController::kUserImageScreenName;
308 }
309 
OnActorDestroyed(UserImageScreenActor * actor)310 void UserImageScreen::OnActorDestroyed(UserImageScreenActor* actor) {
311   if (actor_ == actor)
312     actor_ = NULL;
313 }
314 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)315 void 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 
profile_picture_absent()344 bool UserImageScreen::profile_picture_absent() {
345   return profile_picture_absent_;
346 }
347 
selected_image()348 int UserImageScreen::selected_image() {
349   return selected_image_;
350 }
351 
profile_picture_data_url()352 std::string UserImageScreen::profile_picture_data_url() {
353   return profile_picture_data_url_;
354 }
355 
ExitScreen()356 void 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