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 "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