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