#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/app_mode/app_session_lifetime.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
-#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.h"
+#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
-#include "chrome/browser/extensions/webstore_startup_installer.h"
+#include "chrome/browser/extensions/install_tracker.h"
+#include "chrome/browser/extensions/install_tracker_factory.h"
+#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/lifetime/application_lifetime.h"
-#include "chrome/browser/signin/profile_oauth2_token_service.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/token_service.h"
-#include "chrome/browser/signin/token_service_factory.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/manifest_handlers/kiosk_mode_info.h"
+#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/extensions/manifest_url_handler.h"
+#include "components/crx_file/id_util.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
+#include "extensions/common/manifest_handlers/offline_enabled_info.h"
#include "google_apis/gaia/gaia_auth_consumer.h"
#include "google_apis/gaia/gaia_constants.h"
+#include "net/base/load_flags.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
using content::BrowserThread;
using extensions::Extension;
-using extensions::WebstoreStartupInstaller;
namespace chromeos {
const base::FilePath::CharType kOAuthFileName[] =
FILE_PATH_LITERAL("kiosk_auth");
-bool IsAppInstalled(Profile* profile, const std::string& app_id) {
- return extensions::ExtensionSystem::Get(profile)->extension_service()->
- GetInstalledExtension(app_id);
-}
+const int kMaxLaunchAttempt = 5;
} // namespace
-
StartupAppLauncher::StartupAppLauncher(Profile* profile,
- const std::string& app_id)
+ const std::string& app_id,
+ bool diagnostic_mode,
+ StartupAppLauncher::Delegate* delegate)
: profile_(profile),
app_id_(app_id),
- ready_to_launch_(false) {
+ diagnostic_mode_(diagnostic_mode),
+ delegate_(delegate),
+ network_ready_handled_(false),
+ launch_attempt_(0),
+ ready_to_launch_(false),
+ wait_for_crx_update_(false) {
DCHECK(profile_);
- DCHECK(Extension::IdIsValid(app_id_));
+ DCHECK(crx_file::id_util::IdIsValid(app_id_));
+ KioskAppManager::Get()->AddObserver(this);
}
StartupAppLauncher::~StartupAppLauncher() {
+ KioskAppManager::Get()->RemoveObserver(this);
+
// StartupAppLauncher can be deleted at anytime during the launch process
// through a user bailout shortcut.
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
->RemoveObserver(this);
- net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void StartupAppLauncher::Initialize() {
- DVLOG(1) << "Starting... connection = "
- << net::NetworkChangeNotifier::GetConnectionType();
StartLoadingOAuthFile();
}
-void StartupAppLauncher::AddObserver(Observer* observer) {
- observer_list_.AddObserver(observer);
-}
-
-void StartupAppLauncher::RemoveObserver(Observer* observer) {
- observer_list_.RemoveObserver(observer);
+void StartupAppLauncher::ContinueWithNetworkReady() {
+ if (!network_ready_handled_) {
+ network_ready_handled_ = true;
+ // The network might not be ready when KioskAppManager tries to update
+ // external cache initially. Update the external cache now that the network
+ // is ready for sure.
+ wait_for_crx_update_ = true;
+ KioskAppManager::Get()->UpdateExternalCache();
+ }
}
void StartupAppLauncher::StartLoadingOAuthFile() {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnLoadingOAuthFile());
+ delegate_->OnLoadingOAuthFile();
KioskOAuthParams* auth_params = new KioskOAuthParams();
BrowserThread::PostBlockingPoolTaskAndReply(
// Override chrome client_id and secret that will be used for identity
// API token minting.
if (!auth_params_.client_id.empty() && !auth_params_.client_secret.empty()) {
- UserManager::Get()->SetAppModeChromeClientOAuthInfo(
+ UserSessionManager::GetInstance()->SetAppModeChromeClientOAuthInfo(
auth_params_.client_id,
auth_params_.client_secret);
}
// If we are restarting chrome (i.e. on crash), we need to initialize
- // TokenService as well.
+ // OAuth2TokenService as well.
InitializeTokenService();
}
-void StartupAppLauncher::InitializeNetwork() {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingNetwork());
+void StartupAppLauncher::RestartLauncher() {
+ // If the installer is still running in the background, we don't need to
+ // restart the launch process. We will just wait until it completes and
+ // launches the kiosk app.
+ if (extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->pending_extension_manager()
+ ->IsIdPending(app_id_)) {
+ LOG(WARNING) << "Installer still running";
+ return;
+ }
- // TODO(tengs): Use NetworkStateInformer instead because it can handle
- // portal and proxy detection. We will need to do some refactoring to
- // make NetworkStateInformer more independent from the WebUI handlers.
- net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
- OnNetworkChanged(net::NetworkChangeNotifier::GetConnectionType());
+ MaybeInitializeNetwork();
}
-void StartupAppLauncher::InitializeTokenService() {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnInitializingTokenService());
+void StartupAppLauncher::MaybeInitializeNetwork() {
+ network_ready_handled_ = false;
- ProfileOAuth2TokenService* profile_token_service =
- ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
- if (profile_token_service->RefreshTokenIsAvailable(
- profile_token_service->GetPrimaryAccountId())) {
- InitializeNetwork();
+ const Extension* extension = extensions::ExtensionSystem::Get(profile_)->
+ extension_service()->GetInstalledExtension(app_id_);
+ bool crx_cached = KioskAppManager::Get()->HasCachedCrx(app_id_);
+ const bool requires_network =
+ (!extension && !crx_cached) ||
+ (extension &&
+ !extensions::OfflineEnabledInfo::IsOfflineEnabled(extension));
+
+ if (requires_network) {
+ delegate_->InitializeNetwork();
return;
}
- // At the end of this method, the execution will be put on hold until
- // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
- // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
- // whichever comes first, both handlers call RemoveObserver on PO2TS. Handling
- // any of the two events is the only way to resume the execution and enable
- // Cleanup method to be called, self-invoking a destructor. In destructor
- // StartupAppLauncher is no longer an observer of PO2TS and there is no need
- // to call RemoveObserver again.
- profile_token_service->AddObserver(this);
-
- TokenService* token_service = TokenServiceFactory::GetForProfile(profile_);
- token_service->Initialize(GaiaConstants::kChromeSource, profile_);
-
- // Pass oauth2 refresh token from the auth file.
- // TODO(zelidrag): We should probably remove this option after M27.
- // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
- // Unless the code is no longer needed.
- if (!auth_params_.refresh_token.empty()) {
- token_service->UpdateCredentialsWithOAuth2(
- GaiaAuthConsumer::ClientOAuthResult(
- auth_params_.refresh_token,
- std::string(), // access_token
- 0)); // new_expires_in_secs
+ // Update the offline enabled crx cache if the network is ready;
+ // or just install the app.
+ if (delegate_->IsNetworkReady())
+ ContinueWithNetworkReady();
+ else
+ BeginInstall();
+}
+
+void StartupAppLauncher::InitializeTokenService() {
+ delegate_->OnInitializingTokenService();
+
+ ProfileOAuth2TokenService* profile_token_service =
+ ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
+ SigninManagerBase* signin_manager =
+ SigninManagerFactory::GetForProfile(profile_);
+ const std::string primary_account_id =
+ signin_manager->GetAuthenticatedAccountId();
+ if (profile_token_service->RefreshTokenIsAvailable(primary_account_id) ||
+ auth_params_.refresh_token.empty()) {
+ MaybeInitializeNetwork();
} else {
- // Load whatever tokens we have stored there last time around.
- token_service->LoadTokensFromDB();
+ // Pass oauth2 refresh token from the auth file.
+ // TODO(zelidrag): We should probably remove this option after M27.
+ // TODO(fgorski): This can go when we have persistence implemented on PO2TS.
+ // Unless the code is no longer needed.
+ // TODO(rogerta): Now that this CL implements token persistence in PO2TS, is
+ // this code still needed? See above two TODOs.
+ //
+ // ProfileOAuth2TokenService triggers either OnRefreshTokenAvailable or
+ // OnRefreshTokensLoaded. Given that we want to handle exactly one event,
+ // whichever comes first, both handlers call RemoveObserver on PO2TS.
+ // Handling any of the two events is the only way to resume the execution
+ // and enable Cleanup method to be called, self-invoking a destructor.
+ profile_token_service->AddObserver(this);
+
+ profile_token_service->UpdateCredentials(
+ primary_account_id,
+ auth_params_.refresh_token);
}
}
const std::string& account_id) {
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
->RemoveObserver(this);
- InitializeNetwork();
+ MaybeInitializeNetwork();
}
void StartupAppLauncher::OnRefreshTokensLoaded() {
ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)
->RemoveObserver(this);
- InitializeNetwork();
+ MaybeInitializeNetwork();
}
-void StartupAppLauncher::OnLaunchSuccess() {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchSucceeded());
+void StartupAppLauncher::MaybeLaunchApp() {
+ // Check if the app is offline enabled.
+ const Extension* extension = extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->GetInstalledExtension(app_id_);
+ DCHECK(extension);
+ const bool offline_enabled =
+ extensions::OfflineEnabledInfo::IsOfflineEnabled(extension);
+ if (offline_enabled || delegate_->IsNetworkReady()) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&StartupAppLauncher::OnReadyToLaunch, AsWeakPtr()));
+ } else {
+ ++launch_attempt_;
+ if (launch_attempt_ < kMaxLaunchAttempt) {
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&StartupAppLauncher::MaybeInitializeNetwork, AsWeakPtr()));
+ return;
+ }
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_LAUNCH);
+ }
}
-void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
- LOG(ERROR) << "App launch failed, error: " << error;
- DCHECK_NE(KioskAppLaunchError::NONE, error);
+void StartupAppLauncher::OnFinishCrxInstall(const std::string& extension_id,
+ bool success) {
+ if (extension_id != app_id_)
+ return;
- FOR_EACH_OBSERVER(Observer, observer_list_, OnLaunchFailed(error));
+ extensions::InstallTracker* tracker =
+ extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+ tracker->RemoveObserver(this);
+ if (delegate_->IsShowingNetworkConfigScreen()) {
+ LOG(WARNING) << "Showing network config screen";
+ return;
+ }
+
+ if (success) {
+ MaybeLaunchApp();
+ return;
+ }
+
+ LOG(ERROR) << "Failed to install the kiosk application app_id: "
+ << extension_id;
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+}
+
+void StartupAppLauncher::OnKioskExtensionLoadedInCache(
+ const std::string& app_id) {
+ OnKioskAppDataLoadStatusChanged(app_id);
+}
+
+void StartupAppLauncher::OnKioskExtensionDownloadFailed(
+ const std::string& app_id) {
+ OnKioskAppDataLoadStatusChanged(app_id);
+}
+
+void StartupAppLauncher::OnKioskAppDataLoadStatusChanged(
+ const std::string& app_id) {
+ if (app_id != app_id_ || !wait_for_crx_update_)
+ return;
+
+ wait_for_crx_update_ = false;
+ if (KioskAppManager::Get()->HasCachedCrx(app_id_))
+ BeginInstall();
+ else
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_DOWNLOAD);
}
void StartupAppLauncher::LaunchApp() {
// Always open the app in a window.
OpenApplication(AppLaunchParams(profile_, extension,
- extension_misc::LAUNCH_WINDOW, NEW_WINDOW));
+ extensions::LAUNCH_CONTAINER_WINDOW,
+ NEW_WINDOW));
InitAppSession(profile_, app_id_);
- UserManager::Get()->SessionStarted();
+ user_manager::UserManager::Get()->SessionStarted();
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_KIOSK_APP_LAUNCHED,
content::NotificationService::AllSources(),
content::NotificationService::NoDetails());
+ if (diagnostic_mode_)
+ KioskDiagnosisRunner::Run(profile_, app_id_);
+
OnLaunchSuccess();
}
-void StartupAppLauncher::BeginInstall() {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnInstallingApp());
+void StartupAppLauncher::OnLaunchSuccess() {
+ delegate_->OnLaunchSucceeded();
+}
- DVLOG(1) << "BeginInstall... connection = "
- << net::NetworkChangeNotifier::GetConnectionType();
+void StartupAppLauncher::OnLaunchFailure(KioskAppLaunchError::Error error) {
+ LOG(ERROR) << "App launch failed, error: " << error;
+ DCHECK_NE(KioskAppLaunchError::NONE, error);
- if (IsAppInstalled(profile_, app_id_)) {
- OnReadyToLaunch();
- return;
- }
+ delegate_->OnLaunchFailed(error);
+}
- installer_ = new WebstoreStartupInstaller(
- app_id_,
- profile_,
- false,
- base::Bind(&StartupAppLauncher::InstallCallback, AsWeakPtr()));
- installer_->BeginInstall();
+void StartupAppLauncher::OnUpdateCheckFinished() {
+ OnReadyToLaunch();
+ UpdateAppData();
}
-void StartupAppLauncher::InstallCallback(bool success,
- const std::string& error) {
- installer_ = NULL;
- if (success) {
- // Finish initialization after the callback returns.
- // So that the app finishes its installation.
- BrowserThread::PostTask(
- BrowserThread::UI,
- FROM_HERE,
- base::Bind(&StartupAppLauncher::OnReadyToLaunch,
- AsWeakPtr()));
+void StartupAppLauncher::BeginInstall() {
+ KioskAppManager::Get()->InstallFromCache(app_id_);
+ if (extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->pending_extension_manager()
+ ->IsIdPending(app_id_)) {
+ delegate_->OnInstallingApp();
+ // Observe the crx installation events.
+ extensions::InstallTracker* tracker =
+ extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+ tracker->AddObserver(this);
return;
}
- LOG(ERROR) << "App install failed: " << error;
- OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ if (extensions::ExtensionSystem::Get(profile_)
+ ->extension_service()
+ ->GetInstalledExtension(app_id_)) {
+ // Launch the app.
+ OnReadyToLaunch();
+ } else {
+ // The extension is skipped for installation due to some error.
+ OnLaunchFailure(KioskAppLaunchError::UNABLE_TO_INSTALL);
+ }
}
void StartupAppLauncher::OnReadyToLaunch() {
ready_to_launch_ = true;
- FOR_EACH_OBSERVER(Observer, observer_list_, OnReadyToLaunch());
+ delegate_->OnReadyToLaunch();
}
-void StartupAppLauncher::OnNetworkChanged(
- net::NetworkChangeNotifier::ConnectionType type) {
- DVLOG(1) << "OnNetworkChanged... connection = "
- << net::NetworkChangeNotifier::GetConnectionType();
- if (!net::NetworkChangeNotifier::IsOffline()) {
- DVLOG(1) << "Network up and running!";
- net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
-
- BeginInstall();
- } else {
- DVLOG(1) << "Network not running yet!";
- }
+void StartupAppLauncher::UpdateAppData() {
+ KioskAppManager::Get()->ClearAppData(app_id_);
+ KioskAppManager::Get()->UpdateAppDataFromProfile(app_id_, profile_, NULL);
}
} // namespace chromeos