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 402