summaryrefslogtreecommitdiffstats
path: root/chromecast/base/device_capabilities.h
blob: 88d952569a3b2c0d9c81ff8a15fc6d095d0ef734 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// 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 <string>

#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<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|.
    void SetValidatedValue(const std::string& path,
                           scoped_ptr<base::Value> 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<Data> {
   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<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(scoped_ptr<const base::DictionaryValue> dictionary);
    ~Data();

    const scoped_ptr<const base::DictionaryValue> dictionary_;
    const scoped_ptr<const std::string> json_string_;

    DISALLOW_COPY_AND_ASSIGN(Data);
  };

  // Default Capability keys
  static const char kKeyBluetoothSupported[];
  static const char kKeyDisplaySupported[];
  static const char kKeyHiResAudioSupported[];

  // 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<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 scoped_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
  // 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;
  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 scoped_ptr<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 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<Data> 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<base::Value> 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<Data> CreateData();
  // Uses |dictionary| as capabilities dictionary.
  static scoped_refptr<Data> CreateData(
      scoped_ptr<const base::DictionaryValue> dictionary);

 private:
  // Does actual internal update of |path| to |new_value|.
  virtual void SetValidatedValue(const std::string& path,
                                 scoped_ptr<base::Value> new_value) = 0;

  DISALLOW_COPY_AND_ASSIGN(DeviceCapabilities);
};

}  // namespace chromecast

#endif  // CHROMECAST_BASE_DEVICE_CAPABILITIES_H_