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