Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / chromeos / app_mode / startup_app_launcher.cc
blobcb2bf6dcae3fc0c533111ae7eaa157180477d986
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.
5 #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h"
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/app_launch_params.h"
26 #include "chrome/browser/ui/extensions/application_launch.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "components/crx_file/id_util.h"
30 #include "components/signin/core/browser/profile_oauth2_token_service.h"
31 #include "components/signin/core/browser/signin_manager.h"
32 #include "components/user_manager/user_manager.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "extensions/browser/extension_system.h"
36 #include "extensions/common/constants.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 "extensions/common/manifest_url_handlers.h"
41 #include "google_apis/gaia/gaia_auth_consumer.h"
42 #include "google_apis/gaia/gaia_constants.h"
43 #include "net/base/load_flags.h"
44 #include "net/url_request/url_fetcher.h"
45 #include "net/url_request/url_fetcher_delegate.h"
46 #include "net/url_request/url_request_context_getter.h"
47 #include "net/url_request/url_request_status.h"
48 #include "url/gurl.h"
50 using content::BrowserThread;
51 using extensions::Extension;
53 namespace chromeos {
55 namespace {
57 const char kOAuthRefreshToken[] = "refresh_token";
58 const char kOAuthClientId[] = "client_id";
59 const char kOAuthClientSecret[] = "client_secret";
61 const base::FilePath::CharType kOAuthFileName[] =
62 FILE_PATH_LITERAL("kiosk_auth");
64 const int kMaxLaunchAttempt = 5;
66 } // namespace
68 StartupAppLauncher::StartupAppLauncher(Profile* profile,
69 const std::string& app_id,
70 bool diagnostic_mode,
71 StartupAppLauncher::Delegate* delegate)
72 : profile_(profile),
73 app_id_(app_id),
74 diagnostic_mode_(diagnostic_mode),
75 delegate_(delegate),
76 network_ready_handled_(false),
77 launch_attempt_(0),
78 ready_to_launch_(false),
79 wait_for_crx_update_(false) {
80 DCHECK(profile_);
81 DCHECK(crx_file::id_util::IdIsValid(app_id_));
82 KioskAppManager::Get()->AddObserver(this);
85 StartupAppLauncher::~StartupAppLauncher() {
86 KioskAppManager::Get()->RemoveObserver(this);
88 // StartupAppLauncher can be deleted at anytime during the launch process
89 // through a user bailout shortcut.
90 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
91 ->RemoveObserver(this);
92 extensions::InstallTrackerFactory::GetForBrowserContext(profile_)
93 ->RemoveObserver(this);
96 void StartupAppLauncher::Initialize() {
97 StartLoadingOAuthFile();
100 void StartupAppLauncher::ContinueWithNetworkReady() {
101 if (!network_ready_handled_) {
102 network_ready_handled_ = true;
103 // The network might not be ready when KioskAppManager tries to update
104 // external cache initially. Update the external cache now that the network
105 // is ready for sure.
106 wait_for_crx_update_ = true;
107 KioskAppManager::Get()->UpdateExternalCache();
111 void StartupAppLauncher::StartLoadingOAuthFile() {
112 delegate_->OnLoadingOAuthFile();
114 KioskOAuthParams* auth_params = new KioskOAuthParams();
115 BrowserThread::PostBlockingPoolTaskAndReply(
116 FROM_HERE,
117 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
118 auth_params),
119 base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
120 AsWeakPtr(),
121 base::Owned(auth_params)));
124 // static.
125 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
126 KioskOAuthParams* auth_params) {
127 int error_code = JSONFileValueDeserializer::JSON_NO_ERROR;
128 std::string error_msg;
129 base::FilePath user_data_dir;
130 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
131 base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
132 scoped_ptr<JSONFileValueDeserializer> deserializer(
133 new JSONFileValueDeserializer(user_data_dir.Append(kOAuthFileName)));
134 scoped_ptr<base::Value> value(
135 deserializer->Deserialize(&error_code, &error_msg));
136 base::DictionaryValue* dict = NULL;
137 if (error_code != JSONFileValueDeserializer::JSON_NO_ERROR ||
138 !value.get() || !value->GetAsDictionary(&dict)) {
139 LOG(WARNING) << "Can't find auth file at " << auth_file.value();
140 return;
143 dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
144 dict->GetString(kOAuthClientId, &auth_params->client_id);
145 dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
148 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
149 auth_params_ = *auth_params;
150 // Override chrome client_id and secret that will be used for identity
151 // API token minting.
152 if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
153 UserSessionManager::GetInstance()->SetAppModeChromeClientOAuthInfo(
154 auth_params_.client_id,
155 auth_params_.client_secret);
158 // If we are restarting chrome (i.e. on crash), we need to initialize
159 // OAuth2TokenService as well.
160 InitializeTokenService();
163 void StartupAppLauncher::RestartLauncher() {
164 // If the installer is still running in the background, we don't need to
165 // restart the launch process. We will just wait until it completes and
166 // launches the kiosk app.
167 if (extensions::ExtensionSystem::Get(profile_)
168 ->extension_service()
169 ->pending_extension_manager()
170 ->IsIdPending(app_id_)) {
171 LOG(WARNING) << "Installer still running";
172 return;
175 MaybeInitializeNetwork();
178 void StartupAppLauncher::MaybeInitializeNetwork() {
179 network_ready_handled_ = false;
181 const Extension* extension = GetPrimaryAppExtension();
182 bool crx_cached = KioskAppManager::Get()->HasCachedCrx(app_id_);
183 const bool requires_network =
184 (!extension && !crx_cached) ||
185 (extension &&
186 !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension));
188 if (requires_network) {
189 delegate_->InitializeNetwork();
190 return;
193 // Update the offline enabled crx cache if the network is ready;
194 // or just install the app.
195 if (delegate_->IsNetworkReady())
196 ContinueWithNetworkReady();
197 else
198 BeginInstall();
201 void StartupAppLauncher::InitializeTokenService() {
202 delegate_->OnInitializingTokenService();
204 ProfileOAuth2TokenService* profile_token_service =
205 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
206 SigninManagerBase* signin_manager =
207 SigninManagerFactory::GetForProfile(profile_);
208 const std::string primary_account_id =
209 signin_manager->GetAuthenticatedAccountId();
210 if (profile_token_service->RefreshTokenIsAvailable(primary_account_id) ||
211 auth_params_.refresh_token.empty()) {
212 MaybeInitializeNetwork();
213 } else {
214 // Pass oauth2 refresh token from the auth file.
215 // TODO(zelidrag): We should probably remove this option after M27.
216 // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
217 // Unless the code is no longer needed.
218 // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
219 // this code still needed? See above two TODOs.
221 // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
222 // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
223 // whichever comes first, both handlers call RemoveObserver on PO2TS.
224 // Handling any of the two events is the only way to resume the execution
225 // and enable Cleanup method to be called, self-invoking a destructor.
226 profile_token_service->AddObserver(this);
228 profile_token_service->UpdateCredentials(
229 primary_account_id,
230 auth_params_.refresh_token);
234 void StartupAppLauncher::OnRefreshTokenAvailable(
235 const std::string& account_id) {
236 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
237 ->RemoveObserver(this);
238 MaybeInitializeNetwork();
241 void StartupAppLauncher::OnRefreshTokensLoaded() {
242 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
243 ->RemoveObserver(this);
244 MaybeInitializeNetwork();
247 void StartupAppLauncher::MaybeLaunchApp() {
248 // Check if the app is offline enabled.
249 const Extension* extension = GetPrimaryAppExtension();
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;
267 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH);
271 void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
272 bool success) {
273 // Wait for pending updates or dependent extensions to download.
274 if (extensions::ExtensionSystem::Get(profile_)
275 ->extension_service()
276 ->pending_extension_manager()
277 ->HasPendingExtensions()) {
278 return;
281 extensions::InstallTracker* tracker =
282 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
283 tracker->RemoveObserver(this);
284 if (delegate_->IsShowingNetworkConfigScreen()) {
285 LOG(WARNING) << "Showing network config screen";
286 return;
289 if (extension_id == app_id_) {
290 if (success) {
291 MaybeInstallSecondaryApps();
292 } else {
293 LOG(ERROR) << "Failed to install the kiosk application app_id: "
294 << extension_id;
295 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
297 return;
300 if (AreSecondaryAppsInstalled()) {
301 MaybeLaunchApp();
302 } else {
303 LOG(ERROR) << "Failed to install one or more secondary apps.";
304 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
308 void StartupAppLauncher::OnKioskExtensionLoadedInCache(
309 const std::string& app_id) {
310 OnKioskAppDataLoadStatusChanged(app_id);
313 void StartupAppLauncher::OnKioskExtensionDownloadFailed(
314 const std::string& app_id) {
315 OnKioskAppDataLoadStatusChanged(app_id);
318 void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
319 const std::string& app_id) {
320 if (app_id != app_id_ || !wait_for_crx_update_)
321 return;
323 wait_for_crx_update_ = false;
324 if (KioskAppManager::Get()->HasCachedCrx(app_id_))
325 BeginInstall();
326 else
327 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
330 bool StartupAppLauncher::IsAnySecondaryAppPending() const {
331 DCHECK(HasSecondaryApps());
332 const extensions::Extension* extension = GetPrimaryAppExtension();
333 DCHECK(extension);
334 extensions::KioskModeInfo* info = extensions::KioskModeInfo::Get(extension);
335 for (const auto& id : info->secondary_app_ids) {
336 if (extensions::ExtensionSystem::Get(profile_)
337 ->extension_service()
338 ->pending_extension_manager()
339 ->IsIdPending(id)) {
340 return true;
343 return false;
346 bool StartupAppLauncher::AreSecondaryAppsInstalled() const {
347 DCHECK(HasSecondaryApps());
348 const extensions::Extension* extension = GetPrimaryAppExtension();
349 DCHECK(extension);
350 extensions::KioskModeInfo* info = extensions::KioskModeInfo::Get(extension);
351 for (const auto& id : info->secondary_app_ids) {
352 if (!extensions::ExtensionSystem::Get(profile_)
353 ->extension_service()
354 ->GetInstalledExtension(id)) {
355 return false;
358 return true;
361 bool StartupAppLauncher::HasSecondaryApps() const {
362 const extensions::Extension* extension = GetPrimaryAppExtension();
363 DCHECK(extension);
364 return extensions::KioskModeInfo::HasSecondaryApps(extension);
367 const extensions::Extension* StartupAppLauncher::GetPrimaryAppExtension()
368 const {
369 return extensions::ExtensionSystem::Get(profile_)
370 ->extension_service()
371 ->GetInstalledExtension(app_id_);
374 void StartupAppLauncher::LaunchApp() {
375 if (!ready_to_launch_) {
376 NOTREACHED();
377 LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
380 const Extension* extension = GetPrimaryAppExtension();
381 CHECK(extension);
383 if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
384 OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
385 return;
388 // Always open the app in a window.
389 OpenApplication(AppLaunchParams(profile_, extension,
390 extensions::LAUNCH_CONTAINER_WINDOW,
391 NEW_WINDOW, extensions::SOURCE_KIOSK));
392 InitAppSession(profile_, app_id_);
394 user_manager::UserManager::Get()->SessionStarted();
396 content::NotificationService::current()->Notify(
397 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
398 content::NotificationService::AllSources(),
399 content::NotificationService::NoDetails());
401 if (diagnostic_mode_)
402 KioskDiagnosisRunner::Run(profile_, app_id_);
404 OnLaunchSuccess();
407 void StartupAppLauncher::OnLaunchSuccess() {
408 delegate_->OnLaunchSucceeded();
411 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
412 LOG(ERROR) << "App launch failed, error: " << error;
413 DCHECK_NE(KioskAppLaunchError::NONE, error);
415 delegate_->OnLaunchFailed(error);
418 void StartupAppLauncher::BeginInstall() {
419 KioskAppManager::Get()->InstallFromCache(app_id_);
420 if (extensions::ExtensionSystem::Get(profile_)
421 ->extension_service()
422 ->pending_extension_manager()
423 ->IsIdPending(app_id_)) {
424 delegate_->OnInstallingApp();
425 // Observe the crx installation events.
426 extensions::InstallTracker* tracker =
427 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
428 tracker->AddObserver(this);
429 return;
432 if (GetPrimaryAppExtension()) {
433 // Install secondary apps.
434 MaybeInstallSecondaryApps();
435 } else {
436 // The extension is skipped for installation due to some error.
437 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
441 void StartupAppLauncher::MaybeInstallSecondaryApps() {
442 if (!HasSecondaryApps()) {
443 // Launch the primary app.
444 MaybeLaunchApp();
445 return;
448 if (!AreSecondaryAppsInstalled() && !delegate_->IsNetworkReady()) {
449 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
450 return;
453 extensions::KioskModeInfo* info =
454 extensions::KioskModeInfo::Get(GetPrimaryAppExtension());
455 KioskAppManager::Get()->InstallSecondaryApps(info->secondary_app_ids);
456 if (IsAnySecondaryAppPending()) {
457 delegate_->OnInstallingApp();
458 // Observe the crx installation events.
459 extensions::InstallTracker* tracker =
460 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
461 tracker->AddObserver(this);
462 return;
465 if (AreSecondaryAppsInstalled()) {
466 // Launch the primary app.
467 MaybeLaunchApp();
468 } else {
469 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
473 void StartupAppLauncher::OnReadyToLaunch() {
474 ready_to_launch_ = true;
475 UpdateAppData();
476 delegate_->OnReadyToLaunch();
479 void StartupAppLauncher::UpdateAppData() {
480 KioskAppManager::Get()->ClearAppData(app_id_);
481 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
484 } // namespace chromeos