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_chromeos.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::ExtensionGarbageCollectorChromeOS; 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 50ExtensionGarbageCollectorChromeOS* GetDefaultExtensionGarbageCollector() { 51 Profile* default_profile = ProfileHelper::GetSigninProfile(); 52 if (!default_profile) 53 return NULL; 54 return ExtensionGarbageCollectorChromeOS::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 ExtensionGarbageCollectorChromeOS* 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 ExtensionGarbageCollectorChromeOS* gc = 188 GetDefaultExtensionGarbageCollector(); 189 if (gc) 190 gc->enable_garbage_collection(); 191 192 // Delete it. 193 content::BrowserThread::PostTask( 194 content::BrowserThread::FILE, 195 FROM_HERE, 196 base::Bind( 197 &extensions::file_util::DeleteFile, extension_base_path_, true)); 198 } 199 200 // In case we're shutting down without ever triggering the active 201 // notification and/or logging in. 202 if (ash::Shell::GetInstance() && 203 ash::Shell::GetInstance()->user_activity_detector() && 204 ash::Shell::GetInstance()->user_activity_detector()->HasObserver(this)) 205 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this); 206} 207 208void KioskModeScreensaver::GetScreensaverCrxPath() { 209 chromeos::KioskModeSettings::Get()->GetScreensaverPath( 210 base::Bind(&KioskModeScreensaver::ScreensaverPathCallback, 211 weak_ptr_factory_.GetWeakPtr())); 212} 213 214void KioskModeScreensaver::ScreensaverPathCallback( 215 const base::FilePath& screensaver_crx) { 216 if (screensaver_crx.empty()) 217 return; 218 219 ExtensionService* extension_service = GetDefaultExtensionService(); 220 if (!extension_service) 221 return; 222 base::FilePath extensions_dir = extension_service->install_directory(); 223 scoped_refptr<SandboxedUnpacker> screensaver_unpacker( 224 new SandboxedUnpacker( 225 screensaver_crx, 226 extensions::Manifest::COMPONENT, 227 Extension::NO_FLAGS, 228 extensions_dir, 229 content::BrowserThread::GetMessageLoopProxyForThread( 230 content::BrowserThread::FILE).get(), 231 new ScreensaverUnpackerClient( 232 screensaver_crx, 233 base::Bind( 234 &KioskModeScreensaver::SetupScreensaver, 235 weak_ptr_factory_.GetWeakPtr())))); 236 237 // Fire off the unpacker on the file thread; don't need it to return. 238 content::BrowserThread::PostTask( 239 content::BrowserThread::FILE, 240 FROM_HERE, 241 base::Bind( 242 &SandboxedUnpacker::Start, screensaver_unpacker.get())); 243} 244 245void KioskModeScreensaver::SetupScreensaver( 246 scoped_refptr<Extension> extension, 247 const base::FilePath& extension_base_path) { 248 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 249 extension_base_path_ = extension_base_path; 250 251 // If the user is already logged in, don't need to display the screensaver. 252 if (chromeos::LoginState::Get()->IsUserLoggedIn()) 253 return; 254 255 ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this); 256 257 ExtensionService* extension_service = GetDefaultExtensionService(); 258 // Add the extension to the extension service and display the screensaver. 259 if (extension_service) { 260 extension_service->AddExtension(extension.get()); 261 ash::ShowScreensaver( 262 extensions::AppLaunchInfo::GetFullLaunchURL(extension.get())); 263 } else { 264 LOG(ERROR) << "Couldn't get extension system. Unable to load screensaver!"; 265 ShutdownKioskModeScreensaver(); 266 } 267} 268 269void KioskModeScreensaver::OnUserActivity(const ui::Event* event) { 270 // We don't want to handle further user notifications; we'll either login 271 // the user and close out or or at least close the screensaver. 272 ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this); 273 274 // Find the retail mode login page. 275 if (LoginDisplayHostImpl::default_host()) { 276 LoginDisplayHostImpl* webui_host = 277 static_cast<LoginDisplayHostImpl*>( 278 LoginDisplayHostImpl::default_host()); 279 OobeUI* oobe_ui = webui_host->GetOobeUI(); 280 281 // Show the login spinner. 282 if (oobe_ui) 283 oobe_ui->ShowRetailModeLoginSpinner(); 284 285 // Close the screensaver, our login spinner is already showing. 286 ash::CloseScreensaver(); 287 288 // Log us in. 289 ExistingUserController* controller = 290 ExistingUserController::current_controller(); 291 if (controller && !chromeos::LoginState::Get()->IsUserLoggedIn()) 292 controller->LoginAsRetailModeUser(); 293 } else { 294 // No default host for the WebUiLoginDisplay means that we're already in the 295 // process of logging in - shut down screensaver and do nothing else. 296 ash::CloseScreensaver(); 297 } 298 299 ShutdownKioskModeScreensaver(); 300} 301 302static KioskModeScreensaver* g_kiosk_mode_screensaver = NULL; 303 304void InitializeKioskModeScreensaver() { 305 if (g_kiosk_mode_screensaver) { 306 LOG(WARNING) << "Screensaver was already initialized"; 307 return; 308 } 309 310 g_kiosk_mode_screensaver = new KioskModeScreensaver(); 311} 312 313void ShutdownKioskModeScreensaver() { 314 delete g_kiosk_mode_screensaver; 315 g_kiosk_mode_screensaver = NULL; 316} 317 318} // namespace chromeos 319