// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMECAST_BASE_DEVICE_CAPABILITIES_H_
#define CHROMECAST_BASE_DEVICE_CAPABILITIES_H_
#include <memory>
#include <string>
#include "base/memory/ref_counted.h"
#include "base/values.h"
namespace chromecast {
// Device capabilities are a set of features used to determine what operations
// are available on the device. They are identified by a key (string) and a
// value (base::Value). The class serves 2 main purposes:
//
// 1) Provide an interface for updating default capabilities and querying their
// current value. Default capabilities are known to the system beforehand
// and used by modules throughout Chromecast to control behavior of operations.
//
// 2) Store dynamic capabilities. Dynamic capabilities are not known to the
// system beforehand and are introduced by external parties. These capabilites
// are stored and then forwarded to app servers that use them to determine how
// to interact with the device.
//
// Capabilities can be classified as either "public" or "private". Capabilities
// of both types can be used by the Chromecast platform to control internal
// behaviors, but only public capabilities will be advertised to app servers.
// Once a capability is set, it retains its privacy classification permanently;
// attempting to change the privacy of a capability results in an error.
// Private capabilities can only be added by Validators. Calling SetCapability()
// on a path without a Validator will default to setting the capability as
// public.
//
// Thread Safety:
// Observers can be added from any thread. Each Observer is guaranteed to be
// notified on same thread that it was added on and must be removed on the same
// thread that it was added on.
//
// Validators can be registered from any thread. Each Validator's Validate()
// method is guaranteed to be called on same thread that the Validator was
// registered on. The Validator must be unregistered on the same thread
// that it was registered on.
//
// All other methods can be called safely from any thread.
// TODO(esum):
// 1) Add WifiSupported, HotspotSupported, and MultizoneSupported capabilities.
// 2) It's not ideal to have the accessors (BluetoothSupported(), etc.) not
// be valid initially until the capability gets registered. We might want
// to use some kind of builder class to solve this.
class DeviceCapabilities {
public:
class Observer {
public:
// Called when DeviceCapabilities gets written to in any way. |path|
// is full path to capability that has been updated.
virtual void OnCapabilitiesChanged(const std::string& path) = 0;
protected:
virtual ~Observer() {}
};
// When another module attempts to update the value for a capability,
// a manager may want to validate the change or even modify the new value.
// Managers that wish to perform this validation should inherit from the
// Validator class and implement its interface.
class Validator {
public:
Validator(const Validator&) = delete;
Validator& operator=(const Validator&) = delete;
// |path| is full path to capability, which could include paths expanded on
// the capability key that gets registered through the Register() method.
// For example, if a key of "foo" is registered for a Validator, |path|
// could be "foo", "foo.bar", "foo.bar.what", etc. |proposed_value| is new
// value being proposed for |path|. Determines if |proposed_value| is valid
// change for |path|. This method may be asynchronous, but multiple calls
// to it must be handled serially. Returns response through
// SetPublicValidatedValue() or SetPrivateValidatedValue().
virtual void Validate(const std::string& path,
base::Value proposed_value) = 0;
protected:
explicit Validator(DeviceCapabilities* capabilities);
virtual ~Validator() {}
DeviceCapabilities* capabilities() const { return capabilities_; }
// Meant to be called when Validate() has finished. |path| is full path to
// capability. |new_value| is new validated value to be used in
// DeviceCapabilities. This method passes these parameters to
// DeviceCapabilities, where |path| is updated internally to |new_value|.
// TODO(seantopping): Change this interface so that Validators are not the
// only means of accessing private capabilities.
void SetPublicValidatedValue(const std::string& path,
base::Value new_value) const;
void SetPrivateValidatedValue(const std::string& path,
base::Value new_value) const;
private:
DeviceCapabilities* const capabilities_;
};
// Class used to store/own capabilities-related data. It is immutable and
// RefCountedThreadSafe, so client code can freely query it throughout its
// lifetime without worrying about the data getting invalidated in any way.
class Data : public base::RefCountedThreadSafe<Data> {
public:
Data(const Data&) = delete;
Data& operator=(const Data&) = delete;
// Accessor for complete capabilities in dictionary format.
const base::Value::Dict& dictionary() const { return dictionary_; }
// Accessor for complete capabilities string in JSON format.
const std::string& json_string() const { return json_string_; }
private:
friend class base::RefCountedThreadSafe<Data>;
// DeviceCapabilities should be the only one responsible for Data
// construction. See CreateData() methods.
friend class DeviceCapabilities;
// Constructs empty dictionary with no capabilities.
Data();
// Uses |dictionary| as capabilities dictionary.
explicit Data(base::Value::Dict dictionary);
~Data();
const base::Value::Dict dictionary_;
std::string json_string_;
};
// Default Capability keys
static const char kKeyAssistantSupported[];
static const char kKeyBluetoothSupported[];
static const char kKeyDisplaySupported[];
static const char kKeyHiResAudioSupported[];
DeviceCapabilities(const DeviceCapabilities&) = delete;
DeviceCapabilities& operator=(const DeviceCapabilities&) = delete;
// This class should get destroyed after all Validators have been
// unregistered, all Observers have been removed, and the class is no longer
// being accessed.
virtual ~DeviceCapabilities() {}
// Create empty instance with no capabilities. Although the class is not
// singleton, there is meant to be a single instance owned by another module.
// The instance should be created early enough for all managers to register
// themselves, and then live long enough for all managers to unregister.
static std::unique_ptr<DeviceCapabilities> Create();
// Creates an instance where all the default capabilities are initialized
// to a predefined default value, and no Validators are registered. For use
// only in unit tests.
static std::unique_ptr<DeviceCapabilities> CreateForTesting();
// Registers a Validator for a capability. A given key must only be
// registered once, and must be unregistered before calling Register() again.
// If the capability has a value of Dictionary type, |key| must be just
// the capability's top-level key and not include path expansions to levels
// farther down. For example, "foo" is a valid value for |key|, but "foo.bar"
// is not. Note that if "foo.bar" is updated in SetCapability(), the
// Validate() method for "foo"'s Validator will be called, with a |path| of
// "foo.bar". Note that this method does not add or modify the capability.
// To do this, SetCapability() should be called, or Validators can call
// SetPublicValidatedValue() or SetPrivateValidatedValue(). This method is
// synchronous to ensure Validators know exactly when they may start receiving
// validation requests.
virtual void Register(const std::string& key,
Validator* validator) = 0;
// Unregisters Validator for |key|. |validator| argument must match
// |validator| argument that was passed in to Register() for |key|. Note that
// the capability and its value remain untouched. This method is synchronous
// to ensure Validators know exactly when they will stop receiving validation
// requests.
virtual void Unregister(const std::string& key,
const Validator* validator) = 0;
// Gets the Validator currently registered for |key|. Returns nullptr if
// no Validator is registered.
virtual Validator* GetValidator(const std::string& key) const = 0;
// Accessors for default capabilities. Note that the capability must be added
// through SetCapability() (or Set[Private]ValidatedValue() for Validators)
// before accessors are called.
virtual bool AssistantSupported() const = 0;
virtual bool BluetoothSupported() const = 0;
virtual bool DisplaySupported() const = 0;
virtual bool HiResAudioSupported() const = 0;
// Returns a deep copy of the value at |path|. If the capability at |path|
// does not exist, a null scoped_ptr is returned.
virtual base::Value GetCapability(const std::string& path) const = 0;
// Use this method to access dictionary and JSON string. No deep copying is
// performed, so this method is inexpensive. Note that any capability updates
// that occur after GetAllData() has been called will not be reflected in the
// returned scoped_refptr. You can think of this method as taking a snapshot
// of the capabilities when it gets called. All capabilities (those set by
// SetPrivateValidatedValue() and SetPublicValidatedValue()) will be present
// in the returned Data object.
virtual scoped_refptr<Data> GetAllData() const = 0;
// Similar to GetAllData(), but this only returns public capabilities.
virtual scoped_refptr<Data> GetPublicData() const = 0;
// Updates the value at |path| to |proposed_value| if |path| already exists
// and adds new capability if |path| doesn't. Note that if a key has been
// registered that is at the beginning of |path|, then the Validator will be
// used to determine if |proposed_value| is accepted.
// Ex: If "foo" has a Validator registered, a |path| of "foo.bar"
// will cause |proposed_value| to go through the Validator's Validate()
// method. Client code may use the Observer interface to determine the
// ultimate value used.
// This method is asynchronous. By default, this method will classify the new
// value at |path| as a public capability; if a Validator is present, it may
// classify the value as public or private via SetPublicValidatedValue() or
// SetPrivateValidatedValue() respectively.
virtual void SetCapability(const std::string& path,
base::Value proposed_value) = 0;
// Iterates through entries in |dict| and calls SetCapability() for each one.
// This method is asynchronous.
virtual void MergeDictionary(const base::Value::Dict& dict) = 0;
// Adds/removes an observer. It doesn't take the ownership of |observer|.
virtual void AddCapabilitiesObserver(Observer* observer) = 0;
virtual void RemoveCapabilitiesObserver(Observer* observer) = 0;
protected:
DeviceCapabilities() {}
// For derived implementation classes to create Data instances since they do
// not have access to Data constructors.
// Creates empty dictionary with no capabilities.
static scoped_refptr<Data> CreateData();
// Uses |dictionary| as capabilities dictionary.
static scoped_refptr<Data> CreateData(base::Value::Dict dictionary);
private:
// Internally update the capability residing at |path| to |new_value|. This
// capability will be visible in GetAllData() and GetPublicData().
virtual void SetPublicValidatedValue(const std::string& path,
base::Value new_value) = 0;
// Similar to SetPublicValidatedValue(), but this capability will only be
// visible in GetAllData().
virtual void SetPrivateValidatedValue(const std::string& path,
base::Value new_value) = 0;
};
} // namespace chromecast
#endif // CHROMECAST_BASE_DEVICE_CAPABILITIES_H_