startup_app_launcher.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/startup_app_launcher.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/shell.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h"
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/files/file_path.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/json/json_file_value_serializer.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/path_service.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/time.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/values.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/chromeos/login/user_manager.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/chromeos/ui/app_launch_view.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/webstore_startup_installer.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/lifetime/application_lifetime.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/signin/token_service.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/signin/token_service_factory.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/extensions/application_launch.h"
257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/common/chrome_notification_types.h"
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_paths.h"
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_switches.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/extension.h"
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/extensions/manifest_handlers/kiosk_enabled_info.h"
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/public/browser/notification_details.h"
327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/public/browser/notification_service.h"
337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/public/browser/notification_source.h"
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "google_apis/gaia/gaia_auth_consumer.h"
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "google_apis/gaia/gaia_constants.h"
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::Extension;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::WebstoreStartupInstaller;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace chromeos {
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kOAuthRefreshToken[] = "refresh_token";
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kOAuthClientId[] = "client_id";
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const char kOAuthClientSecret[] = "client_secret";
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const base::FilePath::CharType kOAuthFileName[] =
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    FILE_PATH_LITERAL("kiosk_auth");
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Application install splash screen minimum show time in milliseconds.
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kAppInstallSplashScreenMinTimeMS = 3000;
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsAppInstalled(Profile* profile, const std::string& app_id) {
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return extensions::ExtensionSystem::Get(profile)->extension_service()->
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetInstalledExtension(app_id);
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)StartupAppLauncher::StartupAppLauncher(Profile* profile,
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       const std::string& app_id)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : profile_(profile),
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_id_(app_id),
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      launch_splash_start_time_(0) {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(profile_);
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(Extension::IdIsValid(app_id_));
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(ash::Shell::HasInstance());
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ash::Shell::GetInstance()->AddPreTargetHandler(this);
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)StartupAppLauncher::~StartupAppLauncher() {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(ash::Shell::HasInstance());
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ash::Shell::GetInstance()->RemovePreTargetHandler(this);
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::Start() {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  launch_splash_start_time_ = base::TimeTicks::Now().ToInternalValue();
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "Starting... connection = "
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           <<  net::NetworkChangeNotifier::GetConnectionType();
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chromeos::ShowAppLaunchSplashScreen(app_id_);
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  StartLoadingOAuthFile();
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::StartLoadingOAuthFile() {
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  KioskOAuthParams* auth_params = new KioskOAuthParams();
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  BrowserThread::PostBlockingPoolTaskAndReply(
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      FROM_HERE,
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&StartupAppLauncher::LoadOAuthFileOnBlockingPool,
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 auth_params),
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&StartupAppLauncher::OnOAuthFileLoaded,
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 AsWeakPtr(),
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 base::Owned(auth_params)));
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static.
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::LoadOAuthFileOnBlockingPool(
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    KioskOAuthParams* auth_params) {
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string error_msg;
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath user_data_dir;
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath auth_file = user_data_dir.Append(kOAuthFileName);
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<JSONFileValueSerializer> serializer(
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new JSONFileValueSerializer(user_data_dir.Append(kOAuthFileName)));
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<base::Value> value(
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      serializer->Deserialize(&error_code, &error_msg));
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::DictionaryValue* dict = NULL;
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (error_code != JSONFileValueSerializer::JSON_NO_ERROR ||
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      !value.get() || !value->GetAsDictionary(&dict)) {
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LOG(WARNING) << "Can't find auth file at " << auth_file.value();
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  dict->GetString(kOAuthRefreshToken, &auth_params->refresh_token);
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  dict->GetString(kOAuthClientId, &auth_params->client_id);
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  dict->GetString(kOAuthClientSecret, &auth_params->client_secret);
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::OnOAuthFileLoaded(KioskOAuthParams* auth_params) {
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  auth_params_ = *auth_params;
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Override chrome client_id and secret that will be used for identity
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // API token minting.
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    UserManager::Get()->SetAppModeChromeClientOAuthInfo(
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            auth_params_.client_id,
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            auth_params_.client_secret);
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // If we are restarting chrome (i.e. on crash), we need to initialize
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TokenService as well.
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  InitializeTokenService();
134c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
135c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::InitializeNetwork() {
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chromeos::UpdateAppLaunchSplashScreenState(
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chromeos::APP_LAUNCH_STATE_PREPARING_NETWORK);
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Set a maximum allowed wait time for network.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kMaxNetworkWaitSeconds = 5 * 60;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  network_wait_timer_.Start(
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromSeconds(kMaxNetworkWaitSeconds),
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this, &StartupAppLauncher::OnNetworkWaitTimedout);
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnNetworkChanged(net::NetworkChangeNotifier::GetConnectionType());
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::InitializeTokenService() {
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  chromeos::UpdateAppLaunchSplashScreenState(
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chromeos::APP_LAUNCH_STATE_LOADING_TOKEN_SERVICE);
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TokenService* token_service =
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TokenServiceFactory::GetForProfile(profile_);
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (token_service->HasOAuthLoginToken()) {
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    InitializeNetwork();
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registrar_.Add(this,
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED,
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 content::Source<TokenService>(token_service));
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  registrar_.Add(this,
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 chrome::NOTIFICATION_TOKEN_AVAILABLE,
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 content::Source<TokenService>(token_service));
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  token_service->Initialize(GaiaConstants::kChromeSource, profile_);
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Pass oauth2 refresh token from the auth file.
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(zelidrag): We should probably remove this option after M27.
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!auth_params_.refresh_token.empty()) {
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    token_service->UpdateCredentialsWithOAuth2(
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        GaiaAuthConsumer::ClientOAuthResult(
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            auth_params_.refresh_token,
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            std::string(),  // access_token
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            0));            // new_expires_in_secs
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Load whatever tokens we have stored there last time around.
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    token_service->LoadTokensFromDB();
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void StartupAppLauncher::Observe(
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int type,
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::NotificationSource& source,
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::NotificationDetails& details) {
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  switch (type) {
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case chrome::NOTIFICATION_TOKEN_LOADING_FINISHED: {
188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      registrar_.RemoveAll();
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      InitializeNetwork();
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case chrome::NOTIFICATION_TOKEN_AVAILABLE: {
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      TokenService::TokenAvailableDetails* token_details =
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          content::Details<TokenService::TokenAvailableDetails>(
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              details).ptr();
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (token_details->service() ==
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              GaiaConstants::kGaiaOAuth2LoginRefreshToken) {
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        registrar_.RemoveAll();
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        InitializeNetwork();
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    default:
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      NOTREACHED();
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      break;
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::Cleanup() {
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chromeos::CloseAppLaunchSplashScreen();
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::OnLaunchSuccess() {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int64 time_taken_ms = (base::TimeTicks::Now() -
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeTicks::FromInternalValue(launch_splash_start_time_)).
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      InMilliseconds();
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Enforce that we show app install splash screen for some minimum amount
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // of time.
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (time_taken_ms < kAppInstallSplashScreenMinTimeMS) {
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserThread::PostDelayedTask(
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        BrowserThread::UI,
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE,
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&StartupAppLauncher::OnLaunchSuccess, AsWeakPtr()),
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::TimeDelta::FromMilliseconds(
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            kAppInstallSplashScreenMinTimeMS - time_taken_ms));
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Cleanup();
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_NE(KioskAppLaunchError::NONE, error);
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Saves the error and ends the session to go back to login screen.
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  KioskAppLaunchError::Save(error);
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::AttemptUserExit();
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Cleanup();
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::Launch() {
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      extension_service()->GetInstalledExtension(app_id_);
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(extension);
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!extensions::KioskEnabledInfo::IsKioskEnabled(extension)) {
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    OnLaunchFailure(KioskAppLaunchError::NOT_KIOSK_ENABLED);
252c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Always open the app in a window.
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome::OpenApplication(chrome::AppLaunchParams(profile_,
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  extension,
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  extension_misc::LAUNCH_WINDOW,
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                  NEW_WINDOW));
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  InitAppSession(profile_, app_id_);
261c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  content::NotificationService::current()->Notify(
263c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
264c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::NotificationService::AllSources(),
265c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::NotificationService::NoDetails());
266c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnLaunchSuccess();
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::BeginInstall() {
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "BeginInstall... connection = "
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           <<  net::NetworkChangeNotifier::GetConnectionType();
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chromeos::UpdateAppLaunchSplashScreenState(
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chromeos::APP_LAUNCH_STATE_INSTALLING_APPLICATION);
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (IsAppInstalled(profile_, app_id_)) {
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Launch();
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  installer_ = new WebstoreStartupInstaller(
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      app_id_,
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      profile_,
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      false,
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  installer_->BeginInstall();
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::InstallCallback(bool success,
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         const std::string& error) {
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  installer_ = NULL;
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (success) {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Schedules Launch() to be called after the callback returns.
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // So that the app finishes its installation.
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserThread::PostTask(
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        BrowserThread::UI,
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE,
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&StartupAppLauncher::Launch, AsWeakPtr()));
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LOG(ERROR) << "Failed to install app with error: " << error;
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::OnNetworkWaitTimedout() {
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LOG(WARNING) << "OnNetworkWaitTimedout... connection = "
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               <<  net::NetworkChangeNotifier::GetConnectionType();
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Timeout in waiting for online. Try the install anyway.
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BeginInstall();
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::OnNetworkChanged(
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    net::NetworkChangeNotifier::ConnectionType type) {
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "OnNetworkChanged... connection = "
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           <<  net::NetworkChangeNotifier::GetConnectionType();
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!net::NetworkChangeNotifier::IsOffline()) {
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Network up and running!";
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    network_wait_timer_.Stop();
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BeginInstall();
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "Network not running yet!";
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void StartupAppLauncher::OnKeyEvent(ui::KeyEvent* event) {
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->type() != ui::ET_KEY_PRESSED)
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (KioskAppManager::Get()->GetDisableBailoutShortcut())
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->key_code() != ui::VKEY_S ||
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !(event->flags() & ui::EF_CONTROL_DOWN) ||
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !(event->flags() & ui::EF_ALT_DOWN)) {
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OnLaunchFailure(KioskAppLaunchError::USER_CANCEL);
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}   // namespace chromeos
347