blob: d12b286f9c31ba9f41d08eaad135c22fbb6ee334 [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FEATURED_SERVICE_H_
#define FEATURED_SERVICE_H_
#include "dbus/exported_object.h"
#include <dbus/object_path.h>
#include <chromeos/dbus/service_constants.h>
#include <base/command_line.h>
#include <base/files/file_util.h>
#include <base/memory/weak_ptr.h>
#include <base/values.h>
#include <brillo/dbus/exported_object_manager.h>
#include <brillo/dbus/exported_property_set.h>
#include <brillo/dbus/dbus_method_response.h>
#include <brillo/errors/error.h>
#include <brillo/syslog_logging.h>
#include <brillo/variant_dictionary.h>
#include <featured/feature_library.h>
#include <featured/proto_bindings/featured.pb.h>
#include <session_manager/dbus-proxies.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <optional>
#include <utility>
#include <vector>
#include "featured/store_interface.h"
#include "featured/tmp_storage_interface.h"
namespace featured {
// FeatureCommand is the base class for all commands to enable a feature.
class FeatureCommand {
public:
explicit FeatureCommand(const std::string& name) : name_(name) {}
FeatureCommand(FeatureCommand&& other) = default;
// virtual destructor is required because we create a unique pointer
// of an abstract class. See PlatformFeature class definition.
virtual ~FeatureCommand() = default;
std::string name() const { return name_; }
// Run the command to enable the feature, returning true on success.
virtual bool Execute() = 0;
private:
std::string name_;
};
// Write a specified value to a specified path.
class WriteFileCommand : public FeatureCommand {
public:
WriteFileCommand(const std::string& file_name, const std::string& value);
WriteFileCommand(WriteFileCommand&& other) = default;
// Attempt to write the file, returning true on success.
bool Execute() override;
void SetPrefixForTesting(const base::FilePath& prefix) { prefix_ = prefix; }
private:
std::string file_name_;
std::string value_;
base::FilePath prefix_;
};
// Create a directory at a specified path, and all parent directories.
class MkdirCommand : public FeatureCommand {
public:
explicit MkdirCommand(const std::string& path);
MkdirCommand(MkdirCommand&& other) = default;
// Attempt to make the directory, returning true on success.
bool Execute() override;
void SetPrefixForTesting(const base::FilePath& prefix) { prefix_ = prefix; }
private:
base::FilePath path_;
base::FilePath prefix_;
};
// SupportCheckCommand is the base class for all commands to check whether a
// feature is supported.
class SupportCheckCommand {
public:
explicit SupportCheckCommand(const std::string& name) : name_(name) {}
SupportCheckCommand(SupportCheckCommand&& other) = default;
// virtual destructor is required because we create a unique pointer
// of an abstract class. See PlatformFeature class definition.
virtual ~SupportCheckCommand() = default;
std::string name() const { return name_; }
// Return true if the feature is supported on this device. (false otherwise)
virtual bool IsSupported() = 0;
private:
std::string name_;
};
// Mark the device as supported if a file at a given path exists.
class FileExistsCommand : public SupportCheckCommand {
public:
explicit FileExistsCommand(const std::string& file_name);
FileExistsCommand(FileExistsCommand&& other) = default;
bool IsSupported() override;
private:
std::string file_name_;
};
// Mark the device as supported if a file at a given path *does not* exist.
class FileNotExistsCommand : public SupportCheckCommand {
public:
explicit FileNotExistsCommand(const std::string& file_name);
FileNotExistsCommand(FileNotExistsCommand&& other) = default;
bool IsSupported() override;
private:
std::string file_name_;
};
// Trivial support check command that always returns true.
class AlwaysSupportedCommand : public SupportCheckCommand {
public:
AlwaysSupportedCommand() : SupportCheckCommand("AlwaysSupported") {}
AlwaysSupportedCommand(AlwaysSupportedCommand&& other) = default;
bool IsSupported() override { return true; }
};
class PlatformFeature {
public:
PlatformFeature(
const std::string& name,
std::vector<std::unique_ptr<SupportCheckCommand>>&& query_cmds,
std::vector<std::unique_ptr<FeatureCommand>>&& feature_cmds)
: exec_cmds_(std::move(feature_cmds)),
support_check_cmds_(std::move(query_cmds)),
name_(std::make_unique<std::string>(name)),
feature_{name_->c_str(), FEATURE_DISABLED_BY_DEFAULT} {}
PlatformFeature(PlatformFeature&& other) = default;
PlatformFeature(const PlatformFeature& other) = delete;
PlatformFeature& operator=(const PlatformFeature& other) = delete;
const std::string& name() const { return *name_; }
// Don't copy this because address must *not* change across lookups.
const VariationsFeature* const feature() const { return &feature_; }
// Check if feature is supported on the device
bool IsSupported() const;
// Execute a sequence of commands to enable a feature
bool Execute() const;
// Get the names of the exec commands. Used for testing.
std::vector<std::string> ExecCommandNamesForTesting() const;
// Get the names of the support check commands. Used for testing.
std::vector<std::string> SupportCheckCommandNamesForTesting() const;
private:
std::vector<std::unique_ptr<FeatureCommand>> exec_cmds_;
std::vector<std::unique_ptr<SupportCheckCommand>> support_check_cmds_;
// The string in this unique_ptr must not be modified since feature_ contains
// a pointer to the underlying c_str().
std::unique_ptr<const std::string> name_;
VariationsFeature feature_;
};
class FeatureParserBase {
public:
using FeatureMap = std::unordered_map<std::string, PlatformFeature>;
virtual bool ParseFileContents(const std::string& file_contents) = 0;
virtual ~FeatureParserBase() = default;
bool AreFeaturesParsed() const { return features_parsed_; }
const FeatureMap* GetFeatureMap() { return &feature_map_; }
protected:
std::unordered_map<std::string, PlatformFeature> feature_map_;
// Parse features only once per object
bool features_parsed_ = false;
};
class JsonFeatureParser : public FeatureParserBase {
public:
// Implements the meat of the JSON parsing functionality given a JSON blob
bool ParseFileContents(const std::string& file_contents) override;
private:
// Helper to build a PlatformFeature object by parsing a JSON feature object
std::optional<PlatformFeature> MakeFeatureObject(
const base::Value::Dict& feature_obj);
};
class DbusFeaturedService {
public:
explicit DbusFeaturedService(std::unique_ptr<StoreInterface> store,
std::unique_ptr<TmpStorageInterface> tmp_storage)
: parser_(std::make_unique<JsonFeatureParser>()),
store_(std::move(store)),
tmp_storage_(std::move(tmp_storage)) {}
DbusFeaturedService(const DbusFeaturedService&) = delete;
DbusFeaturedService& operator=(const DbusFeaturedService&) = delete;
~DbusFeaturedService() = default;
bool Start(dbus::Bus* bus, std::shared_ptr<DbusFeaturedService> ptr);
private:
friend class DbusFeaturedServiceTestBase;
// Helpers to invoke a feature parser
bool ParseFeatureList();
// Enable all features that are supported and which Chrome tells us should be
// enabled.
bool EnableFeatures();
void OnSessionStateChanged(const std::string& state);
// Save fetched finch seed from Chrome to disk.
void HandleSeedFetched(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender sender);
std::unique_ptr<FeatureParserBase> parser_;
std::unique_ptr<StoreInterface> store_;
std::unique_ptr<TmpStorageInterface> tmp_storage_;
std::unique_ptr<org::chromium::SessionManagerInterfaceProxyInterface>
session_manager_ = nullptr;
bool evaluated_platform_features_json_ = false;
base::WeakPtrFactory<DbusFeaturedService> weak_ptr_factory_{this};
};
} // namespace featured
#endif // FEATURED_SERVICE_H_