Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / app_mode / startup_app_launcher.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/app_mode/startup_app_launcher.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/json/json_file_value_serializer.h"
10 #include "base/path_service.h"
11 #include "base/time/time.h"
12 #include "base/values.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
15 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
16 #include "chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h"
17 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/install_tracker.h"
20 #include "chrome/browser/extensions/install_tracker_factory.h"
21 #include "chrome/browser/extensions/updater/extension_updater.h"
22 #include "chrome/browser/lifetime/application_lifetime.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
24 #include "chrome/browser/signin/signin_manager_factory.h"
25 #include "chrome/browser/ui/extensions/application_launch.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/chrome_version_info.h"
29 #include "chrome/common/extensions/manifest_url_handler.h"
30 #include "components/crx_file/id_util.h"
31 #include "components/signin/core/browser/profile_oauth2_token_service.h"
32 #include "components/signin/core/browser/signin_manager.h"
33 #include "components/user_manager/user_manager.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/notification_service.h"
36 #include "extensions/browser/extension_system.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
39 #include "extensions/common/manifest_handlers/offline_enabled_info.h"
40 #include "google_apis/gaia/gaia_auth_consumer.h"
41 #include "google_apis/gaia/gaia_constants.h"
42 #include "net/base/load_flags.h"
43 #include "net/url_request/url_fetcher.h"
44 #include "net/url_request/url_fetcher_delegate.h"
45 #include "net/url_request/url_request_context_getter.h"
46 #include "net/url_request/url_request_status.h"
47 #include "url/gurl.h"
48
49 using content::BrowserThread;
50 using extensions::Extension;
51
52 namespace chromeos {
53
54 namespace {
55
56 const char kOAuthRefreshToken[] = "refresh_token";
57 const char kOAuthClientId[] = "client_id";
58 const char kOAuthClientSecret[] = "client_secret";
59
60 const base::FilePath::CharType kOAuthFileName[] =
61     FILE_PATH_LITERAL("kiosk_auth");
62
63 const int kMaxLaunchAttempt = 5;
64
65 }  // namespace
66
67 StartupAppLauncher::StartupAppLauncher(Profile* profile,
68                                        const std::string& app_id,
69                                        bool diagnostic_mode,
70                                        StartupAppLauncher::Delegate* delegate)
71     : profile_(profile),
72       app_id_(app_id),
73       diagnostic_mode_(diagnostic_mode),
74       delegate_(delegate),
75       network_ready_handled_(false),
76       launch_attempt_(0),
77       ready_to_launch_(false),
78       wait_for_crx_update_(false) {
79   DCHECK(profile_);
80   DCHECK(crx_file::id_util::IdIsValid(app_id_));
81   KioskAppManager::Get()->AddObserver(this);
82 }
83
84 StartupAppLauncher::~StartupAppLauncher() {
85   KioskAppManager::Get()->RemoveObserver(this);
86
87   // StartupAppLauncher can be deleted at anytime during the launch process
88   // through a user bailout shortcut.
89   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
90       ->RemoveObserver(this);
91 }
92
93 void StartupAppLauncher::Initialize() {
94   StartLoadingOAuthFile();
95 }
96
97 void StartupAppLauncher::ContinueWithNetworkReady() {
98   if (!network_ready_handled_) {
99     network_ready_handled_ = true;
100     // The network might not be ready when KioskAppManager tries to update
101     // external cache initially. Update the external cache now that the network
102     // is ready for sure.
103     wait_for_crx_update_ = true;
104     KioskAppManager::Get()->UpdateExternalCache();
105   }
106 }
107
108 void StartupAppLauncher::StartLoadingOAuthFile() {
109   delegate_->OnLoadingOAuthFile();
110
111   KioskOAuthParams* auth_params = new KioskOAuthParams();
112   BrowserThread::PostBlockingPoolTaskAndReply(
113       FROM_HERE,
114       base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
115                  auth_params),
116       base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
117                  AsWeakPtr(),
118                  base::Owned(auth_params)));
119 }
120
121 // static.
122 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
123     KioskOAuthParams* auth_params) {
124   int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
125   std::string error_msg;
126   base::FilePath user_data_dir;
127   CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
128   base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
129   scoped_ptr<JSONFileValueSerializer> serializer(
130       new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
131   scoped_ptr<base::Value> value(
132       serializer->Deserialize(&error_code, &error_msg));
133   base::DictionaryValue* dict = NULL;
134   if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
135       !value.get() || !value->GetAsDictionary(&dict)) {
136     LOG(WARNING) << "Can't find auth file at " << auth_file.value();
137     return;
138   }
139
140   dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
141   dict->GetString(kOAuthClientId, &auth_params->client_id);
142   dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
143 }
144
145 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
146   auth_params_ = *auth_params;
147   // Override chrome client_id and secret that will be used for identity
148   // API token minting.
149   if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
150     UserSessionManager::GetInstance()->SetAppModeChromeClientOAuthInfo(
151             auth_params_.client_id,
152             auth_params_.client_secret);
153   }
154
155   // If we are restarting chrome (i.e. on crash), we need to initialize
156   // OAuth2TokenService as well.
157   InitializeTokenService();
158 }
159
160 void StartupAppLauncher::RestartLauncher() {
161   // If the installer is still running in the background, we don't need to
162   // restart the launch process. We will just wait until it completes and
163   // launches the kiosk app.
164   if (extensions::ExtensionSystem::Get(profile_)
165           ->extension_service()
166           ->pending_extension_manager()
167           ->IsIdPending(app_id_)) {
168     LOG(WARNING) << "Installer still running";
169     return;
170   }
171
172   MaybeInitializeNetwork();
173 }
174
175 void StartupAppLauncher::MaybeInitializeNetwork() {
176   network_ready_handled_ = false;
177
178   const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
179       extension_service()->GetInstalledExtension(app_id_);
180   bool crx_cached = KioskAppManager::Get()->HasCachedCrx(app_id_);
181   const bool requires_network =
182       (!extension && !crx_cached) ||
183       (extension &&
184        !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension));
185
186   if (requires_network) {
187     delegate_->InitializeNetwork();
188     return;
189   }
190
191   // Update the offline enabled crx cache if the network is ready;
192   // or just install the app.
193   if (delegate_->IsNetworkReady())
194     ContinueWithNetworkReady();
195   else
196     BeginInstall();
197 }
198
199 void StartupAppLauncher::InitializeTokenService() {
200   delegate_->OnInitializingTokenService();
201
202   ProfileOAuth2TokenService* profile_token_service =
203       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
204   SigninManagerBase* signin_manager =
205       SigninManagerFactory::GetForProfile(profile_);
206   const std::string primary_account_id =
207       signin_manager->GetAuthenticatedAccountId();
208   if (profile_token_service->RefreshTokenIsAvailable(primary_account_id) ||
209       auth_params_.refresh_token.empty()) {
210     MaybeInitializeNetwork();
211   } else {
212     // Pass oauth2 refresh token from the auth file.
213     // TODO(zelidrag): We should probably remove this option after M27.
214     // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
215     // Unless the code is no longer needed.
216     // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
217     // this code still needed?  See above two TODOs.
218     //
219     // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
220     // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
221     // whichever comes first, both handlers call RemoveObserver on PO2TS.
222     // Handling any of the two events is the only way to resume the execution
223     // and enable Cleanup method to be called, self-invoking a destructor.
224     profile_token_service->AddObserver(this);
225
226     profile_token_service->UpdateCredentials(
227         primary_account_id,
228         auth_params_.refresh_token);
229   }
230 }
231
232 void StartupAppLauncher::OnRefreshTokenAvailable(
233     const std::string& account_id) {
234   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
235       ->RemoveObserver(this);
236   MaybeInitializeNetwork();
237 }
238
239 void StartupAppLauncher::OnRefreshTokensLoaded() {
240   ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
241       ->RemoveObserver(this);
242   MaybeInitializeNetwork();
243 }
244
245 void StartupAppLauncher::MaybeLaunchApp() {
246   // Check if the app is offline enabled.
247   const Extension* extension = extensions::ExtensionSystem::Get(profile_)
248                                    ->extension_service()
249                                    ->GetInstalledExtension(app_id_);
250   DCHECK(extension);
251   const bool offline_enabled =
252       extensions::OfflineEnabledInfo::IsOfflineEnabled(extension);
253   if (offline_enabled || delegate_->IsNetworkReady()) {
254     BrowserThread::PostTask(
255         BrowserThread::UI,
256         FROM_HERE,
257         base::Bind(&StartupAppLauncher::OnReadyToLaunch, AsWeakPtr()));
258   } else {
259     ++launch_attempt_;
260     if (launch_attempt_ < kMaxLaunchAttempt) {
261       BrowserThread::PostTask(
262           BrowserThread::UI,
263           FROM_HERE,
264           base::Bind(&StartupAppLauncher::MaybeInitializeNetwork, AsWeakPtr()));
265       return;
266     }
267     OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH);
268   }
269 }
270
271 void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
272                                             bool success) {
273   if (extension_id != app_id_)
274     return;
275
276   extensions::InstallTracker* tracker =
277       extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
278   tracker->RemoveObserver(this);
279   if (delegate_->IsShowingNetworkConfigScreen()) {
280     LOG(WARNING) << "Showing network config screen";
281     return;
282   }
283
284   if (success) {
285     MaybeLaunchApp();
286     return;
287   }
288
289   LOG(ERROR) << "Failed to install the kiosk application app_id: "
290              << extension_id;
291   OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
292 }
293
294 void StartupAppLauncher::OnKioskExtensionLoadedInCache(
295     const std::string& app_id) {
296   OnKioskAppDataLoadStatusChanged(app_id);
297 }
298
299 void StartupAppLauncher::OnKioskExtensionDownloadFailed(
300     const std::string& app_id) {
301   OnKioskAppDataLoadStatusChanged(app_id);
302 }
303
304 void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
305     const std::string& app_id) {
306   if (app_id != app_id_ || !wait_for_crx_update_)
307     return;
308
309   wait_for_crx_update_ = false;
310   if (KioskAppManager::Get()->HasCachedCrx(app_id_))
311     BeginInstall();
312   else
313     OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
314 }
315
316 void StartupAppLauncher::LaunchApp() {
317   if (!ready_to_launch_) {
318     NOTREACHED();
319     LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
320   }
321
322   const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
323       extension_service()->GetInstalledExtension(app_id_);
324   CHECK(extension);
325
326   if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
327     OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
328     return;
329   }
330
331   // Always open the app in a window.
332   OpenApplication(AppLaunchParams(profile_, extension,
333                                   extensions::LAUNCH_CONTAINER_WINDOW,
334                                   NEW_WINDOW));
335   InitAppSession(profile_, app_id_);
336
337   user_manager::UserManager::Get()->SessionStarted();
338
339   content::NotificationService::current()->Notify(
340       chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
341       content::NotificationService::AllSources(),
342       content::NotificationService::NoDetails());
343
344   if (diagnostic_mode_)
345     KioskDiagnosisRunner::Run(profile_, app_id_);
346
347   OnLaunchSuccess();
348 }
349
350 void StartupAppLauncher::OnLaunchSuccess() {
351   delegate_->OnLaunchSucceeded();
352 }
353
354 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
355   LOG(ERROR) << "App launch failed, error: " << error;
356   DCHECK_NE(KioskAppLaunchError::NONE, error);
357
358   delegate_->OnLaunchFailed(error);
359 }
360
361 void StartupAppLauncher::OnUpdateCheckFinished() {
362   OnReadyToLaunch();
363   UpdateAppData();
364 }
365
366 void StartupAppLauncher::BeginInstall() {
367   KioskAppManager::Get()->InstallFromCache(app_id_);
368   if (extensions::ExtensionSystem::Get(profile_)
369           ->extension_service()
370           ->pending_extension_manager()
371           ->IsIdPending(app_id_)) {
372     delegate_->OnInstallingApp();
373     // Observe the crx installation events.
374     extensions::InstallTracker* tracker =
375         extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
376     tracker->AddObserver(this);
377     return;
378   }
379
380   if (extensions::ExtensionSystem::Get(profile_)
381           ->extension_service()
382           ->GetInstalledExtension(app_id_)) {
383     // Launch the app.
384     OnReadyToLaunch();
385   } else {
386     // The extension is skipped for installation due to some error.
387     OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
388   }
389 }
390
391 void StartupAppLauncher::OnReadyToLaunch() {
392   ready_to_launch_ = true;
393   delegate_->OnReadyToLaunch();
394 }
395
396 void StartupAppLauncher::UpdateAppData() {
397   KioskAppManager::Get()->ClearAppData(app_id_);
398   KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
399 }
400
401 }   // namespace chromeos