blob: 466f0f023b444f9efa12cde67afcd8fabbc44613 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "featured/feature_library.h"
#include <utility>
#include <base/bind.h>
#include <base/logging.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/message.h>
#include <brillo/dbus/dbus_connection.h>
#include <brillo/dbus/dbus_method_invoker.h>
#include <brillo/dbus/dbus_proxy_util.h>
#include <brillo/errors/error.h>
namespace feature {
PlatformFeatures::PlatformFeatures(scoped_refptr<dbus::Bus> bus,
dbus::ObjectProxy* proxy)
: bus_(bus), proxy_(proxy) {}
std::unique_ptr<PlatformFeatures> PlatformFeatures::New(
scoped_refptr<dbus::Bus> bus) {
auto* proxy = bus->GetObjectProxy(
chromeos::kChromeFeaturesServiceName,
dbus::ObjectPath(chromeos::kChromeFeaturesServicePath));
if (!proxy) {
LOG(ERROR) << "Failed to create object proxy for "
<< chromeos::kChromeFeaturesServiceName;
return nullptr;
}
return std::unique_ptr<PlatformFeatures>(new PlatformFeatures(bus, proxy));
}
void PlatformFeatures::IsEnabled(const VariationsFeature& feature,
IsEnabledCallback callback) {
DCHECK(CheckFeatureIdentity(feature)) << feature.name;
proxy_->WaitForServiceToBeAvailable(base::BindOnce(
&PlatformFeatures::OnWaitForService, weak_ptr_factory_.GetWeakPtr(),
feature, std::move(callback)));
}
bool PlatformFeatures::IsEnabledBlocking(const VariationsFeature& feature) {
DCHECK(CheckFeatureIdentity(feature)) << feature.name;
dbus::MethodCall call(chromeos::kChromeFeaturesServiceInterface,
chromeos::kChromeFeaturesServiceIsFeatureEnabledMethod);
dbus::MessageWriter writer(&call);
writer.AppendString(feature.name);
std::unique_ptr<dbus::Response> response = brillo::dbus_utils::CallDBusMethod(
bus_, proxy_, &call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
if (!response) {
return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
}
dbus::MessageReader reader(response.get());
bool feature_enabled = false;
if (!reader.PopBool(&feature_enabled)) {
LOG(ERROR) << "failed to read bool from dbus result; using default value";
return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
}
return feature_enabled;
}
void PlatformFeatures::OnWaitForService(const VariationsFeature& feature,
IsEnabledCallback callback,
bool available) {
if (!available) {
std::move(callback).Run(feature.default_state ==
FEATURE_ENABLED_BY_DEFAULT);
LOG(ERROR) << "failed to connect to dbus service; using default value";
return;
}
dbus::MethodCall call(chromeos::kChromeFeaturesServiceInterface,
chromeos::kChromeFeaturesServiceIsFeatureEnabledMethod);
dbus::MessageWriter writer(&call);
writer.AppendString(feature.name);
proxy_->CallMethod(&call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&PlatformFeatures::HandleIsEnabledResponse,
weak_ptr_factory_.GetWeakPtr(), feature,
std::move(callback)));
}
void PlatformFeatures::HandleIsEnabledResponse(const VariationsFeature& feature,
IsEnabledCallback callback,
dbus::Response* response) {
if (!response) {
LOG(ERROR) << "dbus call failed; using default value";
std::move(callback).Run(feature.default_state ==
FEATURE_ENABLED_BY_DEFAULT);
return;
}
dbus::MessageReader reader(response);
bool feature_enabled = false;
if (!reader.PopBool(&feature_enabled)) {
LOG(ERROR) << "failed to read bool from dbus result; using default value";
std::move(callback).Run(feature.default_state ==
FEATURE_ENABLED_BY_DEFAULT);
return;
}
std::move(callback).Run(feature_enabled);
}
bool PlatformFeatures::CheckFeatureIdentity(const VariationsFeature& feature) {
base::AutoLock auto_lock(lock_);
auto it = feature_identity_tracker_.find(feature.name);
if (it == feature_identity_tracker_.end()) {
// If it's not tracked yet, register it.
feature_identity_tracker_[feature.name] = &feature;
return true;
}
// Compare address of |feature| to the existing tracked entry.
return it->second == &feature;
}
void PlatformFeatures::ShutdownBus() {
bus_->ShutdownAndBlock();
}
// FakePlatformFeatures
void FakePlatformFeatures::IsEnabled(const VariationsFeature& feature,
IsEnabledCallback callback) {
base::AutoLock auto_lock(enabled_lock_);
bus_->AssertOnOriginThread();
auto it = enabled_.find(feature.name);
bool enabled = feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
if (it != enabled_.end()) {
enabled = it->second;
}
bus_->GetOriginTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), enabled));
}
bool FakePlatformFeatures::IsEnabledBlocking(const VariationsFeature& feature) {
base::AutoLock auto_lock(enabled_lock_);
auto it = enabled_.find(feature.name);
if (it != enabled_.end()) {
return it->second;
}
return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
}
void FakePlatformFeatures::SetEnabled(const std::string& feature,
bool enabled) {
base::AutoLock auto_lock(enabled_lock_);
enabled_[feature] = enabled;
}
void FakePlatformFeatures::ClearEnabled(const std::string& feature) {
base::AutoLock auto_lock(enabled_lock_);
auto it = enabled_.find(feature);
if (it != enabled_.end()) {
enabled_.erase(it);
}
}
void FakePlatformFeatures::ShutdownBus() {
bus_->ShutdownAndBlock();
}
} // namespace feature