1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromecast/base/device_capabilities_impl.h"
6 
7 #include <stddef.h>
8 
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/values.h"
19 #include "chromecast/base/serializers.h"
20 
21 namespace chromecast {
22 
23 namespace {
24 
25 const char kPathSeparator = '.';
26 
27 // Determines if a key passed to Register() is valid. No path separators can
28 // be present in the key and it must not be empty.
IsValidRegisterKey(const std::string & key)29 bool IsValidRegisterKey(const std::string& key) {
30   return !key.empty() && key.find(kPathSeparator) == std::string::npos;
31 }
32 
33 // Determines if a path is valid. This is true if there are no empty keys
34 // anywhere in the path (ex: .foo, foo., foo..bar are all invalid).
IsValidPath(const std::string & path)35 bool IsValidPath(const std::string& path) {
36   return !path.empty() && *path.begin() != kPathSeparator &&
37          *path.rbegin() != kPathSeparator &&
38          path.find("..") == std::string::npos;
39 }
40 
41 // Given a path, gets the first key present in the path (ex: for path "foo.bar"
42 // return "foo").
GetFirstKey(const std::string & path)43 std::string GetFirstKey(const std::string& path) {
44   std::size_t length_to_first_separator = path.find(kPathSeparator);
45   return (length_to_first_separator == std::string::npos)
46              ? path
47              : path.substr(0, length_to_first_separator);
48 }
49 
50 }  // namespace
51 
52 // static Default Capability Keys
53 const char DeviceCapabilities::kKeyAssistantSupported[] = "assistant_supported";
54 const char DeviceCapabilities::kKeyBluetoothSupported[] = "bluetooth_supported";
55 const char DeviceCapabilities::kKeyDisplaySupported[] = "display_supported";
56 const char DeviceCapabilities::kKeyHiResAudioSupported[] =
57     "hi_res_audio_supported";
58 
59 // static
Create()60 std::unique_ptr<DeviceCapabilities> DeviceCapabilities::Create() {
61   return base::WrapUnique(new DeviceCapabilitiesImpl);
62 }
63 
64 // static
CreateForTesting()65 std::unique_ptr<DeviceCapabilities> DeviceCapabilities::CreateForTesting() {
66   DeviceCapabilities* capabilities = new DeviceCapabilitiesImpl;
67   capabilities->SetCapability(kKeyBluetoothSupported,
68                               std::make_unique<base::Value>(false));
69   capabilities->SetCapability(kKeyDisplaySupported,
70                               std::make_unique<base::Value>(true));
71   capabilities->SetCapability(kKeyHiResAudioSupported,
72                               std::make_unique<base::Value>(false));
73   capabilities->SetCapability(kKeyAssistantSupported,
74                               std::make_unique<base::Value>(true));
75   return base::WrapUnique(capabilities);
76 }
77 
CreateData()78 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilities::CreateData() {
79   return base::WrapRefCounted(new Data);
80 }
81 
CreateData(std::unique_ptr<const base::DictionaryValue> dictionary)82 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilities::CreateData(
83     std::unique_ptr<const base::DictionaryValue> dictionary) {
84   DCHECK(dictionary.get());
85   return base::WrapRefCounted(new Data(std::move(dictionary)));
86 }
87 
Validator(DeviceCapabilities * capabilities)88 DeviceCapabilities::Validator::Validator(DeviceCapabilities* capabilities)
89     : capabilities_(capabilities) {
90   DCHECK(capabilities);
91 }
92 
SetPublicValidatedValue(const std::string & path,std::unique_ptr<base::Value> new_value) const93 void DeviceCapabilities::Validator::SetPublicValidatedValue(
94     const std::string& path,
95     std::unique_ptr<base::Value> new_value) const {
96   capabilities_->SetPublicValidatedValue(path, std::move(new_value));
97 }
98 
SetPrivateValidatedValue(const std::string & path,std::unique_ptr<base::Value> new_value) const99 void DeviceCapabilities::Validator::SetPrivateValidatedValue(
100     const std::string& path,
101     std::unique_ptr<base::Value> new_value) const {
102   capabilities_->SetPrivateValidatedValue(path, std::move(new_value));
103 }
104 
Data()105 DeviceCapabilities::Data::Data()
106     : dictionary_(new base::DictionaryValue),
107       json_string_(*SerializeToJson(*dictionary_)) {}
108 
Data(std::unique_ptr<const base::DictionaryValue> dictionary)109 DeviceCapabilities::Data::Data(
110     std::unique_ptr<const base::DictionaryValue> dictionary)
111     : dictionary_(std::move(dictionary)),
112       json_string_(*SerializeToJson(*dictionary_)) {
113   DCHECK(dictionary_.get());
114 }
115 
~Data()116 DeviceCapabilitiesImpl::Data::~Data() {}
117 
ValidatorInfo(Validator * validator)118 DeviceCapabilitiesImpl::ValidatorInfo::ValidatorInfo(Validator* validator)
119     : validator_(validator), task_runner_(base::ThreadTaskRunnerHandle::Get()) {
120   DCHECK(validator_);
121   DCHECK(task_runner_.get());
122 }
123 
~ValidatorInfo()124 DeviceCapabilitiesImpl::ValidatorInfo::~ValidatorInfo() {
125   // Check that ValidatorInfo is being destroyed on the same thread that it was
126   // constructed on.
127   DCHECK(task_runner_->BelongsToCurrentThread());
128 }
129 
Validate(const std::string & path,std::unique_ptr<base::Value> proposed_value) const130 void DeviceCapabilitiesImpl::ValidatorInfo::Validate(
131     const std::string& path,
132     std::unique_ptr<base::Value> proposed_value) const {
133   // Check that we are running Validate on the same thread that ValidatorInfo
134   // was constructed on.
135   DCHECK(task_runner_->BelongsToCurrentThread());
136   validator_->Validate(path, std::move(proposed_value));
137 }
138 
DeviceCapabilitiesImpl()139 DeviceCapabilitiesImpl::DeviceCapabilitiesImpl()
140     : all_data_(CreateData()),
141       public_data_(CreateData()),
142       task_runner_for_writes_(base::ThreadTaskRunnerHandle::Get()),
143       observer_list_(new base::ObserverListThreadSafe<Observer>) {
144   DCHECK(task_runner_for_writes_.get());
145 }
146 
~DeviceCapabilitiesImpl()147 DeviceCapabilitiesImpl::~DeviceCapabilitiesImpl() {
148   // Make sure that any registered Validators have unregistered at this point
149   DCHECK(validator_map_.empty())
150       << "Some validators weren't properly unregistered: " << [this] {
151            std::vector<std::string> keys;
152            for (const auto& pair : validator_map_) {
153              keys.push_back(pair.first);
154            }
155            return base::JoinString(keys, ", ");
156          }();
157   // Make sure that all observers have been removed at this point
158   observer_list_->AssertEmpty();
159 }
160 
Register(const std::string & key,Validator * validator)161 void DeviceCapabilitiesImpl::Register(const std::string& key,
162                                       Validator* validator) {
163   DCHECK(IsValidRegisterKey(key));
164   DCHECK(validator);
165 
166   base::AutoLock auto_lock(validation_lock_);
167   // Check that a validator has not already been registered for this key
168   DCHECK_EQ(0u, validator_map_.count(key));
169   validator_map_[key] = std::make_unique<ValidatorInfo>(validator);
170 }
171 
Unregister(const std::string & key,const Validator * validator)172 void DeviceCapabilitiesImpl::Unregister(const std::string& key,
173                                         const Validator* validator) {
174   base::AutoLock auto_lock(validation_lock_);
175   auto validator_it = validator_map_.find(key);
176   DCHECK(validator_it != validator_map_.end());
177   // Check that validator being unregistered matches the original for |key|.
178   // This prevents managers from accidentally unregistering incorrect
179   // validators.
180   DCHECK_EQ(validator, validator_it->second->validator());
181   // Check that validator is unregistering on same thread that it was
182   // registered on
183   DCHECK(validator_it->second->task_runner()->BelongsToCurrentThread());
184   validator_map_.erase(validator_it);
185 }
186 
GetValidator(const std::string & key) const187 DeviceCapabilities::Validator* DeviceCapabilitiesImpl::GetValidator(
188     const std::string& key) const {
189   base::AutoLock auto_lock(validation_lock_);
190   auto validator_it = validator_map_.find(key);
191   return validator_it == validator_map_.end()
192              ? nullptr
193              : validator_it->second->validator();
194 }
195 
BluetoothSupported() const196 bool DeviceCapabilitiesImpl::BluetoothSupported() const {
197   scoped_refptr<Data> data_ref = GetAllData();
198   bool bluetooth_supported = false;
199   bool found_key = data_ref->dictionary().GetBoolean(kKeyBluetoothSupported,
200                                                      &bluetooth_supported);
201   DCHECK(found_key);
202   return bluetooth_supported;
203 }
204 
DisplaySupported() const205 bool DeviceCapabilitiesImpl::DisplaySupported() const {
206   scoped_refptr<Data> data_ref = GetAllData();
207   bool display_supported = false;
208   bool found_key = data_ref->dictionary().GetBoolean(kKeyDisplaySupported,
209                                                      &display_supported);
210   DCHECK(found_key);
211   return display_supported;
212 }
213 
HiResAudioSupported() const214 bool DeviceCapabilitiesImpl::HiResAudioSupported() const {
215   scoped_refptr<Data> data_ref = GetAllData();
216   bool hi_res_audio_supported = false;
217   bool found_key = data_ref->dictionary().GetBoolean(kKeyHiResAudioSupported,
218                                                      &hi_res_audio_supported);
219   DCHECK(found_key);
220   return hi_res_audio_supported;
221 }
222 
AssistantSupported() const223 bool DeviceCapabilitiesImpl::AssistantSupported() const {
224   scoped_refptr<Data> data_ref = GetAllData();
225   bool assistant_supported = false;
226   bool found_key = data_ref->dictionary().GetBoolean(kKeyAssistantSupported,
227                                                      &assistant_supported);
228   DCHECK(found_key);
229   return assistant_supported;
230 }
231 
GetCapability(const std::string & path) const232 std::unique_ptr<base::Value> DeviceCapabilitiesImpl::GetCapability(
233     const std::string& path) const {
234   scoped_refptr<Data> data_ref = GetAllData();
235   const base::Value* value = nullptr;
236   bool found_path = data_ref->dictionary().Get(path, &value);
237   return found_path ? value->CreateDeepCopy() : std::unique_ptr<base::Value>();
238 }
239 
GetAllData() const240 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilitiesImpl::GetAllData()
241     const {
242   // Need to acquire lock here when copy constructing all_data_ otherwise we
243   // could concurrently be writing to scoped_refptr in SetPublicValidatedValue()
244   // or SetPrivateValidatedValue(), which could cause a bad scoped_refptr read.
245   base::AutoLock auto_lock(data_lock_);
246   return all_data_;
247 }
248 
GetPublicData() const249 scoped_refptr<DeviceCapabilities::Data> DeviceCapabilitiesImpl::GetPublicData()
250     const {
251   // Need to acquire lock here when copy constructing public_data_ otherwise we
252   // could concurrently be writing to scoped_refptr in SetPublicValidatedValue()
253   // or SetPrivateValidatedValue(), which could cause a bad scoped_refptr read.
254   base::AutoLock auto_lock(data_lock_);
255   return public_data_;
256 }
257 
SetCapability(const std::string & path,std::unique_ptr<base::Value> proposed_value)258 void DeviceCapabilitiesImpl::SetCapability(
259     const std::string& path,
260     std::unique_ptr<base::Value> proposed_value) {
261   DCHECK(proposed_value.get());
262   if (!IsValidPath(path)) {
263     LOG(DFATAL) << "Invalid capability path encountered for SetCapability()";
264     return;
265   }
266 
267   {
268     base::AutoLock auto_lock(validation_lock_);
269     // Check for Validator registered under first key per the Register()
270     // interface.
271     auto validator_it = validator_map_.find(GetFirstKey(path));
272     if (validator_it != validator_map_.end()) {
273       // We do not want to post a task directly for the Validator's Validate()
274       // method here because if another thread is in the middle of unregistering
275       // that Validator, there will be an outstanding call to Validate() that
276       // occurs after it has unregistered. Since ValidatorInfo gets destroyed
277       // in Unregister() on same thread that validation should run on, we can
278       // post a task to the Validator's thread with weak_ptr. This way, if the
279       // Validator gets unregistered, the call to Validate will get skipped.
280       validator_it->second->task_runner()->PostTask(
281           FROM_HERE, base::BindOnce(&ValidatorInfo::Validate,
282                                     validator_it->second->AsWeakPtr(), path,
283                                     std::move(proposed_value)));
284       return;
285     }
286   }
287   // Since we are done checking for a registered Validator at this point, we
288   // can release the lock. All further member access will be for capabilities.
289   // By default, a capability without a validator will be public.
290   SetPublicValidatedValue(path, std::move(proposed_value));
291 }
292 
MergeDictionary(const base::DictionaryValue & dict_value)293 void DeviceCapabilitiesImpl::MergeDictionary(
294     const base::DictionaryValue& dict_value) {
295   for (base::DictionaryValue::Iterator it(dict_value); !it.IsAtEnd();
296        it.Advance()) {
297     SetCapability(it.key(), it.value().CreateDeepCopy());
298   }
299 }
300 
AddCapabilitiesObserver(Observer * observer)301 void DeviceCapabilitiesImpl::AddCapabilitiesObserver(Observer* observer) {
302   DCHECK(observer);
303   observer_list_->AddObserver(observer);
304 }
305 
RemoveCapabilitiesObserver(Observer * observer)306 void DeviceCapabilitiesImpl::RemoveCapabilitiesObserver(Observer* observer) {
307   DCHECK(observer);
308   observer_list_->RemoveObserver(observer);
309 }
310 
SetPublicValidatedValue(const std::string & path,std::unique_ptr<base::Value> new_value)311 void DeviceCapabilitiesImpl::SetPublicValidatedValue(
312     const std::string& path,
313     std::unique_ptr<base::Value> new_value) {
314   // All internal writes/modifications of capabilities must occur on same
315   // thread to avoid race conditions.
316   if (!task_runner_for_writes_->BelongsToCurrentThread()) {
317     task_runner_for_writes_->PostTask(
318         FROM_HERE,
319         base::BindOnce(&DeviceCapabilitiesImpl::SetPublicValidatedValue,
320                        base::Unretained(this), path, std::move(new_value)));
321     return;
322   }
323 
324   DCHECK(IsValidPath(path));
325   DCHECK(new_value.get());
326 
327   // If the capability exists, it must be public (present in all_data_ and
328   // public_data_). We cannot change the privacy of an already existing
329   // capability.
330   bool is_private = all_data_->dictionary().HasKey(path) &&
331                     !public_data_->dictionary().HasKey(path);
332   if (is_private) {
333     NOTREACHED() << "Cannot make a private capability '" << path << "' public.";
334     return;
335   }
336 
337   // We don't need to acquire lock here when reading public_data_ because we
338   // know that all writes to public_data_ must occur serially on thread that
339   // we're on.
340   const base::Value* cur_value = nullptr;
341   bool capability_unchanged =
342       public_data_->dictionary().Get(path, &cur_value) &&
343       cur_value->Equals(new_value.get());
344   if (capability_unchanged) {
345     DVLOG(1) << "Ignoring unchanged public capability: " << path;
346     return;
347   }
348 
349   // In this sequence, we create deep copies for both dictionaries, modify the
350   // copies, and then do a pointer swap. We do this to have minimal time spent
351   // in the data_lock_. If we were to lock and modify the capabilities
352   // dictionary directly, there may be expensive writes that block other
353   // threads.
354   scoped_refptr<Data> new_public_data = GenerateDataWithNewValue(
355       public_data_->dictionary(), path, new_value->CreateDeepCopy());
356   scoped_refptr<Data> new_data = GenerateDataWithNewValue(
357       all_data_->dictionary(), path, std::move(new_value));
358 
359   {
360     base::AutoLock auto_lock(data_lock_);
361     // Using swap instead of assignment operator here because it's a little
362     // faster. Avoids an extra call to AddRef()/Release().
363     public_data_.swap(new_public_data);
364     all_data_.swap(new_data);
365   }
366 
367   // Even though ObserverListThreadSafe notifications are always asynchronous
368   // (posts task even if to same thread), no locks should be held at this point
369   // in the code. This is just to be safe that no deadlocks occur if Observers
370   // call DeviceCapabilities methods in OnCapabilitiesChanged().
371   observer_list_->Notify(FROM_HERE, &Observer::OnCapabilitiesChanged, path);
372 }
373 
SetPrivateValidatedValue(const std::string & path,std::unique_ptr<base::Value> new_value)374 void DeviceCapabilitiesImpl::SetPrivateValidatedValue(
375     const std::string& path,
376     std::unique_ptr<base::Value> new_value) {
377   // All internal writes/modifications of capabilities must occur on same
378   // thread to avoid race conditions.
379   if (!task_runner_for_writes_->BelongsToCurrentThread()) {
380     task_runner_for_writes_->PostTask(
381         FROM_HERE,
382         base::BindOnce(&DeviceCapabilitiesImpl::SetPrivateValidatedValue,
383                        base::Unretained(this), path, std::move(new_value)));
384     return;
385   }
386 
387   DCHECK(IsValidPath(path));
388   DCHECK(new_value.get());
389 
390   // If the capability exists, it must be private (present in all_data_ only).
391   // We cannot change the privacy of an already existing capability.
392   bool is_public = public_data_->dictionary().HasKey(path);
393   if (is_public) {
394     NOTREACHED() << "Cannot make a public capability '" << path << "' private.";
395     return;
396   }
397 
398   // We don't need to acquire lock here when reading all_data_ because we know
399   // that all writes to all_data_ must occur serially on thread that we're on.
400   const base::Value* cur_value = nullptr;
401   bool capability_unchanged = all_data_->dictionary().Get(path, &cur_value) &&
402                               cur_value->Equals(new_value.get());
403   if (capability_unchanged) {
404     DVLOG(1) << "Ignoring unchanged capability: " << path;
405     return;
406   }
407 
408   // In this sequence, we create a deep copy, modify the deep copy, and then
409   // do a pointer swap. We do this to have minimal time spent in the
410   // data_lock_. If we were to lock and modify the capabilities
411   // dictionary directly, there may be expensive writes that block other
412   // threads.
413   scoped_refptr<Data> new_data = GenerateDataWithNewValue(
414       all_data_->dictionary(), path, std::move(new_value));
415 
416   {
417     base::AutoLock auto_lock(data_lock_);
418     // Using swap instead of assignment operator here because it's a little
419     // faster. Avoids an extra call to AddRef()/Release().
420     all_data_.swap(new_data);
421   }
422 
423   // Even though ObserverListThreadSafe notifications are always asynchronous
424   // (posts task even if to same thread), no locks should be held at this point
425   // in the code. This is just to be safe that no deadlocks occur if Observers
426   // call DeviceCapabilities methods in OnCapabilitiesChanged().
427   observer_list_->Notify(FROM_HERE, &Observer::OnCapabilitiesChanged, path);
428 }
429 
430 scoped_refptr<DeviceCapabilities::Data>
GenerateDataWithNewValue(const base::DictionaryValue & dict,const std::string & path,std::unique_ptr<base::Value> new_value)431 DeviceCapabilitiesImpl::GenerateDataWithNewValue(
432     const base::DictionaryValue& dict,
433     const std::string& path,
434     std::unique_ptr<base::Value> new_value) {
435   std::unique_ptr<base::DictionaryValue> dict_deep_copy(dict.CreateDeepCopy());
436   dict_deep_copy->Set(path, std::move(new_value));
437   return CreateData(std::move(dict_deep_copy));
438 }
439 
440 }  // namespace chromecast
441