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:
AppWindowWatcher(AppLaunchController * controller)57 explicit AppWindowWatcher(AppLaunchController* controller)
58 : controller_(controller),
59 window_registry_(apps::ShellWindowRegistry::Get(controller->profile_)) {
60 window_registry_->AddObserver(this);
61 }
~AppWindowWatcher()62 virtual ~AppWindowWatcher() {
63 window_registry_->RemoveObserver(this);
64 }
65
66 private:
67 // apps::ShellWindowRegistry::Observer overrides:
OnShellWindowAdded(apps::ShellWindow * shell_window)68 virtual void OnShellWindowAdded(apps::ShellWindow* shell_window) OVERRIDE {
69 if (controller_) {
70 controller_->OnAppWindowCreated();
71 controller_= NULL;
72 }
73 }
OnShellWindowIconChanged(apps::ShellWindow * shell_window)74 virtual void OnShellWindowIconChanged(
75 apps::ShellWindow* shell_window) OVERRIDE {}
OnShellWindowRemoved(apps::ShellWindow * shell_window)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
AppLaunchController(const std::string & app_id,LoginDisplayHost * host,OobeDisplay * oobe_display)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
~AppLaunchController()104 AppLaunchController::~AppLaunchController() {
105 app_launch_splash_screen_actor_->SetDelegate(NULL);
106 }
107
StartAppLaunch()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
SkipSplashWaitForTesting()128 void AppLaunchController::SkipSplashWaitForTesting() {
129 skip_splash_wait_ = true;
130 }
131
132 // static
SetNetworkWaitForTesting(int wait_time_secs)133 void AppLaunchController::SetNetworkWaitForTesting(int wait_time_secs) {
134 network_wait_time_ = wait_time_secs;
135 }
136
137 // static
SetNetworkTimeoutCallbackForTesting(base::Closure * callback)138 void AppLaunchController::SetNetworkTimeoutCallbackForTesting(
139 base::Closure* callback) {
140 network_timeout_callback_ = callback;
141 }
142
143 // static
SetCanConfigureNetworkCallbackForTesting(ReturnBoolCallback * can_configure_network_callback)144 void AppLaunchController::SetCanConfigureNetworkCallbackForTesting(
145 ReturnBoolCallback* can_configure_network_callback) {
146 can_configure_network_callback_ = can_configure_network_callback;
147 }
148
149 // static
SetNeedOwnerAuthToConfigureNetworkCallbackForTesting(ReturnBoolCallback * need_owner_auth_callback)150 void AppLaunchController::SetNeedOwnerAuthToConfigureNetworkCallbackForTesting(
151 ReturnBoolCallback* need_owner_auth_callback) {
152 need_owner_auth_to_configure_network_callback_ = need_owner_auth_callback;
153 }
154
OnConfigureNetwork()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
OnOwnerSigninSuccess()171 void AppLaunchController::OnOwnerSigninSuccess() {
172 app_launch_splash_screen_actor_->ShowNetworkConfigureUI();
173 signin_screen_.reset();
174 }
175
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
OnCancelAppLaunch()188 void AppLaunchController::OnCancelAppLaunch() {
189 if (KioskAppManager::Get()->GetDisableBailoutShortcut())
190 return;
191
192 OnLaunchFailed(KioskAppLaunchError::USER_CANCEL);
193 }
194
OnNetworkStateChanged(bool online)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
OnProfileLoaded(Profile * profile)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
OnProfileLoadFailed(KioskAppLaunchError::Error error)214 void AppLaunchController::OnProfileLoadFailed(
215 KioskAppLaunchError::Error error) {
216 OnLaunchFailed(error);
217 }
218
CleanUp()219 void AppLaunchController::CleanUp() {
220 kiosk_profile_loader_.reset();
221 startup_app_launcher_.reset();
222
223 if (host_)
224 host_->Finalize();
225 }
226
OnNetworkWaitTimedout()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
OnAppWindowCreated()239 void AppLaunchController::OnAppWindowCreated() {
240 DVLOG(1) << "App window created, closing splash screen.";
241 CleanUp();
242 }
243
CanConfigureNetwork()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
NeedOwnerAuthToConfigureNetwork()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
MaybeShowNetworkConfigureUI()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
InitializeNetwork()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
OnLoadingOAuthFile()297 void AppLaunchController::OnLoadingOAuthFile() {
298 app_launch_splash_screen_actor_->UpdateAppLaunchState(
299 AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_AUTH_FILE);
300 }
301
OnInitializingTokenService()302 void AppLaunchController::OnInitializingTokenService() {
303 app_launch_splash_screen_actor_->UpdateAppLaunchState(
304 AppLaunchSplashScreenActor::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
305 }
306
OnInstallingApp()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
OnReadyToLaunch()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
OnLaunchSucceeded()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
OnLaunchFailed(KioskAppLaunchError::Error error)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