Home | History | Annotate | Download | only in kiosk_mode
      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 
     34 using extensions::Extension;
     35 using extensions::ExtensionGarbageCollectorChromeOS;
     36 using extensions::SandboxedUnpacker;
     37 
     38 namespace chromeos {
     39 
     40 namespace {
     41 
     42 ExtensionService* 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 
     50 ExtensionGarbageCollectorChromeOS* GetDefaultExtensionGarbageCollector() {
     51   Profile* default_profile = ProfileHelper::GetSigninProfile();
     52   if (!default_profile)
     53     return NULL;
     54   return ExtensionGarbageCollectorChromeOS::Get(default_profile);
     55 }
     56 
     57 typedef base::Callback<void(
     58     scoped_refptr<Extension>,
     59     const base::FilePath&)> UnpackCallback;
     60 
     61 class 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 
     92 void 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 
    107 void ScreensaverUnpackerClient::OnUnpackFailure(const base::string16& error) {
    108   LOG(ERROR) << "Couldn't unpack screensaver extension. Error: " << error;
    109   NotifyAppPackOfDamagedFile();
    110 }
    111 
    112 void 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 
    145 void 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 
    163 KioskModeScreensaver::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 
    177 KioskModeScreensaver::~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 
    208 void KioskModeScreensaver::GetScreensaverCrxPath() {
    209   chromeos::KioskModeSettings::Get()->GetScreensaverPath(
    210       base::Bind(&KioskModeScreensaver::ScreensaverPathCallback,
    211                  weak_ptr_factory_.GetWeakPtr()));
    212 }
    213 
    214 void 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 
    245 void 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 
    269 void 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 
    302 static KioskModeScreensaver* g_kiosk_mode_screensaver = NULL;
    303 
    304 void 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 
    313 void ShutdownKioskModeScreensaver() {
    314   delete g_kiosk_mode_screensaver;
    315   g_kiosk_mode_screensaver = NULL;
    316 }
    317 
    318 }  // namespace chromeos
    319