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