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:
AppWindowWatcher(AppLaunchController * controller,const std::string & app_id)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 }
~AppWindowWatcher()77 virtual ~AppWindowWatcher() {
78 window_registry_->RemoveObserver(this);
79 }
80
81 private:
82 // apps::AppWindowRegistry::Observer overrides:
OnAppWindowAdded(apps::AppWindow * app_window)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
NotifyAppWindowCreated()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
AppLaunchController(const std::string & app_id,bool diagnostic_mode,LoginDisplayHost * host,OobeDisplay * oobe_display)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
~AppLaunchController()125 AppLaunchController::~AppLaunchController() {
126 app_launch_splash_screen_actor_->SetDelegate(NULL);
127 }
128
StartAppLaunch()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
SkipSplashWaitForTesting()152 void AppLaunchController::SkipSplashWaitForTesting() {
153 skip_splash_wait_ = true;
154 }
155
156 // static
SetNetworkWaitForTesting(int wait_time_secs)157 void AppLaunchController::SetNetworkWaitForTesting(int wait_time_secs) {
158 network_wait_time_ = wait_time_secs;
159 }
160
161 // static
SetNetworkTimeoutCallbackForTesting(base::Closure * callback)162 void AppLaunchController::SetNetworkTimeoutCallbackForTesting(
163 base::Closure* callback) {
164 network_timeout_callback_ = callback;
165 }
166
167 // static
SetCanConfigureNetworkCallbackForTesting(ReturnBoolCallback * can_configure_network_callback)168 void AppLaunchController::SetCanConfigureNetworkCallbackForTesting(
169 ReturnBoolCallback* can_configure_network_callback) {
170 can_configure_network_callback_ = can_configure_network_callback;
171 }
172
173 // static
SetNeedOwnerAuthToConfigureNetworkCallbackForTesting(ReturnBoolCallback * need_owner_auth_callback)174 void AppLaunchController::SetNeedOwnerAuthToConfigureNetworkCallbackForTesting(
175 ReturnBoolCallback* need_owner_auth_callback) {
176 need_owner_auth_to_configure_network_callback_ = need_owner_auth_callback;
177 }
178
OnConfigureNetwork()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
OnOwnerSigninSuccess()195 void AppLaunchController::OnOwnerSigninSuccess() {
196 app_launch_splash_screen_actor_->ShowNetworkConfigureUI();
197 signin_screen_.reset();
198 }
199
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
OnCancelAppLaunch()212 void AppLaunchController::OnCancelAppLaunch() {
213 if (KioskAppManager::Get()->GetDisableBailoutShortcut())
214 return;
215
216 OnLaunchFailed(KioskAppLaunchError::USER_CANCEL);
217 }
218
OnNetworkConfigRequested(bool requested)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
OnNetworkStateChanged(bool online)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
OnProfileLoaded(Profile * profile)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
OnProfileLoadFailed(KioskAppLaunchError::Error error)250 void AppLaunchController::OnProfileLoadFailed(
251 KioskAppLaunchError::Error error) {
252 OnLaunchFailed(error);
253 }
254
CleanUp()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
OnNetworkWaitTimedout()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
OnAppWindowCreated()276 void AppLaunchController::OnAppWindowCreated() {
277 DVLOG(1) << "App window created, closing splash screen.";
278 CleanUp();
279 }
280
CanConfigureNetwork()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
NeedOwnerAuthToConfigureNetwork()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
MaybeShowNetworkConfigureUI()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
InitializeNetwork()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
IsNetworkReady()341 bool AppLaunchController::IsNetworkReady() {
342 return app_launch_splash_screen_actor_->IsNetworkReady();
343 }
344
OnLoadingOAuthFile()345 void AppLaunchController::OnLoadingOAuthFile() {
346 app_launch_splash_screen_actor_->UpdateAppLaunchState(
347 AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_AUTH_FILE);
348 }
349
OnInitializingTokenService()350 void AppLaunchController::OnInitializingTokenService() {
351 app_launch_splash_screen_actor_->UpdateAppLaunchState(
352 AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
353 }
354
OnInstallingApp()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
OnReadyToLaunch()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
OnLaunchSucceeded()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
OnLaunchFailed(KioskAppLaunchError::Error error)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
IsShowingNetworkConfigScreen()423 bool AppLaunchController::IsShowingNetworkConfigScreen() {
424 return network_config_requested_;
425 }
426
427 } // namespace chromeos
428