blob: d6d15b172ebae4bdf3650b8e641f6c6596ece1c7 [file] [log] [blame]
// 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