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