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