ChromeOS: Replace PO2TS::GetPrimaryAccountId() with SMB::GetAuthenticatedAccountId.
[chromium-blink-merge.git] / chrome / browser / chromeos / app_mode / startup_app_launcher.cc
blob5d1db9f6a614d15defb35fe09dce53323063de32
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/login/user_manager.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/extensions/updater/manifest_fetch_data.h"
20 #include "chrome/browser/extensions/updater/safe_manifest_parser.h"
21 #include "chrome/browser/extensions/webstore_startup_installer.h"
22 #include "chrome/browser/lifetime/application_lifetime.h"
23 #include "chrome/browser/signin/profile_oauth2_token_service.h"
24 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
25 #include "chrome/browser/signin/signin_manager.h"
26 #include "chrome/browser/signin/signin_manager_factory.h"
27 #include "chrome/browser/ui/extensions/application_launch.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/chrome_version_info.h"
31 #include "chrome/common/extensions/manifest_url_handler.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/notification_service.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
36 #include "google_apis/gaia/gaia_auth_consumer.h"
37 #include "google_apis/gaia/gaia_constants.h"
38 #include "net/base/load_flags.h"
39 #include "net/url_request/url_fetcher.h"
40 #include "net/url_request/url_fetcher_delegate.h"
41 #include "net/url_request/url_request_context_getter.h"
42 #include "net/url_request/url_request_status.h"
43 #include "url/gurl.h"
45 using content::BrowserThread;
46 using extensions::Extension;
47 using extensions::WebstoreStartupInstaller;
49 namespace chromeos {
51 namespace {
53 const char kOAuthRefreshToken[] = "refresh_token";
54 const char kOAuthClientId[] = "client_id";
55 const char kOAuthClientSecret[] = "client_secret";
57 const base::FilePath::CharType kOAuthFileName[] =
58 FILE_PATH_LITERAL("kiosk_auth");
60 } // namespace
62 class StartupAppLauncher::AppUpdateChecker
63 : public base::SupportsWeakPtr<AppUpdateChecker>,
64 public net::URLFetcherDelegate {
65 public:
66 explicit AppUpdateChecker(StartupAppLauncher* launcher)
67 : launcher_(launcher),
68 profile_(launcher->profile_),
69 app_id_(launcher->app_id_) {}
70 virtual ~AppUpdateChecker() {}
72 void Start() {
73 const Extension* app = GetInstalledApp();
74 if (!app) {
75 launcher_->OnUpdateCheckNotInstalled();
76 return;
79 GURL update_url = extensions::ManifestURL::GetUpdateURL(app);
80 if (update_url.is_empty())
81 update_url = extension_urls::GetWebstoreUpdateUrl();
82 if (!update_url.is_valid()) {
83 launcher_->OnUpdateCheckNoUpdate();
84 return;
87 manifest_fetch_data_.reset(
88 new extensions::ManifestFetchData(update_url, 0));
89 manifest_fetch_data_->AddExtension(
90 app_id_, app->version()->GetString(), NULL, "", "");
92 manifest_fetcher_.reset(net::URLFetcher::Create(
93 manifest_fetch_data_->full_url(), net::URLFetcher::GET, this));
94 manifest_fetcher_->SetRequestContext(profile_->GetRequestContext());
95 manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
96 net::LOAD_DO_NOT_SAVE_COOKIES |
97 net::LOAD_DISABLE_CACHE);
98 manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
99 manifest_fetcher_->Start();
102 private:
103 const Extension* GetInstalledApp() {
104 ExtensionService* extension_service =
105 extensions::ExtensionSystem::Get(profile_)->extension_service();
106 return extension_service->GetInstalledExtension(app_id_);
109 void HandleManifestResults(const extensions::ManifestFetchData& fetch_data,
110 const UpdateManifest::Results* results) {
111 if (!results || results->list.empty()) {
112 launcher_->OnUpdateCheckNoUpdate();
113 return;
116 DCHECK_EQ(1u, results->list.size());
118 const UpdateManifest::Result& update = results->list[0];
120 if (update.browser_min_version.length() > 0) {
121 Version browser_version;
122 chrome::VersionInfo version_info;
123 if (version_info.is_valid())
124 browser_version = Version(version_info.Version());
126 Version browser_min_version(update.browser_min_version);
127 if (browser_version.IsValid() &&
128 browser_min_version.IsValid() &&
129 browser_min_version.CompareTo(browser_version) > 0) {
130 launcher_->OnUpdateCheckNoUpdate();
131 return;
135 const Version& existing_version = *GetInstalledApp()->version();
136 Version update_version(update.version);
137 if (existing_version.IsValid() &&
138 update_version.IsValid() &&
139 update_version.CompareTo(existing_version) <= 0) {
140 launcher_->OnUpdateCheckNoUpdate();
141 return;
144 launcher_->OnUpdateCheckUpdateAvailable();
147 // net::URLFetcherDelegate implementation.
148 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
149 DCHECK_EQ(source, manifest_fetcher_.get());
151 if (source->GetStatus().status() != net::URLRequestStatus::SUCCESS ||
152 source->GetResponseCode() != 200) {
153 launcher_->OnUpdateCheckNoUpdate();
154 return;
157 std::string data;
158 source->GetResponseAsString(&data);
159 scoped_refptr<extensions::SafeManifestParser> safe_parser(
160 new extensions::SafeManifestParser(
161 data,
162 manifest_fetch_data_.release(),
163 base::Bind(&AppUpdateChecker::HandleManifestResults,
164 AsWeakPtr())));
165 safe_parser->Start();
168 StartupAppLauncher* launcher_;
169 Profile* profile_;
170 const std::string app_id_;
172 scoped_ptr<extensions::ManifestFetchData> manifest_fetch_data_;
173 scoped_ptr<net::URLFetcher> manifest_fetcher_;
175 DISALLOW_COPY_AND_ASSIGN(AppUpdateChecker);
178 StartupAppLauncher::StartupAppLauncher(Profile* profile,
179 const std::string& app_id,
180 StartupAppLauncher::Delegate* delegate)
181 : profile_(profile),
182 app_id_(app_id),
183 delegate_(delegate),
184 install_attempted_(false),
185 ready_to_launch_(false) {
186 DCHECK(profile_);
187 DCHECK(Extension::IdIsValid(app_id_));
190 StartupAppLauncher::~StartupAppLauncher() {
191 // StartupAppLauncher can be deleted at anytime during the launch process
192 // through a user bailout shortcut.
193 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
194 ->RemoveObserver(this);
197 void StartupAppLauncher::Initialize() {
198 StartLoadingOAuthFile();
201 void StartupAppLauncher::ContinueWithNetworkReady() {
202 // Starts install if it is not started.
203 if (!install_attempted_) {
204 install_attempted_ = true;
205 MaybeInstall();
209 void StartupAppLauncher::StartLoadingOAuthFile() {
210 delegate_->OnLoadingOAuthFile();
212 KioskOAuthParams* auth_params = new KioskOAuthParams();
213 BrowserThread::PostBlockingPoolTaskAndReply(
214 FROM_HERE,
215 base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
216 auth_params),
217 base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
218 AsWeakPtr(),
219 base::Owned(auth_params)));
222 // static.
223 void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
224 KioskOAuthParams* auth_params) {
225 int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
226 std::string error_msg;
227 base::FilePath user_data_dir;
228 CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
229 base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
230 scoped_ptr<JSONFileValueSerializer> serializer(
231 new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
232 scoped_ptr<base::Value> value(
233 serializer->Deserialize(&error_code, &error_msg));
234 base::DictionaryValue* dict = NULL;
235 if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
236 !value.get() || !value->GetAsDictionary(&dict)) {
237 LOG(WARNING) << "Can't find auth file at " << auth_file.value();
238 return;
241 dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
242 dict->GetString(kOAuthClientId, &auth_params->client_id);
243 dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
246 void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
247 auth_params_ = *auth_params;
248 // Override chrome client_id and secret that will be used for identity
249 // API token minting.
250 if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
251 UserManager::Get()->SetAppModeChromeClientOAuthInfo(
252 auth_params_.client_id,
253 auth_params_.client_secret);
256 // If we are restarting chrome (i.e. on crash), we need to initialize
257 // OAuth2TokenService as well.
258 InitializeTokenService();
261 void StartupAppLauncher::InitializeNetwork() {
262 delegate_->InitializeNetwork();
265 void StartupAppLauncher::InitializeTokenService() {
266 delegate_->OnInitializingTokenService();
268 ProfileOAuth2TokenService* profile_token_service =
269 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
270 SigninManagerBase* signin_manager =
271 SigninManagerFactory::GetForProfile(profile_);
272 if (profile_token_service->RefreshTokenIsAvailable(
273 signin_manager->GetAuthenticatedAccountId()) ||
274 auth_params_.refresh_token.empty()) {
275 InitializeNetwork();
276 } else {
277 // Pass oauth2 refresh token from the auth file.
278 // TODO(zelidrag): We should probably remove this option after M27.
279 // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
280 // Unless the code is no longer needed.
281 // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
282 // this code still needed? See above two TODOs.
284 // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
285 // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
286 // whichever comes first, both handlers call RemoveObserver on PO2TS.
287 // Handling any of the two events is the only way to resume the execution
288 // and enable Cleanup method to be called, self-invoking a destructor.
289 profile_token_service->AddObserver(this);
291 profile_token_service->UpdateCredentials(
292 "kiosk_mode@localhost",
293 auth_params_.refresh_token);
297 void StartupAppLauncher::OnRefreshTokenAvailable(
298 const std::string& account_id) {
299 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
300 ->RemoveObserver(this);
301 InitializeNetwork();
304 void StartupAppLauncher::OnRefreshTokensLoaded() {
305 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
306 ->RemoveObserver(this);
307 InitializeNetwork();
310 void StartupAppLauncher::LaunchApp() {
311 if (!ready_to_launch_) {
312 NOTREACHED();
313 LOG(ERROR) << "LaunchApp() called but launcher is not initialized.";
316 const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
317 extension_service()->GetInstalledExtension(app_id_);
318 CHECK(extension);
320 if (!extensions::KioskModeInfo::IsKioskEnabled(extension)) {
321 OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
322 return;
325 // Always open the app in a window.
326 OpenApplication(AppLaunchParams(profile_, extension,
327 extensions::LAUNCH_CONTAINER_WINDOW,
328 NEW_WINDOW));
329 InitAppSession(profile_, app_id_);
331 UserManager::Get()->SessionStarted();
333 content::NotificationService::current()->Notify(
334 chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
335 content::NotificationService::AllSources(),
336 content::NotificationService::NoDetails());
338 OnLaunchSuccess();
341 void StartupAppLauncher::OnLaunchSuccess() {
342 delegate_->OnLaunchSucceeded();
345 void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
346 LOG(ERROR) << "App launch failed, error: " << error;
347 DCHECK_NE(KioskAppLaunchError::NONE, error);
349 delegate_->OnLaunchFailed(error);
352 void StartupAppLauncher::MaybeInstall() {
353 delegate_->OnInstallingApp();
355 update_checker_.reset(new AppUpdateChecker(this));
356 update_checker_->Start();
359 void StartupAppLauncher::OnUpdateCheckNotInstalled() {
360 BeginInstall();
363 void StartupAppLauncher::OnUpdateCheckUpdateAvailable() {
364 // Uninstall to force a re-install.
365 // TODO(xiyuan): Find a better way. Either download CRX and install it
366 // directly or integrate with ExtensionUpdater in someway.
367 ExtensionService* extension_service =
368 extensions::ExtensionSystem::Get(profile_)->extension_service();
369 extension_service->UninstallExtension(app_id_, false, NULL);
371 OnUpdateCheckNotInstalled();
374 void StartupAppLauncher::OnUpdateCheckNoUpdate() {
375 OnReadyToLaunch();
378 void StartupAppLauncher::BeginInstall() {
379 installer_ = new WebstoreStartupInstaller(
380 app_id_,
381 profile_,
382 false,
383 base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
384 installer_->BeginInstall();
387 void StartupAppLauncher::InstallCallback(bool success,
388 const std::string& error) {
389 installer_ = NULL;
390 if (success) {
391 // Finish initialization after the callback returns.
392 // So that the app finishes its installation.
393 BrowserThread::PostTask(
394 BrowserThread::UI,
395 FROM_HERE,
396 base::Bind(&StartupAppLauncher::OnReadyToLaunch,
397 AsWeakPtr()));
399 // Schedule app data update after installation.
400 BrowserThread::PostTask(
401 BrowserThread::UI,
402 FROM_HERE,
403 base::Bind(&StartupAppLauncher::UpdateAppData,
404 AsWeakPtr()));
405 return;
408 LOG(ERROR) << "App install failed: " << error;
409 OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
412 void StartupAppLauncher::OnReadyToLaunch() {
413 ready_to_launch_ = true;
414 delegate_->OnReadyToLaunch();
417 void StartupAppLauncher::UpdateAppData() {
418 KioskAppManager::Get()->ClearAppData(app_id_);
419 KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
422 } // namespace chromeos