chromium/chrome/browser/ash/crosapi/persistent_forced_extension_keep_alive.cc

// 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/ash/crosapi/persistent_forced_extension_keep_alive.h"

#include "base/no_destructor.h"
#include "chrome/browser/ash/crosapi/browser_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/features/behavior_feature.h"
#include "extensions/common/features/feature.h"
#include "extensions/common/features/feature_provider.h"
#include "extensions/common/hashed_extension_id.h"

namespace {

bool ShouldEnableKeepAlive(const base::Value::Dict& extension_install_list) {
  // Lacros should be kept alive if the Imprivata in-session extension is
  // installed by admin policy.
  const extensions::Feature* feature =
      extensions::FeatureProvider::GetBehaviorFeature(
          extensions::behavior_feature::kImprivataInSessionExtension);
  DCHECK(feature);

  for (auto entry : extension_install_list) {
    const extensions::ExtensionId& extension_id = entry.first;
    const extensions::HashedExtensionId& hashed_extension_id =
        extensions::HashedExtensionId(extension_id);

    if (feature->IsIdInAllowlist(hashed_extension_id))
      return true;
  }
  return false;
}

}  // namespace

namespace crosapi {

PersistentForcedExtensionKeepAlive::PersistentForcedExtensionKeepAlive(
    PrefService* user_prefs) {
  DCHECK(user_prefs);

  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
  pref_change_registrar_->Init(user_prefs);
  pref_change_registrar_->Add(
      extensions::pref_names::kInstallForceList,
      base::BindRepeating(&PersistentForcedExtensionKeepAlive::UpdateKeepAlive,
                          weak_factory_.GetWeakPtr()));

  UpdateKeepAlive();
}

PersistentForcedExtensionKeepAlive::~PersistentForcedExtensionKeepAlive() =
    default;

void PersistentForcedExtensionKeepAlive::Shutdown() {
  keep_alive_.reset();
  weak_factory_.InvalidateWeakPtrs();
}

void PersistentForcedExtensionKeepAlive::UpdateKeepAlive() {
  DCHECK(pref_change_registrar_);
  DCHECK(pref_change_registrar_->prefs());

  const base::Value::Dict& extension_install_list =
      pref_change_registrar_->prefs()->GetDict(
          extensions::pref_names::kInstallForceList);

  if (!ShouldEnableKeepAlive(extension_install_list)) {
    keep_alive_.reset();
    return;
  }
  if (!keep_alive_) {
    keep_alive_ = BrowserManager::Get()->KeepAlive(
        BrowserManager::Feature::kPersistentForcedExtension);
  }
}

// static
PersistentForcedExtensionKeepAliveFactory*
PersistentForcedExtensionKeepAliveFactory::GetInstance() {
  static base::NoDestructor<PersistentForcedExtensionKeepAliveFactory> instance;
  return instance.get();
}

PersistentForcedExtensionKeepAliveFactory::
    PersistentForcedExtensionKeepAliveFactory()
    : ProfileKeyedServiceFactory(
          "PersistentForcedExtensionKeepAlive",
          ProfileSelections::Builder()
              .WithRegular(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/40257657): Check if this service is needed in
              // Guest mode.
              .WithGuest(ProfileSelection::kOriginalOnly)
              // TODO(crbug.com/41488885): Check if this service is needed for
              // Ash Internals.
              .WithAshInternals(ProfileSelection::kOriginalOnly)
              .Build()) {}

PersistentForcedExtensionKeepAliveFactory::
    ~PersistentForcedExtensionKeepAliveFactory() = default;

std::unique_ptr<KeyedService> PersistentForcedExtensionKeepAliveFactory::
    BuildServiceInstanceForBrowserContext(
        content::BrowserContext* context) const {
  if (!browser_util::IsLacrosEnabled())
    return nullptr;
  Profile* profile = Profile::FromBrowserContext(context);
  if (!profile)
    return nullptr;
  if (ash::ProfileHelper::IsSigninProfile(profile)) {
    // Does not have to be registered on the sign-in profile.
    return nullptr;
  }
  return std::make_unique<PersistentForcedExtensionKeepAlive>(
      user_prefs::UserPrefs::Get(context));
}

bool PersistentForcedExtensionKeepAliveFactory::
    ServiceIsCreatedWithBrowserContext() const {
  // Service is created in the background as soon as the BrowserContext has been
  // brought up.
  return true;
}

}  // namespace crosapi