32263f6c3dbf2118f0347bc100edd033d9693a0d
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / app_launch_controller.cc
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