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