| // 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; |
| } |
| |
| } // namespace feature |