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