• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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