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