| // Copyright 2021 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/app_session_ash.h" |
| #include <memory> |
| |
| #include "ash/public/cpp/accessibility_controller.h" |
| #include "base/notreached.h" |
| #include "base/scoped_observation.h" |
| #include "chrome/browser/ash/app_mode/app_launch_utils.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_manager.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_types.h" |
| #include "chrome/browser/ash/app_mode/kiosk_app_update_service.h" |
| #include "chrome/browser/ash/app_mode/kiosk_mode_idle_app_name_notification.h" |
| #include "chrome/browser/ash/app_mode/metrics/network_connectivity_metrics_service.h" |
| #include "chrome/browser/ash/app_mode/metrics/periodic_metrics_service.h" |
| #include "chrome/browser/ash/crosapi/browser_manager.h" |
| #include "chrome/browser/ash/crosapi/browser_manager_observer.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_process_platform_part.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/user_manager/user.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/manifest_handlers/offline_enabled_info.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Start the floating accessibility menu in ash-chrome if the |
| // `FloatingAccessibilityMenuEnabled` policy is enabled. |
| void StartFloatingAccessibilityMenu() { |
| auto* accessibility_controller = AccessibilityController::Get(); |
| if (accessibility_controller) { |
| accessibility_controller->ShowFloatingMenuIfEnabled(); |
| } |
| } |
| |
| bool IsOfflineEnabledForApp(const std::string& app_id, Profile* profile) { |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(profile); |
| if (!extension_registry) { |
| // If Lacros is enabled, extensions are running in Lacros. So Ash does not |
| // have |extension_registry|. |
| return false; |
| } |
| |
| const extensions::Extension* primary_app = |
| extension_registry->GetInstalledExtension(app_id); |
| if (!primary_app) { |
| return false; |
| } |
| |
| return extensions::OfflineEnabledInfo::IsOfflineEnabled(primary_app); |
| } |
| |
| bool IsLacrosEnabled() { |
| return crosapi::browser_util::IsLacrosEnabledInChromeKioskSession() || |
| crosapi::browser_util::IsLacrosEnabledInWebKioskSession(); |
| } |
| |
| } // namespace |
| |
| class AppSessionAsh::LacrosWatcher : public crosapi::BrowserManagerObserver { |
| public: |
| explicit LacrosWatcher(Profile* profile, const ash::KioskAppId& kiosk_app_id) |
| : profile_(profile), kiosk_app_id_(kiosk_app_id) { |
| observation_.Observe(crosapi::BrowserManager::Get()); |
| } |
| |
| LacrosWatcher(const LacrosWatcher&) = delete; |
| LacrosWatcher& operator=(const LacrosWatcher&) = delete; |
| ~LacrosWatcher() override = default; |
| |
| // `crosapi::BrowserManagerObserver`: |
| void OnStateChanged() override { |
| if (!crosapi::BrowserManager::Get()->IsRunningOrWillRun()) { |
| LOG(WARNING) << "Lacros crashed, restarting Kiosk session"; |
| RestartKioskSession(); |
| } |
| } |
| |
| private: |
| void RestartKioskSession() { |
| // Restart the kiosk session. We do not need to create a new |
| // `AppSessionAsh`, because ash did not crash in this flow. |
| ash::LaunchAppOrDie(profile_, kiosk_app_id_, |
| /*should_start_app_session_ash=*/false); |
| } |
| |
| const raw_ptr<Profile> profile_; |
| const ash::KioskAppId kiosk_app_id_; |
| base::ScopedObservation<crosapi::BrowserManager, |
| crosapi::BrowserManagerObserver> |
| observation_{this}; |
| }; |
| |
| AppSessionAsh::AppSessionAsh(Profile* profile, |
| const KioskAppId& kiosk_app_id, |
| const absl::optional<std::string>& app_name) |
| : AppSession(profile), |
| kiosk_app_id_(kiosk_app_id), |
| network_metrics_service_( |
| std::make_unique<NetworkConnectivityMetricsService>()), |
| periodic_metrics_service_(std::make_unique<PeriodicMetricsService>( |
| g_browser_process->local_state())) { |
| switch (kiosk_app_id_.type) { |
| case KioskAppType::kChromeApp: |
| InitForChromeAppKiosk(); |
| break; |
| case KioskAppType::kWebApp: |
| InitForWebKiosk(app_name); |
| break; |
| case KioskAppType::kArcApp: |
| NOTREACHED(); |
| } |
| if (IsLacrosEnabled()) { |
| lacros_watcher_ = std::make_unique<LacrosWatcher>(profile, kiosk_app_id_); |
| } |
| } |
| |
| AppSessionAsh::~AppSessionAsh() = default; |
| |
| void AppSessionAsh::InitForChromeAppKiosk() { |
| const std::string& app_id = kiosk_app_id_.app_id.value(); |
| chromeos::AppSession::InitForChromeAppKiosk(app_id); |
| StartFloatingAccessibilityMenu(); |
| InitKioskAppUpdateService(app_id); |
| SetRebootAfterUpdateIfNecessary(); |
| |
| periodic_metrics_service_->RecordPreviousSessionMetrics(); |
| periodic_metrics_service_->StartRecordingPeriodicMetrics( |
| IsOfflineEnabledForApp(app_id, profile())); |
| } |
| |
| void AppSessionAsh::InitForWebKiosk( |
| const absl::optional<std::string>& app_name) { |
| chromeos::AppSession::InitForWebKiosk(app_name); |
| StartFloatingAccessibilityMenu(); |
| |
| periodic_metrics_service_->RecordPreviousSessionMetrics(); |
| // Web apps always support offline mode. |
| periodic_metrics_service_->StartRecordingPeriodicMetrics( |
| /*is_offline_enabled=*/true); |
| } |
| |
| void AppSessionAsh::ShuttingDown() { |
| network_metrics_service_.reset(); |
| } |
| |
| void AppSessionAsh::InitKioskAppUpdateService(const std::string& app_id) { |
| // Set the app_id for the current instance of KioskAppUpdateService. |
| auto* update_service = KioskAppUpdateServiceFactory::GetForProfile(profile()); |
| DCHECK(update_service); |
| if (update_service) { |
| update_service->Init(app_id); |
| } |
| |
| // Start to monitor external update from usb stick. |
| KioskAppManager::Get()->MonitorKioskExternalUpdate(); |
| } |
| |
| void AppSessionAsh::SetRebootAfterUpdateIfNecessary() { |
| policy::BrowserPolicyConnectorAsh* connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| if (!connector->IsDeviceEnterpriseManaged()) { |
| PrefService* local_state = g_browser_process->local_state(); |
| local_state->SetBoolean(prefs::kRebootAfterUpdate, true); |
| KioskModeIdleAppNameNotification::Initialize(); |
| } |
| } |
| |
| } // namespace ash |