Home | History | Annotate | Download | only in login
      1 // Copyright 2013 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/login/app_launch_controller.h"
      6 
      7 #include "apps/shell_window_registry.h"
      8 #include "base/callback.h"
      9 #include "base/files/file_path.h"
     10 #include "base/json/json_file_value_serializer.h"
     11 #include "base/time/time.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/chrome_notification_types.h"
     15 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
     16 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
     17 #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h"
     18 #include "chrome/browser/chromeos/login/login_display_host.h"
     19 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
     20 #include "chrome/browser/chromeos/login/oobe_display.h"
     21 #include "chrome/browser/chromeos/login/screens/error_screen_actor.h"
     22 #include "chrome/browser/chromeos/login/webui_login_view.h"
     23 #include "chrome/browser/chromeos/settings/cros_settings.h"
     24 #include "chrome/browser/lifetime/application_lifetime.h"
     25 #include "chrome/browser/policy/browser_policy_connector.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/browser/ui/webui/chromeos/login/app_launch_splash_screen_handler.h"
     28 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
     29 #include "content/public/browser/browser_thread.h"
     30 #include "content/public/browser/notification_service.h"
     31 #include "net/base/network_change_notifier.h"
     32 
     33 namespace chromeos {
     34 
     35 namespace {
     36 
     37 // Application install splash screen minimum show time in milliseconds.
     38 const int kAppInstallSplashScreenMinTimeMS = 3000;
     39 
     40 }  // namespace
     41 
     42 // static
     43 bool AppLaunchController::skip_splash_wait_ = false;
     44 int AppLaunchController::network_wait_time_ = 10;
     45 base::Closure* AppLaunchController::network_timeout_callback_ = NULL;
     46 AppLaunchController::ReturnBoolCallback*
     47     AppLaunchController::can_configure_network_callback_ = NULL;
     48 AppLaunchController::ReturnBoolCallback*
     49     AppLaunchController::need_owner_auth_to_configure_network_callback_ = NULL;
     50 
     51 ////////////////////////////////////////////////////////////////////////////////
     52 // AppLaunchController::AppWindowWatcher
     53 
     54 class AppLaunchController::AppWindowWatcher
     55     : public apps::ShellWindowRegistry::Observer {
     56  public:
     57   explicit AppWindowWatcher(AppLaunchController* controller)
     58     : controller_(controller),
     59       window_registry_(apps::ShellWindowRegistry::Get(controller->profile_)) {
     60     window_registry_->AddObserver(this);
     61   }
     62   virtual ~AppWindowWatcher() {
     63     window_registry_->RemoveObserver(this);
     64   }
     65 
     66  private:
     67   // apps::ShellWindowRegistry::Observer overrides:
     68   virtual void OnShellWindowAdded(apps::ShellWindow* shell_window) OVERRIDE {
     69     if (controller_) {
     70       controller_->OnAppWindowCreated();
     71       controller_= NULL;
     72     }
     73   }
     74   virtual void OnShellWindowIconChanged(
     75       apps::ShellWindow* shell_window) OVERRIDE {}
     76   virtual void OnShellWindowRemoved(apps::ShellWindow* shell_window) OVERRIDE {}
     77 
     78   AppLaunchController* controller_;
     79   apps::ShellWindowRegistry* window_registry_;
     80 
     81   DISALLOW_COPY_AND_ASSIGN(AppWindowWatcher);
     82 };
     83 
     84 ////////////////////////////////////////////////////////////////////////////////
     85 // AppLaunchController
     86 
     87 AppLaunchController::AppLaunchController(const std::string& app_id,
     88                                          LoginDisplayHost* host,
     89                                          OobeDisplay* oobe_display)
     90     : profile_(NULL),
     91       app_id_(app_id),
     92       host_(host),
     93       oobe_display_(oobe_display),
     94       app_launch_splash_screen_actor_(
     95           oobe_display_->GetAppLaunchSplashScreenActor()),
     96       webui_visible_(false),
     97       launcher_ready_(false),
     98       waiting_for_network_(false),
     99       network_wait_timedout_(false),
    100       showing_network_dialog_(false),
    101       launch_splash_start_time_(0) {
    102 }
    103 
    104 AppLaunchController::~AppLaunchController() {
    105   app_launch_splash_screen_actor_->SetDelegate(NULL);
    106 }
    107 
    108 void AppLaunchController::StartAppLaunch() {
    109   DVLOG(1) << "Starting kiosk mode...";
    110 
    111   webui_visible_ = host_->GetWebUILoginView()->webui_visible();
    112   if (!webui_visible_) {
    113     registrar_.Add(this, chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
    114                    content::NotificationService::AllSources());
    115   }
    116   launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
    117 
    118   // TODO(tengs): Add a loading profile app launch state.
    119   app_launch_splash_screen_actor_->SetDelegate(this);
    120   app_launch_splash_screen_actor_->Show(app_id_);
    121 
    122   kiosk_profile_loader_.reset(
    123       new KioskProfileLoader(KioskAppManager::Get(), app_id_, this));
    124   kiosk_profile_loader_->Start();
    125 }
    126 
    127 // static
    128 void AppLaunchController::SkipSplashWaitForTesting() {
    129   skip_splash_wait_ = true;
    130 }
    131 
    132 // static
    133 void AppLaunchController::SetNetworkWaitForTesting(int wait_time_secs) {
    134   network_wait_time_ = wait_time_secs;
    135 }
    136 
    137 // static
    138 void AppLaunchController::SetNetworkTimeoutCallbackForTesting(
    139     base::Closure* callback) {
    140   network_timeout_callback_ = callback;
    141 }
    142 
    143 // static
    144 void AppLaunchController::SetCanConfigureNetworkCallbackForTesting(
    145     ReturnBoolCallback* can_configure_network_callback) {
    146   can_configure_network_callback_ = can_configure_network_callback;
    147 }
    148 
    149 // static
    150 void AppLaunchController::SetNeedOwnerAuthToConfigureNetworkCallbackForTesting(
    151     ReturnBoolCallback* need_owner_auth_callback) {
    152   need_owner_auth_to_configure_network_callback_ = need_owner_auth_callback;
    153 }
    154 
    155 void AppLaunchController::OnConfigureNetwork() {
    156   DCHECK(profile_);
    157   showing_network_dialog_ = true;
    158   if (CanConfigureNetwork() && NeedOwnerAuthToConfigureNetwork()) {
    159     signin_screen_.reset(new AppLaunchSigninScreen(
    160        static_cast<OobeUI*>(oobe_display_), this));
    161     signin_screen_->Show();
    162   } else {
    163     // If kiosk mode was configured through enterprise policy, we may
    164     // not have an owner user.
    165     // TODO(tengs): We need to figure out the appropriate security meausres
    166     // for this case.
    167     NOTREACHED();
    168   }
    169 }
    170 
    171 void AppLaunchController::OnOwnerSigninSuccess() {
    172   app_launch_splash_screen_actor_->ShowNetworkConfigureUI();
    173   signin_screen_.reset();
    174 }
    175 
    176 void AppLaunchController::Observe(
    177     int type,
    178     const content::NotificationSource& source,
    179     const content::NotificationDetails& details) {
    180   DCHECK_EQ(chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, type);
    181   DCHECK(!webui_visible_);
    182   webui_visible_ = true;
    183   launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
    184   if (launcher_ready_)
    185     OnReadyToLaunch();
    186 }
    187 
    188 void AppLaunchController::OnCancelAppLaunch() {
    189   if (KioskAppManager::Get()->GetDisableBailoutShortcut())
    190     return;
    191 
    192   OnLaunchFailed(KioskAppLaunchError::USER_CANCEL);
    193 }
    194 
    195 void AppLaunchController::OnNetworkStateChanged(bool online) {
    196   if (!waiting_for_network_)
    197     return;
    198 
    199   if (online)
    200     startup_app_launcher_->ContinueWithNetworkReady();
    201   else if (network_wait_timedout_)
    202     MaybeShowNetworkConfigureUI();
    203 }
    204 
    205 void AppLaunchController::OnProfileLoaded(Profile* profile) {
    206   DVLOG(1) << "Profile loaded... Starting app launch.";
    207   profile_ = profile;
    208 
    209   kiosk_profile_loader_.reset();
    210   startup_app_launcher_.reset(new StartupAppLauncher(profile_, app_id_, this));
    211   startup_app_launcher_->Initialize();
    212 }
    213 
    214 void AppLaunchController::OnProfileLoadFailed(
    215     KioskAppLaunchError::Error error) {
    216   OnLaunchFailed(error);
    217 }
    218 
    219 void AppLaunchController::CleanUp() {
    220   kiosk_profile_loader_.reset();
    221   startup_app_launcher_.reset();
    222 
    223   if (host_)
    224     host_->Finalize();
    225 }
    226 
    227 void AppLaunchController::OnNetworkWaitTimedout() {
    228   DCHECK(waiting_for_network_);
    229   LOG(WARNING) << "OnNetworkWaitTimedout... connection = "
    230                <<  net::NetworkChangeNotifier::GetConnectionType();
    231   network_wait_timedout_ = true;
    232 
    233   MaybeShowNetworkConfigureUI();
    234 
    235   if (network_timeout_callback_)
    236     network_timeout_callback_->Run();
    237 }
    238 
    239 void AppLaunchController::OnAppWindowCreated() {
    240   DVLOG(1) << "App window created, closing splash screen.";
    241   CleanUp();
    242 }
    243 
    244 bool AppLaunchController::CanConfigureNetwork() {
    245   if (can_configure_network_callback_)
    246     return can_configure_network_callback_->Run();
    247 
    248   if (g_browser_process->browser_policy_connector()->IsEnterpriseManaged()) {
    249     bool should_prompt;
    250     if (CrosSettings::Get()->GetBoolean(
    251             kAccountsPrefDeviceLocalAccountPromptForNetworkWhenOffline,
    252             &should_prompt)) {
    253       return should_prompt;
    254     }
    255 
    256     // Default to true to allow network configuration if the policy is missing.
    257     return true;
    258   }
    259 
    260   return !UserManager::Get()->GetOwnerEmail().empty();
    261 }
    262 
    263 bool AppLaunchController::NeedOwnerAuthToConfigureNetwork() {
    264   if (need_owner_auth_to_configure_network_callback_)
    265     return need_owner_auth_to_configure_network_callback_->Run();
    266 
    267   return !g_browser_process->browser_policy_connector()->IsEnterpriseManaged();
    268 }
    269 
    270 void AppLaunchController::MaybeShowNetworkConfigureUI() {
    271   if (CanConfigureNetwork()) {
    272     if (NeedOwnerAuthToConfigureNetwork()) {
    273       app_launch_splash_screen_actor_->ToggleNetworkConfig(true);
    274     } else {
    275       showing_network_dialog_ = true;
    276       app_launch_splash_screen_actor_->ShowNetworkConfigureUI();
    277     }
    278   } else {
    279     app_launch_splash_screen_actor_->UpdateAppLaunchState(
    280         AppLaunchSplashScreenActor::APP_LAUNCH_STATE_NETWORK_WAIT_TIMEOUT);
    281   }
    282 }
    283 
    284 void AppLaunchController::InitializeNetwork() {
    285   // Show the network configration dialog if network is not initialized
    286   // after a brief wait time.
    287   waiting_for_network_ = true;
    288   network_wait_timer_.Start(
    289       FROM_HERE,
    290       base::TimeDelta::FromSeconds(network_wait_time_),
    291       this, &AppLaunchController::OnNetworkWaitTimedout);
    292 
    293   app_launch_splash_screen_actor_->UpdateAppLaunchState(
    294       AppLaunchSplashScreenActor::APP_LAUNCH_STATE_PREPARING_NETWORK);
    295 }
    296 
    297 void AppLaunchController::OnLoadingOAuthFile() {
    298   app_launch_splash_screen_actor_->UpdateAppLaunchState(
    299       AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_AUTH_FILE);
    300 }
    301 
    302 void AppLaunchController::OnInitializingTokenService() {
    303   app_launch_splash_screen_actor_->UpdateAppLaunchState(
    304       AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
    305 }
    306 
    307 void AppLaunchController::OnInstallingApp() {
    308   app_launch_splash_screen_actor_->UpdateAppLaunchState(
    309       AppLaunchSplashScreenActor::APP_LAUNCH_STATE_INSTALLING_APPLICATION);
    310 
    311   waiting_for_network_ = false;
    312   network_wait_timer_.Stop();
    313   app_launch_splash_screen_actor_->ToggleNetworkConfig(false);
    314 
    315   // We have connectivity at this point, so we can skip the network
    316   // configuration dialog if it is being shown.
    317   if (showing_network_dialog_) {
    318     app_launch_splash_screen_actor_->Show(app_id_);
    319     showing_network_dialog_ = false;
    320     launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
    321   }
    322 }
    323 
    324 void AppLaunchController::OnReadyToLaunch() {
    325   launcher_ready_ = true;
    326   if (!webui_visible_)
    327     return;
    328 
    329   const int64 time_taken_ms = (base::TimeTicks::Now() -
    330       base::TimeTicks::FromInternalValue(launch_splash_start_time_)).
    331       InMilliseconds();
    332 
    333   // Enforce that we show app install splash screen for some minimum amount
    334   // of time.
    335   if (!skip_splash_wait_ && time_taken_ms < kAppInstallSplashScreenMinTimeMS) {
    336     content::BrowserThread::PostDelayedTask(
    337         content::BrowserThread::UI,
    338         FROM_HERE,
    339         base::Bind(&AppLaunchController::OnReadyToLaunch, AsWeakPtr()),
    340         base::TimeDelta::FromMilliseconds(
    341             kAppInstallSplashScreenMinTimeMS - time_taken_ms));
    342     return;
    343   }
    344 
    345   startup_app_launcher_->LaunchApp();
    346 }
    347 
    348 void AppLaunchController::OnLaunchSucceeded() {
    349   DVLOG(1) << "Kiosk launch succeeded, wait for app window.";
    350   app_launch_splash_screen_actor_->UpdateAppLaunchState(
    351       AppLaunchSplashScreenActor::APP_LAUNCH_STATE_WAITING_APP_WINDOW);
    352 
    353   DCHECK(!app_window_watcher_);
    354   app_window_watcher_.reset(new AppWindowWatcher(this));
    355 }
    356 
    357 void AppLaunchController::OnLaunchFailed(KioskAppLaunchError::Error error) {
    358   LOG(ERROR) << "Kiosk launch failed. Will now shut down.";
    359   DCHECK_NE(KioskAppLaunchError::NONE, error);
    360 
    361   // Saves the error and ends the session to go back to login screen.
    362   KioskAppLaunchError::Save(error);
    363   chrome::AttemptUserExit();
    364   CleanUp();
    365 }
    366 
    367 }   // namespace chromeos
    368