// 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. #ifndef CHROMECAST_BASE_DEVICE_CAPABILITIES_H_ #define CHROMECAST_BASE_DEVICE_CAPABILITIES_H_ #include #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" namespace base { class DictionaryValue; class Value; } 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. // // 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: // |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 // SetValidatedValue(). virtual void Validate(const std::string& path, scoped_ptr 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|. void SetValidatedValue(const std::string& path, scoped_ptr new_value) const; private: DeviceCapabilities* const capabilities_; DISALLOW_COPY_AND_ASSIGN(Validator); }; // 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 { public: // Accessor for complete capabilities in dictionary format. const base::DictionaryValue& dictionary() const { return *dictionary_.get(); } // Accessor for complete capabilities string in JSON format. const std::string& json_string() const { return *json_string_.get(); } private: friend class base::RefCountedThreadSafe; // 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(scoped_ptr dictionary); ~Data(); const scoped_ptr dictionary_; const scoped_ptr json_string_; DISALLOW_COPY_AND_ASSIGN(Data); }; // Default Capability keys static const char kKeyBluetoothSupported[]; static const char kKeyDisplaySupported[]; // 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 scoped_ptr 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 scoped_ptr 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 // SetValidatedValue(). 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 SetValidatedValue() (for Validators) before // accessors are called. virtual bool BluetoothSupported() const = 0; virtual bool DisplaySupported() 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 scoped_ptr 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 GetData() 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. virtual scoped_refptr GetData() 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. virtual void SetCapability(const std::string& path, scoped_ptr proposed_value) = 0; // Iterates through entries in |dict_value| and calls SetCapability() for // each one. This method is asynchronous. virtual void MergeDictionary(const base::DictionaryValue& dict_value) = 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 CreateData(); // Uses |dictionary| as capabilities dictionary. static scoped_refptr CreateData( scoped_ptr dictionary); private: // Does actual internal update of |path| to |new_value|. virtual void SetValidatedValue(const std::string& path, scoped_ptr new_value) = 0; DISALLOW_COPY_AND_ASSIGN(DeviceCapabilities); }; } // namespace chromecast #endif // CHROMECAST_BASE_DEVICE_CAPABILITIES_H_