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/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 "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/extension.h"
37 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
38 #include "extensions/common/manifest_handlers/offline_enabled_info.h"
39 #include "extensions/common/manifest_url_handlers.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"
49 using content::BrowserThread;
50 using extensions::Extension;
56 const char kOAuthRefreshToken[] = "refresh_token";
57 const char kOAuthClientId[] = "client_id";
58 const char kOAuthClientSecret[] = "client_secret";
60 const base::FilePath::CharType kOAuthFileName[] =
61 FILE_PATH_LITERAL("kiosk_auth");
63 const int kMaxLaunchAttempt = 5;
67 StartupAppLauncher::StartupAppLauncher(Profile* profile,
68 const std::string& app_id,
70 StartupAppLauncher::Delegate* delegate)
73 diagnostic_mode_(diagnostic_mode),
75 network_ready_handled_(false),
77 ready_to_launch_(false),
78 wait_for_crx_update_(false) {
80 DCHECK(crx_file::id_util::IdIsValid(app_id_));
81 KioskAppManager::Get()->AddObserver(this);
84 StartupAppLauncher::~StartupAppLauncher() {
85 KioskAppManager::Get()->RemoveObserver(this);
87 // StartupAppLauncher can be deleted at anytime during the launch process
88 // through a user bailout shortcut.
89 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
90 ->RemoveObserver(this);
93 void StartupAppLauncher::Initialize() {
94 StartLoadingOAuthFile();
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();
108 void StartupAppLauncher::StartLoadingOAuthFile() {
109 delegate_->OnLoadingOAuthFile();
111 KioskOAuthParams* auth_params = new KioskOAuthParams();
112 BrowserThread::PostBlockingPoolTaskAndReply(
114 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
116 base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
118 base::Owned(auth_params)));
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();
140 dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
141 dict->GetString(kOAuthClientId, &auth_params->client_id);
142 dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
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);
155 // If we are restarting chrome (i.e. on crash), we need to initialize
156 // OAuth2TokenService as well.
157 InitializeTokenService();
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";
172 MaybeInitializeNetwork();
175 void StartupAppLauncher::MaybeInitializeNetwork() {
176 network_ready_handled_ = false;
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) ||
184 !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension));
186 if (requires_network) {
187 delegate_->InitializeNetwork();
191 // Update the offline enabled crx cache if the network is ready;
192 // or just install the app.
193 if (delegate_->IsNetworkReady())
194 ContinueWithNetworkReady();
199 void StartupAppLauncher::InitializeTokenService() {
200 delegate_->OnInitializingTokenService();
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();
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.
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);
226 profile_token_service->UpdateCredentials(
228 auth_params_.refresh_token);
232 void StartupAppLauncher::OnRefreshTokenAvailable(
233 const std::string& account_id) {
234 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
235 ->RemoveObserver(this);
236 MaybeInitializeNetwork();
239 void StartupAppLauncher::OnRefreshTokensLoaded() {
240 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
241 ->RemoveObserver(this);
242 MaybeInitializeNetwork();
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_);
251 const bool offline_enabled =
252 extensions::OfflineEnabledInfo::IsOfflineEnabled(extension);
253 if (offline_enabled || delegate_->IsNetworkReady()) {
254 BrowserThread::PostTask(
257 base::Bind(&StartupAppLauncher::OnReadyToLaunch, AsWeakPtr()));
260 if (launch_attempt_ < kMaxLaunchAttempt) {
261 BrowserThread::PostTask(
264 base::Bind(&StartupAppLauncher::MaybeInitializeNetwork, AsWeakPtr()));
267 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH);
271 void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
273 if (extension_id != app_id_)
276 extensions::InstallTracker* tracker =
277 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
278 tracker->RemoveObserver(this);
279 if (delegate_->IsShowingNetworkConfigScreen()) {
280 LOG(WARNING) << "Showing network config screen";
289 LOG(ERROR) << "Failed to install the kiosk application app_id: "
291 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
294 void StartupAppLauncher::OnKioskExtensionLoadedInCache(
295 const std::string& app_id) {
296 OnKioskAppDataLoadStatusChanged(app_id);
299 void StartupAppLauncher::OnKioskExtensionDownloadFailed(
300 const std::string& app_id) {
301 OnKioskAppDataLoadStatusChanged(app_id);
304 void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
305 const std::string& app_id) {
306 if (app_id != app_id_ || !wait_for_crx_update_)
309 wait_for_crx_update_ = false;
310 if (KioskAppManager::Get()->HasCachedCrx(app_id_))
313 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
316 void StartupAppLauncher::LaunchApp() {
317 if (!ready_to_launch_) {
319 LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
322 const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
323 extension_service()->GetInstalledExtension(app_id_);
326 if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
327 OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
331 // Always open the app in a window.
332 OpenApplication(AppLaunchParams(profile_, extension,
333 extensions::LAUNCH_CONTAINER_WINDOW,
335 InitAppSession(profile_, app_id_);
337 user_manager::UserManager::Get()->SessionStarted();
339 content::NotificationService::current()->Notify(
340 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
341 content::NotificationService::AllSources(),
342 content::NotificationService::NoDetails());
344 if (diagnostic_mode_)
345 KioskDiagnosisRunner::Run(profile_, app_id_);
350 void StartupAppLauncher::OnLaunchSuccess() {
351 delegate_->OnLaunchSucceeded();
354 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
355 LOG(ERROR) << "App launch failed, error: " << error;
356 DCHECK_NE(KioskAppLaunchError::NONE, error);
358 delegate_->OnLaunchFailed(error);
361 void StartupAppLauncher::BeginInstall() {
362 KioskAppManager::Get()->InstallFromCache(app_id_);
363 if (extensions::ExtensionSystem::Get(profile_)
364 ->extension_service()
365 ->pending_extension_manager()
366 ->IsIdPending(app_id_)) {
367 delegate_->OnInstallingApp();
368 // Observe the crx installation events.
369 extensions::InstallTracker* tracker =
370 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
371 tracker->AddObserver(this);
375 if (extensions::ExtensionSystem::Get(profile_)
376 ->extension_service()
377 ->GetInstalledExtension(app_id_)) {
381 // The extension is skipped for installation due to some error.
382 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
386 void StartupAppLauncher::OnReadyToLaunch() {
387 ready_to_launch_ = true;
389 delegate_->OnReadyToLaunch();
392 void StartupAppLauncher::UpdateAppData() {
393 KioskAppManager::Get()->ClearAppData(app_id_);
394 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
397 } // namespace chromeos