1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
17 
18 #include <functional>
19 #include <list>
20 #include <mutex>
21 #include <optional>
22 #include <queue>
23 #include <string>
24 #include <string_view>
25 #include <unordered_set>
26 #include <utility>
27 #include <vector>
28 
29 #include "common/list_map.h"
30 #include "common/lru_cache.h"
31 #include "hci/address.h"
32 #include "os/utils.h"
33 #include "storage/mutation_entry.h"
34 
35 namespace bluetooth {
36 namespace storage {
37 
38 class Mutation;
39 
40 // A memory operated section-key-value structured config
41 //
42 // A section can be either persistent or temporary. When a section becomes persistent, all its
43 // properties are written to disk.
44 //
45 // A section becomes persistent when a property that is part of persistent_property_names_ is
46 // written to config cache; A section becomes temporary when all properties that are part of
47 // persistent_property_names_ is removed
48 //
49 // The definition of persistent sections is up to the user and is defined through the
50 // |persistent_property_names| argument. When these properties are link key properties, then
51 // persistent sections is equal to bonded devices
52 //
53 // This class is thread safe
54 class ConfigCache {
55 public:
56   ConfigCache(size_t temp_device_capacity,
57               std::unordered_set<std::string_view> persistent_property_names);
58 
59   ConfigCache(const ConfigCache&) = delete;
60   ConfigCache& operator=(const ConfigCache&) = delete;
61 
62   virtual ~ConfigCache() = default;
63 
64   // no copy
65 
66   // can move
67   ConfigCache(ConfigCache&& other) noexcept;
68   ConfigCache& operator=(ConfigCache&& other) noexcept;
69 
70   // comparison operators, callback doesn't count
71   bool operator==(const ConfigCache& rhs) const;
72   bool operator!=(const ConfigCache& rhs) const;
73 
74   // observers
75   virtual bool HasSection(const std::string& section) const;
76   virtual bool HasProperty(const std::string& section, const std::string& property) const;
77   // Get property, return std::nullopt if section or property does not exist
78   virtual std::optional<std::string> GetProperty(const std::string& section,
79                                                  const std::string& property) const;
80   // Returns a copy of persistent device MAC addresses
81   virtual std::vector<std::string> GetPersistentSections() const;
82   // Return true if a section is persistent
83   virtual bool IsPersistentSection(const std::string& section) const;
84   // Return true if a section has one of the properties in |property_names|
85   virtual bool HasAtLeastOneMatchingPropertiesInSection(
86           const std::string& section,
87           const std::unordered_set<std::string_view>& property_names) const;
88   // Return true if a property is part of persistent_property_names_
89   virtual bool IsPersistentProperty(const std::string& property) const;
90   // Serialize to legacy config format
91   virtual std::string SerializeToLegacyFormat() const;
92   // Return a copy of pair<section_name, property_value> with property
93   struct SectionAndPropertyValue {
94     std::string section;
95     std::string property;
96     bool operator==(const SectionAndPropertyValue& rhs) const {
97       return section == rhs.section && property == rhs.property;
98     }
99     bool operator!=(const SectionAndPropertyValue& rhs) const { return !(*this == rhs); }
100   };
101   virtual std::vector<SectionAndPropertyValue> GetSectionNamesWithProperty(
102           const std::string& property) const;
103   // Returns all property names in the specific section.
104   virtual std::vector<std::string> GetPropertyNames(const std::string& section) const;
105 
106   // modifiers
107   // Commit all mutation entries in sequence while holding the config mutex
108   virtual void Commit(std::queue<MutationEntry>& mutation);
109   virtual void SetProperty(std::string section, std::string property, std::string value);
110   virtual bool RemoveSection(const std::string& section);
111   virtual bool RemoveProperty(const std::string& section, const std::string& property);
112   virtual void ConvertEncryptOrDecryptKeyIfNeeded();
113   // TODO: have a systematic way of doing this instead of specialized methods
114   // Remove sections with |property| set
115   virtual void RemoveSectionWithProperty(const std::string& property);
116   // remove all content in this config cache, restore it to the state after the explicit constructor
117   virtual void Clear();
118   // Set a callback to notify interested party that a persistent config change has just happened
119   virtual void SetPersistentConfigChangedCallback(
120           std::function<void()> persistent_config_changed_callback);
121 
122   // Device config specific methods
123   // TODO: methods here should be moved to a device specific config cache if this config cache is
124   // supposed to be generic Legacy stack has device type inconsistencies, this method is trying to
125   // fix it
126   virtual bool FixDeviceTypeInconsistencies();
127 
128   // static methods
129   // Check if section is formatted as a MAC address
130   static bool IsDeviceSection(const std::string& section);
131 
132   // constants
133   static const std::string kDefaultSectionName;
134 
135 private:
136   mutable std::recursive_mutex mutex_;
137   // A callback to notify interested party that a persistent config change has just happened, empty
138   // by default
139   std::function<void()> persistent_config_changed_callback_;
140   // A set of property names that if set would make a section persistent and if non of these
141   // properties are set, a section would become temporary again
142   std::unordered_set<std::string_view> persistent_property_names_;
143   // Common section that does not relate to remote device, will be written to disk
144   common::ListMap<std::string, common::ListMap<std::string, std::string>> information_sections_;
145   // Information about persistent devices, normally paired, will be written to disk
146   common::ListMap<std::string, common::ListMap<std::string, std::string>> persistent_devices_;
147   // Information about temporary devices, normally unpaired, will not be written to disk, will be
148   // evicted automatically if capacity exceeds given value during initialization
149   common::LruCache<std::string, common::ListMap<std::string, std::string>> temporary_devices_;
150 
151   // Convenience method to check if the callback is valid before calling it
PersistentConfigChangedCallback()152   inline void PersistentConfigChangedCallback() const {
153     if (persistent_config_changed_callback_) {
154       persistent_config_changed_callback_();
155     }
156   }
157 };
158 
159 }  // namespace storage
160 }  // namespace bluetooth
161