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