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"
50 using content::BrowserThread
;
51 using extensions::Extension
;
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;
68 StartupAppLauncher::StartupAppLauncher(Profile
* profile
,
69 const std::string
& app_id
,
71 StartupAppLauncher::Delegate
* delegate
)
74 diagnostic_mode_(diagnostic_mode
),
76 network_ready_handled_(false),
78 ready_to_launch_(false),
79 wait_for_crx_update_(false) {
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(
117 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool
,
119 base::Bind(&StartupAppLauncher::OnOAuthFileLoaded
,
121 base::Owned(auth_params
)));
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();
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";
175 MaybeInitializeNetwork();
178 void StartupAppLauncher::MaybeInitializeNetwork() {
179 network_ready_handled_
= false;
181 const Extension
* extension
= extensions::ExtensionSystem::Get(profile_
)->
182 extension_service()->GetInstalledExtension(app_id_
);
183 bool crx_cached
= KioskAppManager::Get()->HasCachedCrx(app_id_
);
184 const bool requires_network
=
185 (!extension
&& !crx_cached
) ||
187 !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension
));
189 if (requires_network
) {
190 delegate_
->InitializeNetwork();
194 // Update the offline enabled crx cache if the network is ready;
195 // or just install the app.
196 if (delegate_
->IsNetworkReady())
197 ContinueWithNetworkReady();
202 void StartupAppLauncher::InitializeTokenService() {
203 delegate_
->OnInitializingTokenService();
205 ProfileOAuth2TokenService
* profile_token_service
=
206 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
);
207 SigninManagerBase
* signin_manager
=
208 SigninManagerFactory::GetForProfile(profile_
);
209 const std::string primary_account_id
=
210 signin_manager
->GetAuthenticatedAccountId();
211 if (profile_token_service
->RefreshTokenIsAvailable(primary_account_id
) ||
212 auth_params_
.refresh_token
.empty()) {
213 MaybeInitializeNetwork();
215 // Pass oauth2 refresh token from the auth file.
216 // TODO(zelidrag): We should probably remove this option after M27.
217 // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
218 // Unless the code is no longer needed.
219 // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
220 // this code still needed? See above two TODOs.
222 // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
223 // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
224 // whichever comes first, both handlers call RemoveObserver on PO2TS.
225 // Handling any of the two events is the only way to resume the execution
226 // and enable Cleanup method to be called, self-invoking a destructor.
227 profile_token_service
->AddObserver(this);
229 profile_token_service
->UpdateCredentials(
231 auth_params_
.refresh_token
);
235 void StartupAppLauncher::OnRefreshTokenAvailable(
236 const std::string
& account_id
) {
237 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
)
238 ->RemoveObserver(this);
239 MaybeInitializeNetwork();
242 void StartupAppLauncher::OnRefreshTokensLoaded() {
243 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
)
244 ->RemoveObserver(this);
245 MaybeInitializeNetwork();
248 void StartupAppLauncher::MaybeLaunchApp() {
249 // Check if the app is offline enabled.
250 const Extension
* extension
= extensions::ExtensionSystem::Get(profile_
)
251 ->extension_service()
252 ->GetInstalledExtension(app_id_
);
254 const bool offline_enabled
=
255 extensions::OfflineEnabledInfo::IsOfflineEnabled(extension
);
256 if (offline_enabled
|| delegate_
->IsNetworkReady()) {
257 BrowserThread::PostTask(
260 base::Bind(&StartupAppLauncher::OnReadyToLaunch
, AsWeakPtr()));
263 if (launch_attempt_
< kMaxLaunchAttempt
) {
264 BrowserThread::PostTask(
267 base::Bind(&StartupAppLauncher::MaybeInitializeNetwork
, AsWeakPtr()));
270 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH
);
274 void StartupAppLauncher::OnFinishCrxInstall(const std::string
& extension_id
,
276 // Wait for pending updates or dependent extensions to download.
277 if (extensions::ExtensionSystem::Get(profile_
)
278 ->extension_service()
279 ->pending_extension_manager()
280 ->HasPendingExtensions()) {
284 extensions::InstallTracker
* tracker
=
285 extensions::InstallTrackerFactory::GetForBrowserContext(profile_
);
286 tracker
->RemoveObserver(this);
287 if (delegate_
->IsShowingNetworkConfigScreen()) {
288 LOG(WARNING
) << "Showing network config screen";
297 LOG(ERROR
) << "Failed to install the kiosk application app_id: "
299 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL
);
302 void StartupAppLauncher::OnKioskExtensionLoadedInCache(
303 const std::string
& app_id
) {
304 OnKioskAppDataLoadStatusChanged(app_id
);
307 void StartupAppLauncher::OnKioskExtensionDownloadFailed(
308 const std::string
& app_id
) {
309 OnKioskAppDataLoadStatusChanged(app_id
);
312 void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
313 const std::string
& app_id
) {
314 if (app_id
!= app_id_
|| !wait_for_crx_update_
)
317 wait_for_crx_update_
= false;
318 if (KioskAppManager::Get()->HasCachedCrx(app_id_
))
321 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD
);
324 void StartupAppLauncher::LaunchApp() {
325 if (!ready_to_launch_
) {
327 LOG(ERROR
) << "LaunchApp() called but launcher is not initialized.";
330 const Extension
* extension
= extensions::ExtensionSystem::Get(profile_
)->
331 extension_service()->GetInstalledExtension(app_id_
);
334 if (!extensions::KioskModeInfo::IsKioskEnabled(extension
)) {
335 OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED
);
339 // Always open the app in a window.
340 OpenApplication(AppLaunchParams(profile_
, extension
,
341 extensions::LAUNCH_CONTAINER_WINDOW
,
342 NEW_WINDOW
, extensions::SOURCE_KIOSK
));
343 InitAppSession(profile_
, app_id_
);
345 user_manager::UserManager::Get()->SessionStarted();
347 content::NotificationService::current()->Notify(
348 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED
,
349 content::NotificationService::AllSources(),
350 content::NotificationService::NoDetails());
352 if (diagnostic_mode_
)
353 KioskDiagnosisRunner::Run(profile_
, app_id_
);
358 void StartupAppLauncher::OnLaunchSuccess() {
359 delegate_
->OnLaunchSucceeded();
362 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error
) {
363 LOG(ERROR
) << "App launch failed, error: " << error
;
364 DCHECK_NE(KioskAppLaunchError::NONE
, error
);
366 delegate_
->OnLaunchFailed(error
);
369 void StartupAppLauncher::BeginInstall() {
370 KioskAppManager::Get()->InstallFromCache(app_id_
);
371 if (extensions::ExtensionSystem::Get(profile_
)
372 ->extension_service()
373 ->pending_extension_manager()
374 ->IsIdPending(app_id_
)) {
375 delegate_
->OnInstallingApp();
376 // Observe the crx installation events.
377 extensions::InstallTracker
* tracker
=
378 extensions::InstallTrackerFactory::GetForBrowserContext(profile_
);
379 tracker
->AddObserver(this);
383 if (extensions::ExtensionSystem::Get(profile_
)
384 ->extension_service()
385 ->GetInstalledExtension(app_id_
)) {
389 // The extension is skipped for installation due to some error.
390 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL
);
394 void StartupAppLauncher::OnReadyToLaunch() {
395 ready_to_launch_
= true;
397 delegate_
->OnReadyToLaunch();
400 void StartupAppLauncher::UpdateAppData() {
401 KioskAppManager::Get()->ClearAppData(app_id_
);
402 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_
, profile_
, NULL
);
405 } // namespace chromeos