blob: a1a918fe8527b7432b445fe87cbc59d67cd6a2d6 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/ash/vpn_list_forwarder.h"
#include "ash/public/interfaces/constants.mojom.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
#include "services/service_manager/public/cpp/connector.h"
namespace mojo {
template <>
struct TypeConverter<ash::mojom::ArcVpnProviderPtr,
app_list::ArcVpnProviderManager::ArcVpnProvider*> {
static ash::mojom::ArcVpnProviderPtr Convert(
const app_list::ArcVpnProviderManager::ArcVpnProvider* input) {
auto result = ash::mojom::ArcVpnProvider::New();
result->app_name = input->app_name;
result->package_name = input->package_name;
result->app_id = input->app_id;
result->last_launch_time = input->last_launch_time;
return result;
}
};
} // namespace mojo
namespace {
bool IsVPNProvider(const extensions::Extension* extension) {
return extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kVpnProvider);
}
Profile* GetProfileForPrimaryUser() {
const user_manager::User* const primary_user =
user_manager::UserManager::Get()->GetPrimaryUser();
if (!primary_user)
return nullptr;
return chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
}
// Connects to the VpnList mojo interface in ash.
ash::mojom::VpnListPtr ConnectToVpnList() {
ash::mojom::VpnListPtr vpn_list;
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(ash::mojom::kServiceName, &vpn_list);
return vpn_list;
}
} // namespace
VpnListForwarder::VpnListForwarder() : weak_factory_(this) {
if (user_manager::UserManager::Get()->GetPrimaryUser()) {
// If a user is logged in, start observing the primary user's extension
// registry immediately.
AttachToPrimaryUserProfile();
} else {
// If no user is logged in, wait until the first user logs in (thus becoming
// the primary user) and a profile is created for that user.
registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
}
}
VpnListForwarder::~VpnListForwarder() {
if (extension_registry_)
extension_registry_->RemoveObserver(this);
if (arc_vpn_provider_manager_)
arc_vpn_provider_manager_->RemoveObserver(this);
vpn_list_ = nullptr;
}
void VpnListForwarder::OnArcVpnProvidersRefreshed(
const std::vector<
std::unique_ptr<app_list::ArcVpnProviderManager::ArcVpnProvider>>&
arc_vpn_providers) {
std::vector<ash::mojom::ArcVpnProviderPtr> arc_vpn_provider_ptrs;
for (const auto& arc_vpn_provider : arc_vpn_providers) {
arc_vpn_provider_ptrs.emplace_back(
ash::mojom::ArcVpnProvider::From(arc_vpn_provider.get()));
}
vpn_list_->SetArcVpnProviders(std::move(arc_vpn_provider_ptrs));
}
void VpnListForwarder::OnArcVpnProviderUpdated(
app_list::ArcVpnProviderManager::ArcVpnProvider* arc_vpn_provider) {
vpn_list_->AddOrUpdateArcVPNProvider(
ash::mojom::ArcVpnProvider::From(arc_vpn_provider));
}
void VpnListForwarder::OnArcVpnProviderRemoved(
const std::string& package_name) {
vpn_list_->RemoveArcVPNProvider(package_name);
}
void VpnListForwarder::OnExtensionLoaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension) {
if (IsVPNProvider(extension))
UpdateVPNProviders();
}
void VpnListForwarder::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) {
if (IsVPNProvider(extension))
UpdateVPNProviders();
}
void VpnListForwarder::OnShutdown(extensions::ExtensionRegistry* registry) {
DCHECK(extension_registry_);
extension_registry_->RemoveObserver(this);
extension_registry_ = nullptr;
}
void VpnListForwarder::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_PROFILE_CREATED, type);
const Profile* const profile = content::Source<Profile>(source).ptr();
if (!chromeos::ProfileHelper::Get()->IsPrimaryProfile(profile)) {
// If the profile that was just created does not belong to the primary user
// (e.g. login profile), ignore it.
return;
}
// The first user logged in (thus becoming the primary user) and a profile was
// created for that user. Stop observing profile creation. Wait one message
// loop cycle to allow other code which observes the
// chrome::NOTIFICATION_PROFILE_CREATED notification to finish initializing
// the profile, then start observing the primary user's extension registry.
registrar_.RemoveAll();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&VpnListForwarder::AttachToPrimaryUserProfile,
weak_factory_.GetWeakPtr()));
}
void VpnListForwarder::UpdateVPNProviders() {
DCHECK(extension_registry_);
std::vector<ash::mojom::ThirdPartyVpnProviderPtr> third_party_providers;
for (const auto& extension : extension_registry_->enabled_extensions()) {
if (!IsVPNProvider(extension.get()))
continue;
ash::mojom::ThirdPartyVpnProviderPtr provider =
ash::mojom::ThirdPartyVpnProvider::New();
provider->name = extension->name();
provider->extension_id = extension->id();
third_party_providers.push_back(std::move(provider));
}
// Ash starts without any third-party providers. If we've never sent one then
// there's no need to send an empty list. This case commonly occurs on startup
// when the user has no third-party VPN extensions installed.
if (!sent_providers_ && third_party_providers.empty())
return;
vpn_list_->SetThirdPartyVpnProviders(std::move(third_party_providers));
sent_providers_ = true;
}
void VpnListForwarder::AttachToPrimaryUserProfile() {
DCHECK(!vpn_list_);
vpn_list_ = ConnectToVpnList();
DCHECK(vpn_list_);
AttachToPrimaryUserExtensionRegistry();
AttachToPrimaryUserArcVpnProviderManager();
}
void VpnListForwarder::AttachToPrimaryUserExtensionRegistry() {
DCHECK(!extension_registry_);
extension_registry_ =
extensions::ExtensionRegistry::Get(GetProfileForPrimaryUser());
extension_registry_->AddObserver(this);
UpdateVPNProviders();
}
void VpnListForwarder::AttachToPrimaryUserArcVpnProviderManager() {
arc_vpn_provider_manager_ =
app_list::ArcVpnProviderManager::Get(GetProfileForPrimaryUser());
if (arc_vpn_provider_manager_)
arc_vpn_provider_manager_->AddObserver(this);
}