Commit 876cffa2 authored by Anatoliy Potapchuk's avatar Anatoliy Potapchuk Committed by Commit Bot

[Kiosk] Restart Web App Kiosk in case of browser crash

Previously, if the browser process crashed during Web kiosk session, we
would have just exited the session. This cl leverages the new kiosk
launch logic, where we abstract out KioskAppLauncher.

Bug: 1054382
Change-Id: Ia8bed81f839b7f7f81d703e8a03af58e768d0fb9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2279121
Commit-Queue: Anatoliy Potapchuk <apotapchuk@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarAnqing Zhao <anqing@chromium.org>
Cr-Commit-Position: refs/heads/master@{#786287}
parent 35dc9176
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_types.h"
#include "chrome/browser/chromeos/app_mode/startup_app_launcher.h" #include "chrome/browser/chromeos/app_mode/startup_app_launcher.h"
#include "chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
...@@ -31,25 +33,28 @@ std::vector<std::string>* test_prefs_to_reset = nullptr; ...@@ -31,25 +33,28 @@ std::vector<std::string>* test_prefs_to_reset = nullptr;
// it exits the browser process. // it exits the browser process.
class AppLaunchManager : public StartupAppLauncher::Delegate { class AppLaunchManager : public StartupAppLauncher::Delegate {
public: public:
AppLaunchManager(Profile* profile, const std::string& app_id) AppLaunchManager(Profile* profile, const KioskAppId& kiosk_app_id) {
: startup_app_launcher_( CHECK(kiosk_app_id.type != KioskAppType::ARC_APP);
new StartupAppLauncher(profile,
app_id, if (kiosk_app_id.type == KioskAppType::CHROME_APP)
this)) {} app_launcher_ = std::make_unique<StartupAppLauncher>(
profile, *kiosk_app_id.app_id, this);
else
app_launcher_ = std::make_unique<WebKioskAppLauncher>(
profile, this, *kiosk_app_id.account_id);
}
void Start() { startup_app_launcher_->Initialize(); } void Start() { app_launcher_->Initialize(); }
private: private:
~AppLaunchManager() override {} ~AppLaunchManager() override {}
void Cleanup() { delete this; } void Cleanup() { delete this; }
// StartupAppLauncher::Delegate overrides: // KioskAppLauncher::Delegate:
void InitializeNetwork() override { void InitializeNetwork() override {
// This is on crash-restart path and assumes network is online. // This is on crash-restart path and assumes network is online.
// TODO(xiyuan): Remove the crash-restart path for kiosk or add proper app_launcher_->ContinueWithNetworkReady();
// network configure handling.
startup_app_launcher_->ContinueWithNetworkReady();
} }
bool IsNetworkReady() const override { bool IsNetworkReady() const override {
// See comments above. Network is assumed to be online here. // See comments above. Network is assumed to be online here.
...@@ -63,7 +68,7 @@ class AppLaunchManager : public StartupAppLauncher::Delegate { ...@@ -63,7 +68,7 @@ class AppLaunchManager : public StartupAppLauncher::Delegate {
return true; return true;
} }
void OnAppInstalling() override {} void OnAppInstalling() override {}
void OnAppPrepared() override { startup_app_launcher_->LaunchApp(); } void OnAppPrepared() override { app_launcher_->LaunchApp(); }
void OnAppLaunched() override {} void OnAppLaunched() override {}
void OnAppWindowCreated() override { Cleanup(); } void OnAppWindowCreated() override { Cleanup(); }
void OnLaunchFailed(KioskAppLaunchError::Error error) override { void OnLaunchFailed(KioskAppLaunchError::Error error) override {
...@@ -73,14 +78,14 @@ class AppLaunchManager : public StartupAppLauncher::Delegate { ...@@ -73,14 +78,14 @@ class AppLaunchManager : public StartupAppLauncher::Delegate {
} }
bool IsShowingNetworkConfigScreen() const override { return false; } bool IsShowingNetworkConfigScreen() const override { return false; }
std::unique_ptr<KioskAppLauncher> startup_app_launcher_; std::unique_ptr<KioskAppLauncher> app_launcher_;
DISALLOW_COPY_AND_ASSIGN(AppLaunchManager); DISALLOW_COPY_AND_ASSIGN(AppLaunchManager);
}; };
void LaunchAppOrDie(Profile* profile, const std::string& app_id) { void LaunchAppOrDie(Profile* profile, const KioskAppId& kiosk_app_id) {
// AppLaunchManager manages its own lifetime. // AppLaunchManager manages its own lifetime.
(new AppLaunchManager(profile, app_id))->Start(); (new AppLaunchManager(profile, kiosk_app_id))->Start();
} }
void ResetEphemeralKioskPreferences(PrefService* prefs) { void ResetEphemeralKioskPreferences(PrefService* prefs) {
......
...@@ -12,11 +12,13 @@ class Profile; ...@@ -12,11 +12,13 @@ class Profile;
namespace chromeos { namespace chromeos {
// Attempts to launch the app given by |app_id| in app mode class KioskAppId;
// Attempts to launch the app given by |kiosk_app_id| in app mode
// or exit on failure. This function will not show any launch UI // or exit on failure. This function will not show any launch UI
// during the launch. Use StartupAppLauncher for finer control // during the launch. Use StartupAppLauncher for finer control
// over the app launch processes. // over the app launch processes.
void LaunchAppOrDie(Profile* profile, const std::string& app_id); void LaunchAppOrDie(Profile* profile, const KioskAppId& kiosk_app_id);
// Removes obsolete preferences left out by previous user session; // Removes obsolete preferences left out by previous user session;
void ResetEphemeralKioskPreferences(PrefService* prefs); void ResetEphemeralKioskPreferences(PrefService* prefs);
......
...@@ -30,6 +30,11 @@ class KioskAppId { ...@@ -30,6 +30,11 @@ class KioskAppId {
static KioskAppId ForWebApp(const AccountId& account_id); static KioskAppId ForWebApp(const AccountId& account_id);
static KioskAppId ForArcApp(const AccountId& account_id); static KioskAppId ForArcApp(const AccountId& account_id);
// Use this method when we are unsure which type of kiosk app this AccountId
// belongs to.
static bool FromAccountId(const AccountId& account_id,
KioskAppId* kiosk_app_id);
private: private:
KioskAppId(KioskAppType type, const std::string& app_id); KioskAppId(KioskAppType type, const std::string& app_id);
KioskAppId(KioskAppType type, const AccountId& account_id); KioskAppId(KioskAppType type, const AccountId& account_id);
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "chrome/browser/chromeos/app_mode/fake_cws.h" #include "chrome/browser/chromeos/app_mode/fake_cws.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/login/test/kiosk_test_helpers.h"
#include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h" #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
#include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/device_policy_builder.h" #include "chrome/browser/chromeos/policy/device_policy_builder.h"
...@@ -32,12 +33,18 @@ ...@@ -32,12 +33,18 @@
#include "net/dns/mock_host_resolver.h" #include "net/dns/mock_host_resolver.h"
namespace em = enterprise_management; namespace em = enterprise_management;
constexpr em::DeviceLocalAccountInfoProto_AccountType kWebKioskAccountType =
em::DeviceLocalAccountInfoProto_AccountType_ACCOUNT_TYPE_WEB_KIOSK_APP;
constexpr em::DeviceLocalAccountInfoProto_AccountType kKioskAccountType =
em::DeviceLocalAccountInfoProto_AccountType_ACCOUNT_TYPE_KIOSK_APP;
namespace chromeos { namespace chromeos {
namespace { namespace {
const char kTestKioskApp[] = "ggaeimfdpnmlhdhpcikgoblffmkckdmn"; const char kTestKioskApp[] = "ggaeimfdpnmlhdhpcikgoblffmkckdmn";
const char kTestWebAppId[] = "id";
const char kTestWebAppUrl[] = "https://example.com/";
} // namespace } // namespace
...@@ -83,7 +90,8 @@ class KioskCrashRestoreTest : public InProcessBrowserTest { ...@@ -83,7 +90,8 @@ class KioskCrashRestoreTest : public InProcessBrowserTest {
CryptohomeClient::GetStubSanitizedUsername(cryptohome_id)); CryptohomeClient::GetStubSanitizedUsername(cryptohome_id));
fake_cws_->Init(embedded_test_server()); fake_cws_->Init(embedded_test_server());
fake_cws_->SetUpdateCrx(test_app_id_, test_app_id_ + ".crx", "1.0.0"); fake_cws_->SetUpdateCrx(kTestKioskApp, std::string(kTestKioskApp) + ".crx",
"1.0.0");
} }
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
...@@ -93,12 +101,7 @@ class KioskCrashRestoreTest : public InProcessBrowserTest { ...@@ -93,12 +101,7 @@ class KioskCrashRestoreTest : public InProcessBrowserTest {
embedded_test_server()->StartAcceptingConnections(); embedded_test_server()->StartAcceptingConnections();
} }
const std::string GetTestAppUserId() const { virtual const std::string GetTestAppUserId() const = 0;
return policy::GenerateDeviceLocalAccountUserId(
test_app_id_, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
}
const std::string& test_app_id() const { return test_app_id_; }
private: private:
void SetUpExistingKioskApp() { void SetUpExistingKioskApp() {
...@@ -106,12 +109,20 @@ class KioskCrashRestoreTest : public InProcessBrowserTest { ...@@ -106,12 +109,20 @@ class KioskCrashRestoreTest : public InProcessBrowserTest {
em::DeviceLocalAccountsProto* const device_local_accounts = em::DeviceLocalAccountsProto* const device_local_accounts =
device_policy_.payload().mutable_device_local_accounts(); device_policy_.payload().mutable_device_local_accounts();
em::DeviceLocalAccountInfoProto* const account = {
device_local_accounts->add_account(); em::DeviceLocalAccountInfoProto* const account =
account->set_account_id(test_app_id_); device_local_accounts->add_account();
account->set_type( account->set_account_id(kTestKioskApp);
em::DeviceLocalAccountInfoProto_AccountType_ACCOUNT_TYPE_KIOSK_APP); account->set_type(kKioskAccountType);
account->mutable_kiosk_app()->set_app_id(test_app_id_); account->mutable_kiosk_app()->set_app_id(kTestKioskApp);
}
{
em::DeviceLocalAccountInfoProto* const account =
device_local_accounts->add_account();
account->set_account_id(kTestWebAppId);
account->set_type(kWebKioskAccountType);
account->mutable_web_kiosk_app()->set_url(kTestWebAppUrl);
}
device_policy_.Build(); device_policy_.Build();
// Prepare the policy data to store in device policy cache. // Prepare the policy data to store in device policy cache.
...@@ -136,8 +147,6 @@ class KioskCrashRestoreTest : public InProcessBrowserTest { ...@@ -136,8 +147,6 @@ class KioskCrashRestoreTest : public InProcessBrowserTest {
base::WriteFile(local_state_file, local_state_json); base::WriteFile(local_state_file, local_state_json);
} }
std::string test_app_id_ = kTestKioskApp;
policy::DevicePolicyBuilder device_policy_; policy::DevicePolicyBuilder device_policy_;
scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_; scoped_refptr<ownership::MockOwnerKeyUtil> owner_key_util_;
std::unique_ptr<FakeCWS> fake_cws_; std::unique_ptr<FakeCWS> fake_cws_;
...@@ -145,11 +154,33 @@ class KioskCrashRestoreTest : public InProcessBrowserTest { ...@@ -145,11 +154,33 @@ class KioskCrashRestoreTest : public InProcessBrowserTest {
DISALLOW_COPY_AND_ASSIGN(KioskCrashRestoreTest); DISALLOW_COPY_AND_ASSIGN(KioskCrashRestoreTest);
}; };
IN_PROC_BROWSER_TEST_F(KioskCrashRestoreTest, AppNotInstalled) { class ChromeKioskCrashRestoreTest : public KioskCrashRestoreTest {
const std::string GetTestAppUserId() const override {
return policy::GenerateDeviceLocalAccountUserId(
kTestKioskApp, policy::DeviceLocalAccount::TYPE_KIOSK_APP);
}
};
IN_PROC_BROWSER_TEST_F(ChromeKioskCrashRestoreTest, ChromeAppNotInstalled) {
// If app is not installed when restoring from crash, the kiosk launch is // If app is not installed when restoring from crash, the kiosk launch is
// expected to fail, as in that case the crash occured during the app // expected to fail, as in that case the crash occured during the app
// initialization - before the app was actually launched. // initialization - before the app was actually launched.
EXPECT_EQ(KioskAppLaunchError::UNABLE_TO_LAUNCH, KioskAppLaunchError::Get()); EXPECT_EQ(KioskAppLaunchError::UNABLE_TO_LAUNCH, KioskAppLaunchError::Get());
} }
class WebKioskCrashRestoreTest : public KioskCrashRestoreTest {
const std::string GetTestAppUserId() const override {
return policy::GenerateDeviceLocalAccountUserId(
kTestWebAppId, policy::DeviceLocalAccount::TYPE_WEB_KIOSK_APP);
}
};
IN_PROC_BROWSER_TEST_F(WebKioskCrashRestoreTest, WebKioskLaunches) {
// If app is not installed when restoring from crash, the kiosk launch is
// expected to fail, as in that case the crash occured during the app
// initialization - before the app was actually launched.
EXPECT_EQ(KioskAppLaunchError::NONE, KioskAppLaunchError::Get());
KioskSessionInitializedWaiter().Wait();
}
} // namespace chromeos } // namespace chromeos
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include "chrome/browser/app_mode/app_mode_utils.h" #include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/apps/platform_apps/app_load_service.h" #include "chrome/browser/apps/platform_apps/app_load_service.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_types.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/extensions/startup_helper.h" #include "chrome/browser/extensions/startup_helper.h"
#include "chrome/browser/first_run/first_run.h" #include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/prefs/incognito_mode_prefs.h"
...@@ -116,9 +118,7 @@ namespace { ...@@ -116,9 +118,7 @@ namespace {
class ProfileLaunchObserver : public ProfileObserver, class ProfileLaunchObserver : public ProfileObserver,
public BrowserListObserver { public BrowserListObserver {
public: public:
ProfileLaunchObserver() { ProfileLaunchObserver() { BrowserList::AddObserver(this); }
BrowserList::AddObserver(this);
}
~ProfileLaunchObserver() override { BrowserList::RemoveObserver(this); } ~ProfileLaunchObserver() override { BrowserList::RemoveObserver(this); }
...@@ -656,25 +656,30 @@ bool StartupBrowserCreator::ProcessCmdLineImpl( ...@@ -656,25 +656,30 @@ bool StartupBrowserCreator::ProcessCmdLineImpl(
if (command_line.HasSwitch(chromeos::switches::kLoginManager)) if (command_line.HasSwitch(chromeos::switches::kLoginManager))
silent_launch = true; silent_launch = true;
if (chrome::IsRunningInAppMode() && if (chrome::IsRunningInForcedAppMode()) {
command_line.HasSwitch(switches::kAppId)) { user_manager::User* user =
chromeos::LaunchAppOrDie( chromeos::ProfileHelper::Get()->GetUserByProfile(last_used_profile);
last_used_profile, if (user && user->GetType() == user_manager::USER_TYPE_KIOSK_APP) {
command_line.GetSwitchValueASCII(switches::kAppId)); chromeos::LaunchAppOrDie(
last_used_profile,
chromeos::KioskAppId::ForChromeApp(
command_line.GetSwitchValueASCII(switches::kAppId)));
} else if (user &&
user->GetType() == user_manager::USER_TYPE_WEB_KIOSK_APP) {
chromeos::LaunchAppOrDie(
last_used_profile,
chromeos::KioskAppId::ForWebApp(user->GetAccountId()));
} else {
// If we are here, we are either in ARC kiosk session or the user is
// invalid. We should terminate the session in such cases.
chrome::AttemptUserExit();
return false;
}
// Skip browser launch since app mode launches its app window. // Skip browser launch since app mode launches its app window.
silent_launch = true; silent_launch = true;
} }
// If we are in the recoverable ARC/PWA app mode state (we do not have kAppId
// set), we should terminate the session instead of just showing black screen.
// TODO(crbug.com/1054382): Add a way of restarting PWA and Arc kiosks.
if (chrome::IsRunningInForcedAppMode() &&
!command_line.HasSwitch(switches::kAppId)) {
chrome::AttemptUserExit();
return false;
}
// If we are a demo app session and we crashed, there is no safe recovery // If we are a demo app session and we crashed, there is no safe recovery
// possible. We should instead cleanly exit and go back to the OOBE screen, // possible. We should instead cleanly exit and go back to the OOBE screen,
// where we will launch again after the timeout has expired. // where we will launch again after the timeout has expired.
...@@ -770,8 +775,8 @@ bool StartupBrowserCreator::ProcessCmdLineImpl( ...@@ -770,8 +775,8 @@ bool StartupBrowserCreator::ProcessCmdLineImpl(
base::CommandLine::StringType path = base::CommandLine::StringType path =
command_line.GetSwitchValueNative(apps::kLoadAndLaunchApp); command_line.GetSwitchValueNative(apps::kLoadAndLaunchApp);
if (!apps::AppLoadService::Get(last_used_profile)->LoadAndLaunch( if (!apps::AppLoadService::Get(last_used_profile)
base::FilePath(path), command_line, cur_dir)) { ->LoadAndLaunch(base::FilePath(path), command_line, cur_dir)) {
return false; return false;
} }
...@@ -814,11 +819,12 @@ bool StartupBrowserCreator::LaunchBrowserForLastProfiles( ...@@ -814,11 +819,12 @@ bool StartupBrowserCreator::LaunchBrowserForLastProfiles(
} }
#endif // !defined(OS_CHROMEOS) #endif // !defined(OS_CHROMEOS)
chrome::startup::IsProcessStartup is_process_startup = process_startup ? chrome::startup::IsProcessStartup is_process_startup =
chrome::startup::IS_PROCESS_STARTUP : process_startup ? chrome::startup::IS_PROCESS_STARTUP
chrome::startup::IS_NOT_PROCESS_STARTUP; : chrome::startup::IS_NOT_PROCESS_STARTUP;
chrome::startup::IsFirstRun is_first_run = first_run::IsChromeFirstRun() ? chrome::startup::IsFirstRun is_first_run =
chrome::startup::IS_FIRST_RUN : chrome::startup::IS_NOT_FIRST_RUN; first_run::IsChromeFirstRun() ? chrome::startup::IS_FIRST_RUN
: chrome::startup::IS_NOT_FIRST_RUN;
// On Windows, when chrome is launched by notification activation where the // On Windows, when chrome is launched by notification activation where the
// kNotificationLaunchId switch is used, always use |last_used_profile| which // kNotificationLaunchId switch is used, always use |last_used_profile| which
...@@ -1022,7 +1028,7 @@ bool StartupBrowserCreator::ActivatedProfile() { ...@@ -1022,7 +1028,7 @@ bool StartupBrowserCreator::ActivatedProfile() {
bool HasPendingUncleanExit(Profile* profile) { bool HasPendingUncleanExit(Profile* profile) {
return profile->GetLastSessionExitType() == Profile::EXIT_CRASHED && return profile->GetLastSessionExitType() == Profile::EXIT_CRASHED &&
!profile_launch_observer.Get().HasBeenLaunched(profile); !profile_launch_observer.Get().HasBeenLaunched(profile);
} }
base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir, base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
...@@ -1076,9 +1082,10 @@ Profile* GetStartupProfile(const base::FilePath& user_data_dir, ...@@ -1076,9 +1082,10 @@ Profile* GetStartupProfile(const base::FilePath& user_data_dir,
// We want to show the user manager. To indicate this, return the guest // We want to show the user manager. To indicate this, return the guest
// profile. However, we can only do this if the system profile (where the user // profile. However, we can only do this if the system profile (where the user
// manager lives) also exists (or is creatable). // manager lives) also exists (or is creatable).
return profile_manager->GetProfile(ProfileManager::GetSystemProfilePath()) ? return profile_manager->GetProfile(ProfileManager::GetSystemProfilePath())
profile_manager->GetProfile(ProfileManager::GetGuestProfilePath()) : ? profile_manager->GetProfile(
nullptr; ProfileManager::GetGuestProfilePath())
: nullptr;
} }
Profile* GetFallbackStartupProfile() { Profile* GetFallbackStartupProfile() {
...@@ -1095,8 +1102,8 @@ Profile* GetFallbackStartupProfile() { ...@@ -1095,8 +1102,8 @@ Profile* GetFallbackStartupProfile() {
for (Profile* profile : ProfileManager::GetLastOpenedProfiles()) { for (Profile* profile : ProfileManager::GetLastOpenedProfiles()) {
// Return any profile that is not locked. // Return any profile that is not locked.
ProfileAttributesEntry* entry; ProfileAttributesEntry* entry;
bool has_entry = storage->GetProfileAttributesWithPath(profile->GetPath(), bool has_entry =
&entry); storage->GetProfileAttributesWithPath(profile->GetPath(), &entry);
if (!has_entry || !entry->IsSigninRequired()) if (!has_entry || !entry->IsSigninRequired())
return profile; return profile;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment