kiosk_mode_screensaver.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/kiosk_mode/kiosk_mode_screensaver.h" 6 7#include "ash/screensaver/screensaver_view.h" 8#include "ash/shell.h" 9#include "base/bind.h" 10#include "base/callback.h" 11#include "base/lazy_instance.h" 12#include "base/logging.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/chrome_notification_types.h" 15#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h" 16#include "chrome/browser/chromeos/login/existing_user_controller.h" 17#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h" 18#include "chrome/browser/chromeos/policy/app_pack_updater.h" 19#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" 20#include "chrome/browser/chromeos/profiles/profile_helper.h" 21#include "chrome/browser/extensions/extension_garbage_collector.h" 22#include "chrome/browser/extensions/extension_service.h" 23#include "chrome/browser/extensions/sandboxed_unpacker.h" 24#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" 25#include "chrome/common/extensions/manifest_handlers/app_launch_info.h" 26#include "chromeos/login/login_state.h" 27#include "content/public/browser/browser_thread.h" 28#include "content/public/browser/notification_service.h" 29#include "extensions/browser/extension_system.h" 30#include "extensions/common/extension.h" 31#include "extensions/common/file_util.h" 32#include "ui/wm/core/user_activity_detector.h" 33 34using extensions::Extension; 35using extensions::ExtensionGarbageCollector; 36using extensions::SandboxedUnpacker; 37 38namespace chromeos { 39 40namespace { 41 42ExtensionService* GetDefaultExtensionService() { 43 Profile* default_profile = ProfileHelper::GetSigninProfile(); 44 if (!default_profile) 45 return NULL; 46 return extensions::ExtensionSystem::Get( 47 default_profile)->extension_service(); 48} 49 50ExtensionGarbageCollector* GetDefaultExtensionGarbageCollector() { 51 Profile* default_profile = ProfileHelper::GetSigninProfile(); 52 if (!default_profile) 53 return NULL; 54 return ExtensionGarbageCollector::Get(default_profile); 55} 56 57typedef base::Callback<void( 58 scoped_refptr<Extension>, 59 const base::FilePath&)> UnpackCallback; 60 61class ScreensaverUnpackerClient 62 : public extensions::SandboxedUnpackerClient { 63 public: 64 ScreensaverUnpackerClient(const base::FilePath& crx_path, 65 const UnpackCallback& unpacker_callback) 66 : crx_path_(crx_path), 67 unpack_callback_(unpacker_callback) {} 68 69 virtual void OnUnpackSuccess(const base::FilePath& temp_dir, 70 const base::FilePath& extension_root, 71 const base::DictionaryValue* original_manifest, 72 const Extension* extension, 73 const SkBitmap& install_icon) OVERRIDE; 74 virtual void OnUnpackFailure(const base::string16& error) OVERRIDE; 75 76 protected: 77 virtual ~ScreensaverUnpackerClient() {} 78 79 private: 80 void LoadScreensaverExtension( 81 const base::FilePath& extension_base_path, 82 const base::FilePath& screensaver_extension_path); 83 84 void NotifyAppPackOfDamagedFile(); 85 86 base::FilePath crx_path_; 87 UnpackCallback unpack_callback_; 88 89 DISALLOW_COPY_AND_ASSIGN(ScreensaverUnpackerClient); 90}; 91 92void ScreensaverUnpackerClient::OnUnpackSuccess( 93 const base::FilePath& temp_dir, 94 const base::FilePath& extension_root, 95 const base::DictionaryValue* original_manifest, 96 const Extension* extension, 97 const SkBitmap& install_icon) { 98 content::BrowserThread::PostTask( 99 content::BrowserThread::FILE, 100 FROM_HERE, 101 base::Bind(&ScreensaverUnpackerClient::LoadScreensaverExtension, 102 this, 103 temp_dir, 104 extension_root)); 105} 106 107void ScreensaverUnpackerClient::OnUnpackFailure(const base::string16& error) { 108 LOG(ERROR) << "Couldn't unpack screensaver extension. Error: " << error; 109 NotifyAppPackOfDamagedFile(); 110} 111 112void ScreensaverUnpackerClient::LoadScreensaverExtension( 113 const base::FilePath& extension_base_path, 114 const base::FilePath& screensaver_extension_path) { 115 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); 116 117 // TODO(rkc): This is a HACK, please remove this method from extension 118 // service once this code is deprecated. See crbug.com/280363 119 ExtensionGarbageCollector* gc = GetDefaultExtensionGarbageCollector(); 120 if (gc) 121 gc->disable_garbage_collection(); 122 123 std::string error; 124 scoped_refptr<Extension> screensaver_extension = 125 extensions::file_util::LoadExtension(screensaver_extension_path, 126 extensions::Manifest::COMPONENT, 127 Extension::NO_FLAGS, 128 &error); 129 if (!screensaver_extension.get()) { 130 LOG(ERROR) << "Could not load screensaver extension from: " 131 << screensaver_extension_path.value() << " due to: " << error; 132 NotifyAppPackOfDamagedFile(); 133 return; 134 } 135 136 content::BrowserThread::PostTask( 137 content::BrowserThread::UI, 138 FROM_HERE, 139 base::Bind( 140 unpack_callback_, 141 screensaver_extension, 142 extension_base_path)); 143} 144 145void ScreensaverUnpackerClient::NotifyAppPackOfDamagedFile() { 146 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { 147 content::BrowserThread::PostTask( 148 content::BrowserThread::UI, FROM_HERE, 149 base::Bind(&ScreensaverUnpackerClient::NotifyAppPackOfDamagedFile, 150 this)); 151 return; 152 } 153 154 policy::BrowserPolicyConnectorChromeOS* connector = 155 g_browser_process->platform_part()->browser_policy_connector_chromeos(); 156 policy::AppPackUpdater* updater = connector->GetAppPackUpdater(); 157 if (updater) 158 updater->OnDamagedFileDetected(crx_path_); 159} 160 161} // namespace 162 163KioskModeScreensaver::KioskModeScreensaver() 164 : weak_ptr_factory_(this) { 165 chromeos::KioskModeSettings* kiosk_mode_settings = 166 chromeos::KioskModeSettings::Get(); 167 168 if (kiosk_mode_settings->is_initialized()) { 169 GetScreensaverCrxPath(); 170 } else { 171 kiosk_mode_settings->Initialize(base::Bind( 172 &KioskModeScreensaver::GetScreensaverCrxPath, 173 weak_ptr_factory_.GetWeakPtr())); 174 } 175} 176 177KioskModeScreensaver::~KioskModeScreensaver() { 178 // If we are shutting down the system might already be gone and we shouldn't 179 // do anything (see crbug.com/288216). 180 if (!g_browser_process || g_browser_process->IsShuttingDown()) 181 return; 182 183 // If the extension was unpacked. 184 if (!extension_base_path_.empty()) { 185 // TODO(rkc): This is a HACK, please remove this method from extension 186 // service once this code is deprecated. See crbug.com/280363 187 ExtensionGarbageCollector* gc = GetDefaultExtensionGarbageCollector(); 188 if (gc) 189 gc->enable_garbage_collection(); 190 191 // Delete it. 192 content::BrowserThread::PostTask( 193 content::BrowserThread::FILE, 194 FROM_HERE, 195 base::Bind( 196 &extensions::file_util::DeleteFile, extension_base_path_, true)); 197 } 198 199 // In case we're shutting down without ever triggering the active 200 // notification and/or logging in. 201 if (ash::Shell::GetInstance() && 202 ash::Shell::GetInstance()->user_activity_detector() && 203 ash::Shell::GetInstance()->user_activity_detector()->HasObserver(this)) 204 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this); 205} 206 207void KioskModeScreensaver::GetScreensaverCrxPath() { 208 chromeos::KioskModeSettings::Get()->GetScreensaverPath( 209 base::Bind(&KioskModeScreensaver::ScreensaverPathCallback, 210 weak_ptr_factory_.GetWeakPtr())); 211} 212 213void KioskModeScreensaver::ScreensaverPathCallback( 214 const base::FilePath& screensaver_crx) { 215 if (screensaver_crx.empty()) 216 return; 217 218 ExtensionService* extension_service = GetDefaultExtensionService(); 219 if (!extension_service) 220 return; 221 base::FilePath extensions_dir = extension_service->install_directory(); 222 scoped_refptr<SandboxedUnpacker> screensaver_unpacker( 223 new SandboxedUnpacker( 224 screensaver_crx, 225 extensions::Manifest::COMPONENT, 226 Extension::NO_FLAGS, 227 extensions_dir, 228 content::BrowserThread::GetMessageLoopProxyForThread( 229 content::BrowserThread::FILE).get(), 230 new ScreensaverUnpackerClient( 231 screensaver_crx, 232 base::Bind( 233 &KioskModeScreensaver::SetupScreensaver, 234 weak_ptr_factory_.GetWeakPtr())))); 235 236 // Fire off the unpacker on the file thread; don't need it to return. 237 content::BrowserThread::PostTask( 238 content::BrowserThread::FILE, 239 FROM_HERE, 240 base::Bind( 241 &SandboxedUnpacker::Start, screensaver_unpacker.get())); 242} 243 244void KioskModeScreensaver::SetupScreensaver( 245 scoped_refptr<Extension> extension, 246 const base::FilePath& extension_base_path) { 247 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 248 extension_base_path_ = extension_base_path; 249 250 // If the user is already logged in, don't need to display the screensaver. 251 if (chromeos::LoginState::Get()->IsUserLoggedIn()) 252 return; 253 254 ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this); 255 256 ExtensionService* extension_service = GetDefaultExtensionService(); 257 // Add the extension to the extension service and display the screensaver. 258 if (extension_service) { 259 extension_service->AddExtension(extension.get()); 260 ash::ShowScreensaver( 261 extensions::AppLaunchInfo::GetFullLaunchURL(extension.get())); 262 } else { 263 LOG(ERROR) << "Couldn't get extension system. Unable to load screensaver!"; 264 ShutdownKioskModeScreensaver(); 265 } 266} 267 268void KioskModeScreensaver::OnUserActivity(const ui::Event* event) { 269 // We don't want to handle further user notifications; we'll either login 270 // the user and close out or or at least close the screensaver. 271 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this); 272 273 // Find the retail mode login page. 274 if (LoginDisplayHostImpl::default_host()) { 275 LoginDisplayHostImpl* webui_host = 276 static_cast<LoginDisplayHostImpl*>( 277 LoginDisplayHostImpl::default_host()); 278 OobeUI* oobe_ui = webui_host->GetOobeUI(); 279 280 // Show the login spinner. 281 if (oobe_ui) 282 oobe_ui->ShowRetailModeLoginSpinner(); 283 284 // Close the screensaver, our login spinner is already showing. 285 ash::CloseScreensaver(); 286 287 // Log us in. 288 ExistingUserController* controller = 289 ExistingUserController::current_controller(); 290 if (controller && !chromeos::LoginState::Get()->IsUserLoggedIn()) 291 controller->LoginAsRetailModeUser(); 292 } else { 293 // No default host for the WebUiLoginDisplay means that we're already in the 294 // process of logging in - shut down screensaver and do nothing else. 295 ash::CloseScreensaver(); 296 } 297 298 ShutdownKioskModeScreensaver(); 299} 300 301static KioskModeScreensaver* g_kiosk_mode_screensaver = NULL; 302 303void InitializeKioskModeScreensaver() { 304 if (g_kiosk_mode_screensaver) { 305 LOG(WARNING) << "Screensaver was already initialized"; 306 return; 307 } 308 309 g_kiosk_mode_screensaver = new KioskModeScreensaver(); 310} 311 312void ShutdownKioskModeScreensaver() { 313 delete g_kiosk_mode_screensaver; 314 g_kiosk_mode_screensaver = NULL; 315} 316 317} // namespace chromeos 318