device_capabilities_impl.cc 16 KB

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