| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h" |
| |
| #include <stddef.h> |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/constants/ash_paths.h" |
| #include "ash/constants/ash_switches.h" |
| #include "base/check.h" |
| #include "base/check_op.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/path_service.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_data.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_data_base.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_manager_observer.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_types.h" |
| #include "chrome/browser/ash/app_mode/kiosk_cryptohome_remover.h" |
| #include "chrome/browser/ash/app_mode/kiosk_external_updater.h" |
| #include "chrome/browser/ash/app_mode/kiosk_system_session.h" |
| #include "chrome/browser/ash/extensions/external_cache.h" |
| #include "chrome/browser/ash/extensions/external_cache_impl.h" |
| #include "chrome/browser/ash/login/session/user_session_manager.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" |
| #include "chrome/browser/ash/policy/core/device_local_account.h" |
| #include "chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h" |
| #include "chrome/browser/chromeos/app_mode/chrome_kiosk_external_loader_broker.h" |
| #include "chrome/browser/chromeos/app_mode/kiosk_browser_session.h" |
| #include "chrome/browser/extensions/external_loader.h" |
| #include "chrome/browser/extensions/external_provider_impl.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chromeos/ash/components/policy/device_local_account/device_local_account_type.h" |
| #include "chromeos/ash/components/settings/cros_settings.h" |
| #include "chromeos/ash/components/settings/cros_settings_names.h" |
| #include "components/account_id/account_id.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/known_user.h" |
| #include "components/user_manager/user_manager.h" |
| #include "extensions/browser/updater/extension_downloader_delegate.h" |
| #include "extensions/common/extension_id.h" |
| #include "extensions/common/extension_urls.h" |
| #include "extensions/common/manifest_handlers/kiosk_mode_info.h" |
| #include "net/base/backoff_entry.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "third_party/cros_system_api/switches/chrome_switches.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Sub directory under DIR_USER_DATA to store cached crx files. |
| constexpr char kCrxCacheDir[] = "kiosk/crx"; |
| |
| // Sub directory under DIR_USER_DATA to store unpacked crx file for validating |
| // its signature. |
| constexpr char kCrxUnpackDir[] = "kiosk_unpack"; |
| |
| KioskChromeAppManager::Overrides* g_test_overrides = nullptr; |
| |
| base::FilePath GetCrxCacheDir() { |
| base::FilePath user_data_dir; |
| CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)); |
| return user_data_dir.AppendASCII(kCrxCacheDir); |
| } |
| |
| base::FilePath GetCrxUnpackDir() { |
| base::FilePath temp_dir; |
| base::GetTempDir(&temp_dir); |
| return temp_dir.AppendASCII(kCrxUnpackDir); |
| } |
| |
| scoped_refptr<base::SequencedTaskRunner> GetBackgroundTaskRunner() { |
| // TODO(eseckler): The ExternalCacheImpl that uses this TaskRunner seems to be |
| // important during startup, which is why we cannot currently use the |
| // BEST_EFFORT TaskPriority here. |
| return base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| } |
| |
| std::unique_ptr<chromeos::ExternalCache> CreateExternalCache( |
| scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory, |
| chromeos::ExternalCacheDelegate* delegate) { |
| if (g_test_overrides) { |
| return g_test_overrides->CreateExternalCache(delegate, true); |
| } |
| |
| auto cache = std::make_unique<chromeos::ExternalCacheImpl>( |
| GetCrxCacheDir(), std::move(shared_url_loader_factory), |
| GetBackgroundTaskRunner(), delegate, /*always_check_updates=*/true, |
| /*wait_for_cache_initialization=*/false, |
| /*allow_scheduled_updates=*/true); |
| cache->set_flush_on_put(true); |
| return cache; |
| } |
| |
| base::Version GetPlatformVersion() { |
| return base::Version(base::SysInfo::OperatingSystemVersion()); |
| } |
| |
| // Converts a flag constant to actual command line switch value. |
| std::string GetSwitchString(const std::string& flag_name) { |
| base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM); |
| cmd_line.AppendSwitch(flag_name); |
| DCHECK_EQ(2U, cmd_line.argv().size()); |
| return cmd_line.argv()[1]; |
| } |
| |
| bool IsWebstoreUpdateUrl(const std::string* url) { |
| return url && extension_urls::IsWebstoreUpdateUrl(GURL(*url)); |
| } |
| |
| KioskChromeAppManager::PrimaryAppDownloadResult |
| PrimaryAppDownloadResultFromError( |
| extensions::ExtensionDownloaderDelegate::Error error) { |
| switch (error) { |
| case extensions::ExtensionDownloaderDelegate::Error::DISABLED: |
| return KioskChromeAppManager::PrimaryAppDownloadResult::kDisabled; |
| case extensions::ExtensionDownloaderDelegate::Error::MANIFEST_FETCH_FAILED: |
| return KioskChromeAppManager::PrimaryAppDownloadResult:: |
| kManifestFetchFailed; |
| case extensions::ExtensionDownloaderDelegate::Error::MANIFEST_INVALID: |
| return KioskChromeAppManager::PrimaryAppDownloadResult::kManifestInvalid; |
| case extensions::ExtensionDownloaderDelegate::Error::NO_UPDATE_AVAILABLE: |
| return KioskChromeAppManager::PrimaryAppDownloadResult:: |
| kNoUpdateAvailable; |
| case extensions::ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_EMPTY: |
| return KioskChromeAppManager::PrimaryAppDownloadResult::kCrxFetchUrlEmpty; |
| case extensions::ExtensionDownloaderDelegate::Error::CRX_FETCH_URL_INVALID: |
| return KioskChromeAppManager::PrimaryAppDownloadResult:: |
| kCrxFetchUrlInvalid; |
| case extensions::ExtensionDownloaderDelegate::Error::CRX_FETCH_FAILED: |
| return KioskChromeAppManager::PrimaryAppDownloadResult::kCrxFetchFailed; |
| } |
| } |
| |
| } // namespace |
| |
| // static |
| const char KioskChromeAppManager::kKioskDictionaryName[] = "kiosk"; |
| |
| const char kKioskPrimaryAppInstallErrorHistogram[] = |
| "Kiosk.ChromeApp.PrimaryAppInstallError"; |
| const char kKioskPrimaryAppUpdateResultHistogram[] = |
| "Kiosk.ChromeApp.PrimaryAppUpdateResult"; |
| const char kKioskExternalUpdateSuccessHistogram[] = |
| "Kiosk.ChromeApp.ExternalUpdateSuccess"; |
| |
| namespace { |
| // This class is owned by `ChromeBrowserMainPartsAsh`. |
| static KioskChromeAppManager* g_instance = nullptr; |
| } // namespace |
| |
| // static |
| KioskChromeAppManager* KioskChromeAppManager::Get() { |
| CHECK(g_instance); |
| return g_instance; |
| } |
| |
| // static |
| bool KioskChromeAppManager::IsInitialized() { |
| return g_instance; |
| } |
| |
| // static |
| void KioskChromeAppManager::InitializeForTesting(Overrides* overrides) { |
| DCHECK(!g_instance); |
| g_test_overrides = overrides; |
| } |
| |
| // static |
| void KioskChromeAppManager::RegisterLocalStatePrefs( |
| PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(kKioskDictionaryName); |
| chromeos::KioskBrowserSession::RegisterLocalStatePrefs(registry); |
| } |
| |
| // static |
| void KioskChromeAppManager::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| chromeos::KioskBrowserSession::RegisterProfilePrefs(registry); |
| } |
| |
| const std::string& KioskChromeAppManager::GetAutoLaunchApp() const { |
| return auto_launch_app_id_; |
| } |
| |
| void KioskChromeAppManager::SetAppWasAutoLaunchedWithZeroDelay( |
| const std::string& app_id) { |
| DCHECK_EQ(auto_launch_app_id_, app_id); |
| currently_auto_launched_with_zero_delay_app_ = app_id; |
| auto_launched_with_zero_delay_ = true; |
| } |
| |
| void KioskChromeAppManager::SetExtensionDownloaderBackoffPolicy( |
| std::optional<net::BackoffEntry::Policy> backoff_policy) { |
| // In browser tests `external_cache_` is reset before `StartupAppLauncher`. |
| // Check before trying to set backoff policy here. |
| if (!external_cache_) { |
| return; |
| } |
| external_cache_->SetBackoffPolicy(backoff_policy); |
| } |
| |
| bool KioskChromeAppManager::GetSwitchesForSessionRestore( |
| const std::string& app_id, |
| base::CommandLine* switches) { |
| bool auto_launched = app_id == currently_auto_launched_with_zero_delay_app_; |
| const base::CommandLine* current_command_line = |
| base::CommandLine::ForCurrentProcess(); |
| bool has_auto_launched_flag = |
| current_command_line->HasSwitch(switches::kAppAutoLaunched); |
| if (auto_launched == has_auto_launched_flag) { |
| return false; |
| } |
| |
| // Collect current policy defined switches, so they can be passed on to the |
| // session manager as well - otherwise they would get lost on restart. |
| // This ignores 'flag-switches-begin' - 'flag-switches-end' flags, but those |
| // should not be present for kiosk sessions. |
| bool in_policy_switches_block = false; |
| const std::string policy_switches_begin = |
| GetSwitchString(chromeos::switches::kPolicySwitchesBegin); |
| const std::string policy_switches_end = |
| GetSwitchString(chromeos::switches::kPolicySwitchesEnd); |
| |
| for (const auto& it : current_command_line->argv()) { |
| if (it == policy_switches_begin) { |
| DCHECK(!in_policy_switches_block); |
| in_policy_switches_block = true; |
| } |
| |
| if (in_policy_switches_block) { |
| switches->AppendSwitch(it); |
| } |
| |
| if (it == policy_switches_end) { |
| DCHECK(in_policy_switches_block); |
| in_policy_switches_block = false; |
| } |
| } |
| |
| DCHECK(!in_policy_switches_block); |
| |
| if (auto_launched) { |
| switches->AppendSwitch(switches::kAppAutoLaunched); |
| } |
| |
| return true; |
| } |
| |
| void KioskChromeAppManager::OnExternalCacheDamaged(const std::string& app_id) { |
| CHECK(external_cache_); |
| auto [crx_path, _] = GetCachedCrx(app_id).value_or(CachedCrxInfo()); |
| external_cache_->OnDamagedFileDetected(crx_path); |
| } |
| |
| void KioskChromeAppManager::AddAppForTest( |
| const std::string& app_id, |
| const AccountId& account_id, |
| const GURL& update_url, |
| const std::string& required_platform_version) { |
| for (auto it = apps_.begin(); it != apps_.end(); ++it) { |
| if ((*it)->app_id() == app_id) { |
| apps_.erase(it); |
| break; |
| } |
| } |
| |
| apps_.emplace_back(KioskAppData::CreateForTest( |
| &local_state_.get(), shared_url_loader_factory_, *this, app_id, |
| account_id, update_url, required_platform_version)); |
| } |
| |
| std::string KioskChromeAppManager::GetAutoLaunchAppRequiredPlatformVersion() |
| const { |
| // Bail out if there is no auto launched app with zero delay. |
| if (auto_launch_app_id_.empty() || !GetAutoLaunchDelay().is_zero()) { |
| return std::string(); |
| } |
| |
| const KioskAppData* data = GetAppData(auto_launch_app_id_); |
| return data == nullptr ? std::string() : data->required_platform_version(); |
| } |
| |
| std::vector<KioskChromeAppManager::App> KioskChromeAppManager::GetApps() const { |
| std::vector<App> apps; |
| for (const auto& app : apps_) { |
| if (app->status() != KioskAppData::Status::kError) { |
| apps.push_back(ConstructApp(*app)); |
| } |
| } |
| return apps; |
| } |
| |
| KioskChromeAppManager::App KioskChromeAppManager::ConstructApp( |
| const KioskAppData& data) const { |
| App app(data); |
| app.required_platform_version = data.required_platform_version(); |
| app.is_loading = external_cache_->ExtensionFetchPending(app.app_id); |
| app.was_auto_launched_with_zero_delay = |
| app.app_id == currently_auto_launched_with_zero_delay_app_; |
| return app; |
| } |
| |
| std::optional<KioskChromeAppManager::App> KioskChromeAppManager::GetApp( |
| const std::string& app_id) const { |
| const KioskAppData* data = GetAppData(app_id); |
| if (!data) { |
| return std::nullopt; |
| } |
| return ConstructApp(*data); |
| } |
| |
| void KioskChromeAppManager::ClearAppData(const std::string& app_id) { |
| KioskAppData* app_data = GetAppDataMutable(app_id); |
| if (!app_data) { |
| return; |
| } |
| |
| app_data->ClearCache(); |
| } |
| |
| void KioskChromeAppManager::UpdateAppDataFromProfile( |
| const std::string& app_id, |
| Profile* profile, |
| const extensions::Extension* app) { |
| KioskAppData* app_data = GetAppDataMutable(app_id); |
| if (!app_data) { |
| return; |
| } |
| |
| app_data->LoadFromInstalledApp(profile, app); |
| } |
| |
| void KioskChromeAppManager::RetryFailedAppDataFetch() { |
| for (const auto& app : apps_) { |
| if (app->status() == KioskAppData::Status::kError) { |
| app->Load(); |
| } |
| } |
| } |
| |
| bool KioskChromeAppManager::HasCachedCrx(const std::string& app_id) const { |
| return GetCachedCrx(app_id).has_value(); |
| } |
| |
| std::optional<KioskChromeAppManager::CachedCrxInfo> |
| KioskChromeAppManager::GetCachedCrx(std::string_view app_id) const { |
| base::FilePath path; |
| std::string version; |
| return external_cache_->GetExtension(std::string(app_id), &path, &version) |
| ? std::make_optional(std::make_tuple(path, version)) |
| : std::nullopt; |
| } |
| |
| crosapi::mojom::AppInstallParams |
| KioskChromeAppManager::CreatePrimaryAppInstallData( |
| const std::string& id) const { |
| const base::Value::Dict* extension = |
| external_cache_->GetCachedExtensions().FindDict(id); |
| if (!extension) { |
| return crosapi::mojom::AppInstallParams(id, std::string(), std::string(), |
| false); |
| } |
| |
| const std::optional<bool> is_store_app_maybe = |
| extension->FindBool(extensions::ExternalProviderImpl::kIsFromWebstore); |
| const std::string* external_update_url_value = extension->FindString( |
| extensions::ExternalProviderImpl::kExternalUpdateUrl); |
| bool is_store_app_bool = is_store_app_maybe.value_or(false) || |
| IsWebstoreUpdateUrl(external_update_url_value); |
| |
| const std::string* crx_file_location = |
| extension->FindString(extensions::ExternalProviderImpl::kExternalCrx); |
| DCHECK(crx_file_location); |
| |
| const std::string* external_version = |
| extension->FindString(extensions::ExternalProviderImpl::kExternalVersion); |
| DCHECK(external_version); |
| |
| return crosapi::mojom::AppInstallParams(id, *crx_file_location, |
| *external_version, is_store_app_bool); |
| } |
| |
| void KioskChromeAppManager::OnKioskSessionStarted(const KioskAppId& app_id) { |
| base::CommandLine session_flags(base::CommandLine::NO_PROGRAM); |
| if (GetSwitchesForSessionRestore(app_id.app_id.value(), &session_flags)) { |
| base::CommandLine::StringVector flags; |
| // argv[0] is the program name `base::CommandLine::NO_PROGRAM`. |
| flags.assign(session_flags.argv().begin() + 1, session_flags.argv().end()); |
| |
| // Update user flags, but do not restart Chrome - the purpose of the flags |
| // set here is to be able to properly restore session if the session is |
| // restarted - e.g. due to crash. For example, this will ensure restarted |
| // app session restores auto-launched state. |
| UserSessionManager::GetInstance()->SetSwitchesForUser( |
| user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(), |
| UserSessionManager::CommandLineSwitchesType::kPolicyAndKioskControl, |
| flags); |
| } |
| |
| NotifySessionInitialized(); |
| } |
| |
| void KioskChromeAppManager::UpdateExternalCache() { |
| UpdateAppsFromPolicy(); |
| } |
| |
| void KioskChromeAppManager::OnKioskAppCacheUpdated(const std::string& app_id) { |
| for (auto& observer : observers_) { |
| observer.OnKioskAppCacheUpdated(app_id); |
| } |
| } |
| |
| void KioskChromeAppManager::OnKioskAppExternalUpdateComplete(bool success) { |
| base::UmaHistogramBoolean(kKioskExternalUpdateSuccessHistogram, success); |
| for (auto& observer : observers_) { |
| observer.OnKioskAppExternalUpdateComplete(success); |
| } |
| } |
| |
| void KioskChromeAppManager::PutValidatedExternalExtension( |
| const std::string& app_id, |
| const base::FilePath& crx_path, |
| const std::string& version, |
| chromeos::ExternalCache::PutExternalExtensionCallback callback) { |
| external_cache_->PutExternalExtension(app_id, crx_path, version, |
| std::move(callback)); |
| } |
| |
| bool KioskChromeAppManager::IsPlatformCompliant( |
| const std::string& required_platform_version) const { |
| // Empty required version is compliant with any platform version. |
| if (required_platform_version.empty()) { |
| return true; |
| } |
| |
| // Not compliant for bad formatted required versions. |
| const base::Version required_version(required_platform_version); |
| if (!required_version.IsValid() || |
| required_version.components().size() > 3u) { |
| LOG(ERROR) << "Bad formatted required platform version: " |
| << required_platform_version; |
| return false; |
| } |
| |
| // Not compliant if the platform version components do not match. |
| const size_t count = required_version.components().size(); |
| const base::Version platform_version = GetPlatformVersion(); |
| const auto& platform_version_components = platform_version.components(); |
| const auto& required_version_components = required_version.components(); |
| for (size_t i = 0; i < count; ++i) { |
| if (platform_version_components[i] != required_version_components[i]) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool KioskChromeAppManager::IsPlatformCompliantWithApp( |
| const extensions::Extension* app) const { |
| // Compliant if the app is not the auto launched with zero delay app. |
| if (currently_auto_launched_with_zero_delay_app_ != app->id()) { |
| return true; |
| } |
| |
| // Compliant if the app does not specify required platform version. |
| const extensions::KioskModeInfo* info = extensions::KioskModeInfo::Get(app); |
| if (info == nullptr) { |
| return true; |
| } |
| |
| // Compliant if the app wants to be always updated. |
| if (info->always_update) { |
| return true; |
| } |
| |
| return IsPlatformCompliant(info->required_platform_version); |
| } |
| |
| KioskChromeAppManager::KioskChromeAppManager( |
| PrefService* local_state, |
| scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory, |
| KioskCryptohomeRemover* cryptohome_remover) |
| : KioskAppManagerBase(local_state, cryptohome_remover), |
| shared_url_loader_factory_(std::move(shared_url_loader_factory)) { |
| CHECK(!g_instance); // Only one instance is allowed. |
| external_cache_ = CreateExternalCache(shared_url_loader_factory_, this); |
| g_instance = this; |
| UpdateAppsFromPolicy(); |
| } |
| |
| KioskChromeAppManager::~KioskChromeAppManager() { |
| chromeos::ChromeKioskExternalLoaderBroker::Shutdown(); |
| observers_.Clear(); |
| g_test_overrides = nullptr; |
| g_instance = nullptr; |
| } |
| |
| void KioskChromeAppManager::MonitorKioskExternalUpdate() { |
| usb_stick_updater_ = std::make_unique<KioskExternalUpdater>( |
| GetBackgroundTaskRunner(), GetCrxCacheDir(), GetCrxUnpackDir()); |
| } |
| |
| const KioskAppData* KioskChromeAppManager::GetAppData( |
| const std::string& app_id) const { |
| for (const auto& app : apps_) { |
| if (app->app_id() == app_id) { |
| return app.get(); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| KioskAppData* KioskChromeAppManager::GetAppDataMutable( |
| const std::string& app_id) { |
| return const_cast<KioskAppData*>(GetAppData(app_id)); |
| } |
| |
| void KioskChromeAppManager::UpdateAppsFromPolicy() { |
| // Gets app id to data mapping for existing apps. |
| std::map<std::string, std::unique_ptr<KioskAppData>> old_apps; |
| for (auto& app : apps_) { |
| old_apps[app->app_id()] = std::move(app); |
| } |
| apps_.clear(); |
| |
| auto_launch_app_id_.clear(); |
| std::string auto_login_account_id; |
| CrosSettings::Get()->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId, |
| &auto_login_account_id); |
| |
| // Re-populates `apps_` and reuses existing KioskAppData when possible. |
| const std::vector<policy::DeviceLocalAccount> device_local_accounts = |
| policy::GetDeviceLocalAccounts(CrosSettings::Get()); |
| for (const auto& device_local_account : device_local_accounts) { |
| if (device_local_account.type != |
| policy::DeviceLocalAccountType::kKioskApp) { |
| continue; |
| } |
| |
| if (device_local_account.account_id == auto_login_account_id) { |
| auto_launch_app_id_ = device_local_account.kiosk_app_id; |
| } |
| |
| // Note that app ids are not canonical, i.e. they can contain upper |
| // case letters. |
| const AccountId account_id( |
| AccountId::FromUserEmail(device_local_account.user_id)); |
| auto old_it = old_apps.find(device_local_account.kiosk_app_id); |
| if (old_it != old_apps.end()) { |
| apps_.push_back(std::move(old_it->second)); |
| old_apps.erase(old_it); |
| } else { |
| auto [crx_path, _] = GetCachedCrx(device_local_account.kiosk_app_id) |
| .value_or(CachedCrxInfo()); |
| |
| apps_.push_back(std::make_unique<KioskAppData>( |
| &local_state_.get(), shared_url_loader_factory_, *this, |
| device_local_account.kiosk_app_id, account_id, |
| GURL(device_local_account.kiosk_app_update_url), crx_path)); |
| apps_.back()->Load(); |
| } |
| cryptohome_remover_->CancelDelayedCryptohomeRemoval(account_id); |
| } |
| |
| std::vector<const KioskAppDataBase*> apps_to_remove; |
| std::vector<std::string> app_ids_to_remove; |
| for (auto& entry : old_apps) { |
| apps_to_remove.emplace_back(entry.second.get()); |
| app_ids_to_remove.push_back(entry.second->app_id()); |
| } |
| ClearRemovedApps(apps_to_remove); |
| external_cache_->RemoveExtensions(app_ids_to_remove); |
| |
| UpdateExternalCachePrefs(); |
| RetryFailedAppDataFetch(); |
| |
| NotifyKioskAppsChanged(); |
| } |
| |
| void KioskChromeAppManager::UpdateExternalCachePrefs() { |
| // Request external_cache_ to download new apps and update the existing |
| // apps. |
| base::Value::Dict prefs; |
| for (const auto& app : apps_) { |
| base::Value::Dict entry; |
| |
| if (app->update_url().is_valid()) { |
| entry.Set(extensions::ExternalProviderImpl::kExternalUpdateUrl, |
| app->update_url().spec()); |
| } else { |
| entry.Set(extensions::ExternalProviderImpl::kExternalUpdateUrl, |
| extension_urls::GetWebstoreUpdateUrl().spec()); |
| } |
| |
| prefs.SetByDottedPath(app->app_id(), std::move(entry)); |
| } |
| external_cache_->UpdateExtensionsList(std::move(prefs)); |
| } |
| |
| void KioskChromeAppManager::OnExtensionLoadedInCache( |
| const extensions::ExtensionId& id, |
| bool is_updated) { |
| KioskAppData* app_data = GetAppDataMutable(id); |
| if (!app_data) { |
| return; |
| } |
| |
| if (auto crx_info = GetCachedCrx(id); crx_info.has_value()) { |
| auto& [crx_path, _] = crx_info.value(); |
| app_data->SetCachedCrx(crx_path); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.OnKioskExtensionLoadedInCache(id); |
| } |
| |
| if (is_updated) { |
| base::UmaHistogramEnumeration(kKioskPrimaryAppUpdateResultHistogram, |
| PrimaryAppDownloadResult::kSuccess); |
| } |
| } |
| |
| void KioskChromeAppManager::OnExtensionDownloadFailed( |
| const extensions::ExtensionId& id, |
| extensions::ExtensionDownloaderDelegate::Error error) { |
| KioskAppData* app_data = GetAppDataMutable(id); |
| if (!app_data) { |
| return; |
| } |
| for (auto& observer : observers_) { |
| observer.OnKioskExtensionDownloadFailed(id); |
| } |
| |
| if (!external_cache_->GetExtension(id, nullptr, nullptr)) { |
| // Initial install fail. |
| base::UmaHistogramEnumeration(kKioskPrimaryAppInstallErrorHistogram, |
| PrimaryAppDownloadResultFromError(error)); |
| return; |
| } |
| base::UmaHistogramEnumeration(kKioskPrimaryAppUpdateResultHistogram, |
| PrimaryAppDownloadResultFromError(error)); |
| } |
| |
| base::TimeDelta KioskChromeAppManager::GetAutoLaunchDelay() const { |
| int delay; |
| if (!CrosSettings::Get()->GetInteger( |
| kAccountsPrefDeviceLocalAccountAutoLoginDelay, &delay)) { |
| return base::TimeDelta(); // Default delay is 0ms. |
| } |
| return base::Milliseconds(delay); |
| } |
| |
| } // namespace ash |