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