| // Copyright 2022 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/chromeos/app_mode/kiosk_app_service_launcher.h" |
| |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_forward.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/syslog_logging.h" |
| #include "base/types/cxx23_to_underlying.h" |
| #include "build/buildflag.h" |
| #include "chrome/browser/apps/app_service/app_launch_params.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/apps/app_service/launch_result_type.h" |
| #include "components/services/app_service/public/cpp/app.h" |
| #include "components/services/app_service/public/cpp/app_launch_util.h" |
| #include "components/services/app_service/public/cpp/app_registry_cache.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "components/services/app_service/public/cpp/app_update.h" |
| #include "components/services/app_service/public/cpp/instance.h" |
| #include "components/services/app_service/public/cpp/instance_update.h" |
| #include "ui/base/window_open_disposition.h" |
| #include "url/gurl.h" |
| |
| namespace chromeos { |
| |
| KioskAppServiceLauncher::KioskAppServiceLauncher(Profile* profile) { |
| app_service_ = apps::AppServiceProxyFactory::GetForProfile(profile); |
| DCHECK(app_service_); |
| } |
| |
| KioskAppServiceLauncher::~KioskAppServiceLauncher() = default; |
| |
| void KioskAppServiceLauncher::CheckAndMaybeLaunchApp( |
| const std::string& app_id, |
| AppLaunchedCallback app_launched_callback) { |
| DCHECK(!app_launched_callback_) |
| << "CheckAndMaybeLaunchApp() should only be called once."; |
| app_launched_callback_ = std::move(app_launched_callback); |
| |
| app_id_ = app_id; |
| |
| apps::Readiness readiness = apps::Readiness::kUnknown; |
| app_service_->AppRegistryCache().ForOneApp( |
| app_id_, |
| [&readiness](apps::AppUpdate update) { readiness = update.Readiness(); }); |
| |
| base::UmaHistogramEnumeration(kLaunchAppReadinessUMA, readiness); |
| switch (readiness) { |
| case apps::Readiness::kUnknown: |
| case apps::Readiness::kTerminated: |
| SYSLOG(WARNING) << "Kiosk app not ready yet: " |
| << base::to_underlying(readiness); |
| app_registry_observation_.Observe(&app_service_->AppRegistryCache()); |
| break; |
| case apps::Readiness::kReady: |
| LaunchAppInternal(); |
| break; |
| case apps::Readiness::kDisabledByBlocklist: |
| case apps::Readiness::kDisabledByPolicy: |
| case apps::Readiness::kDisabledByUser: |
| case apps::Readiness::kUninstalledByUser: |
| case apps::Readiness::kRemoved: |
| case apps::Readiness::kUninstalledByNonUser: |
| case apps::Readiness::kDisabledByLocalSettings: |
| SYSLOG(ERROR) << "Kiosk app should not have readiness " |
| << base::to_underlying(readiness); |
| if (!app_launched_callback_.is_null()) { |
| std::move(app_launched_callback_).Run(false); |
| } |
| } |
| } |
| |
| void KioskAppServiceLauncher::EnsureAppTypeInitialized( |
| apps::AppType app_type, |
| base::OnceClosure app_type_initialized_callback) { |
| if (app_service_->AppRegistryCache().IsAppTypePublished(app_type)) { |
| std::move(app_type_initialized_callback).Run(); |
| return; |
| } |
| |
| app_type_ = app_type; |
| app_type_initialized_callback_ = std::move(app_type_initialized_callback); |
| app_registry_observation_.Observe(&app_service_->AppRegistryCache()); |
| } |
| |
| void KioskAppServiceLauncher::CheckAndMaybeLaunchApp( |
| const std::string& app_id, |
| AppLaunchedCallback app_launched_callback, |
| base::OnceClosure app_visible_callback) { |
| app_visible_callback_ = std::move(app_visible_callback); |
| instance_registry_observation_.Observe(&app_service_->InstanceRegistry()); |
| |
| CheckAndMaybeLaunchApp(app_id, std::move(app_launched_callback)); |
| } |
| |
| void KioskAppServiceLauncher::OnAppUpdate(const apps::AppUpdate& update) { |
| if (update.AppId() != app_id_ || |
| update.Readiness() != apps::Readiness::kReady) { |
| return; |
| } |
| app_registry_observation_.Reset(); |
| LaunchAppInternal(); |
| } |
| |
| void KioskAppServiceLauncher::OnAppTypePublishing( |
| const std::vector<apps::AppPtr>& deltas, |
| apps::AppType app_type) { |
| if (app_type == app_type_ && !app_type_initialized_callback_.is_null()) { |
| app_registry_observation_.Reset(); |
| |
| std::move(app_type_initialized_callback_).Run(); |
| } |
| } |
| |
| void KioskAppServiceLauncher::OnAppRegistryCacheWillBeDestroyed( |
| apps::AppRegistryCache* cache) { |
| app_registry_observation_.Reset(); |
| } |
| |
| void KioskAppServiceLauncher::OnInstanceUpdate( |
| const apps::InstanceUpdate& update) { |
| if (update.AppId() != app_id_ || |
| !(update.State() & apps::InstanceState::kVisible)) { |
| return; |
| } |
| |
| // When running with Lacros the visibility update often arrives before the |
| // launch update, so trigger the launch update first. |
| // This will be a no-op if the launch update already arrived. |
| OnAppLaunched(apps::LaunchResult(apps::LaunchResult::State::kSuccess)); |
| |
| instance_registry_observation_.Reset(); |
| if (!app_visible_callback_.is_null()) { |
| std::move(app_visible_callback_).Run(); |
| } |
| } |
| |
| void KioskAppServiceLauncher::OnInstanceRegistryWillBeDestroyed( |
| apps::InstanceRegistry* cache) { |
| instance_registry_observation_.Reset(); |
| } |
| |
| void KioskAppServiceLauncher::LaunchAppInternal() { |
| SYSLOG(INFO) << "Kiosk app is ready to launch with App Service"; |
| |
| auto params = apps::AppLaunchParams( |
| app_id_, apps::LaunchContainer::kLaunchContainerWindow, |
| WindowOpenDisposition::NEW_POPUP, apps::LaunchSource::kFromKiosk); |
| params.override_url = launch_url_.value_or(GURL()); |
| |
| app_service_->LaunchAppWithParams( |
| std::move(params), base::BindOnce(&KioskAppServiceLauncher::OnAppLaunched, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void KioskAppServiceLauncher::OnAppLaunched(apps::LaunchResult&& result) { |
| // App window is not active at this moment. We need to close splash screen |
| // after app window is activated which will be handled in subclasses. |
| if (!app_launched_callback_.is_null()) { |
| std::move(app_launched_callback_).Run(true); |
| } |
| } |
| |
| void KioskAppServiceLauncher::SetLaunchUrl(const GURL& launch_url) { |
| launch_url_ = launch_url; |
| } |
| |
| } // namespace chromeos |