1 /******************************************************************************
2 *
3 * Copyright (C) 2014 Google, Inc.
4 * Copyright (C) 2018 The Linux Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************************/
19
20 #define LOG_TAG "device_iot_config"
21 #include "device_iot_config_int.h"
22
23 #include <base/logging.h>
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include <mutex>
31 #include <string>
32
33 #include "bt_types.h"
34 #include "btcore/include/module.h"
35 #include "btif/include/btif_api.h"
36 #include "btif/include/btif_util.h"
37 #include "common/init_flags.h"
38 #include "device/include/device_iot_config.h"
39 #include "osi/include/alarm.h"
40 #include "osi/include/allocator.h"
41 #include "osi/include/compat.h"
42 #include "osi/include/config.h"
43 #include "osi/include/log.h"
44 #include "osi/include/osi.h"
45 #include "osi/include/properties.h"
46
47 extern enum ConfigSource device_iot_config_source;
48
49 extern int device_iot_config_devices_loaded;
50 extern char device_iot_config_time_created[TIME_STRING_LENGTH];
51
52 extern std::mutex config_lock; // protects operations on |config|.
53 extern std::unique_ptr<config_t> config;
54 extern alarm_t* config_timer;
55
56 using bluetooth::common::InitFlags;
57
cleanup()58 static void cleanup() {
59 alarm_free(config_timer);
60 config_timer = NULL;
61 config.reset();
62 config = NULL;
63 device_iot_config_source = NOT_LOADED;
64 }
65
66 // Module lifecycle functions
device_iot_config_module_init(void)67 future_t* device_iot_config_module_init(void) {
68 LOG_INFO("");
69
70 std::unique_lock<std::mutex> lock(config_lock);
71
72 config_timer = NULL;
73 config = NULL;
74
75 if (device_iot_config_is_factory_reset()) {
76 device_iot_config_delete_files();
77 }
78
79 config = config_new(IOT_CONFIG_FILE_PATH);
80 device_iot_config_source = ORIGINAL;
81 if (!config) {
82 LOG_WARN("Unable to load config file: %s; using backup.",
83 IOT_CONFIG_FILE_PATH);
84 config = config_new(IOT_CONFIG_BACKUP_PATH);
85 device_iot_config_source = BACKUP;
86 }
87
88 if (!config) {
89 LOG_ERROR("Unable to load bak file; creating empty config.");
90 config = config_new_empty();
91 device_iot_config_source = NEW_FILE;
92 }
93
94 if (!config) {
95 LOG_ERROR("Unable to allocate a config object.");
96 cleanup();
97 return future_new_immediate(FUTURE_FAIL);
98 }
99
100 int version;
101 if (device_iot_config_source == NEW_FILE) {
102 version = DEVICE_IOT_INFO_CURRENT_VERSION;
103 config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
104 } else {
105 version = config_get_int(*config, INFO_SECTION, VERSION_KEY, -1);
106 if (version == -1) {
107 version = DEVICE_IOT_INFO_FIRST_VERSION;
108 config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
109 }
110 }
111
112 if (version != DEVICE_IOT_INFO_CURRENT_VERSION) {
113 LOG_INFO("Version in file is %d, CURRENT_VERSION is %d ", version,
114 DEVICE_IOT_INFO_CURRENT_VERSION);
115 remove(IOT_CONFIG_FILE_PATH);
116 remove(IOT_CONFIG_BACKUP_PATH);
117 config.reset();
118 config = config_new_empty();
119 if (!config) {
120 LOG_ERROR("Unable to allocate a config object.");
121 cleanup();
122 return future_new_immediate(FUTURE_FAIL);
123 }
124 config_set_int(config.get(), INFO_SECTION, VERSION_KEY,
125 DEVICE_IOT_INFO_CURRENT_VERSION);
126 device_iot_config_source = NEW_FILE;
127 }
128
129 device_iot_config_devices_loaded = device_iot_config_get_device_num(*config);
130 LOG_INFO("Devices loaded %d", device_iot_config_devices_loaded);
131
132 // Read or set config file creation timestamp
133 const std::string* time_str =
134 config_get_string(*config, INFO_SECTION, FILE_CREATED_TIMESTAMP, NULL);
135 if (time_str != NULL) {
136 strncpy(device_iot_config_time_created, time_str->c_str(),
137 TIME_STRING_LENGTH);
138 } else {
139 // Read or set config file creation timestamp
140 time_t current_time = time(NULL);
141 struct tm* time_created = localtime(¤t_time);
142 if (time_created) {
143 strftime(device_iot_config_time_created, TIME_STRING_LENGTH,
144 TIME_STRING_FORMAT, time_created);
145 config_set_string(config.get(), INFO_SECTION, FILE_CREATED_TIMESTAMP,
146 std::string(device_iot_config_time_created));
147 }
148 }
149
150 // TODO: use a non-wake alarm for this once we have
151 // API support for it. There's no need to wake the system to
152 // write back to disk.
153 config_timer = alarm_new("btif.iot.config");
154 if (!config_timer) {
155 LOG_ERROR("Unable to create alarm.");
156 cleanup();
157 return future_new_immediate(FUTURE_FAIL);
158 }
159
160 return future_new_immediate(FUTURE_SUCCESS);
161 }
162
device_iot_config_module_start_up(void)163 future_t* device_iot_config_module_start_up(void) {
164 LOG_INFO("");
165 return future_new_immediate(FUTURE_SUCCESS);
166 }
167
device_iot_config_module_shut_down(void)168 future_t* device_iot_config_module_shut_down(void) {
169 LOG_INFO("");
170 device_iot_config_flush();
171 return future_new_immediate(FUTURE_SUCCESS);
172 }
173
device_iot_config_module_clean_up(void)174 future_t* device_iot_config_module_clean_up(void) {
175 LOG_INFO("");
176 if (config_timer != NULL && alarm_is_scheduled(config_timer))
177 device_iot_config_flush();
178
179 alarm_free(config_timer);
180 config_timer = NULL;
181
182 std::unique_lock<std::mutex> lock(config_lock);
183 config.reset();
184 config = NULL;
185 return future_new_immediate(FUTURE_SUCCESS);
186 }
187
188 EXPORT_SYMBOL module_t device_iot_config_module = {
189 .name = DEVICE_IOT_CONFIG_MODULE,
190 .init = device_iot_config_module_init,
191 .start_up = device_iot_config_module_start_up,
192 .shut_down = device_iot_config_module_shut_down,
193 .clean_up = device_iot_config_module_clean_up};
194
device_iot_config_write(uint16_t event,UNUSED_ATTR char * p_param)195 void device_iot_config_write(uint16_t event, UNUSED_ATTR char* p_param) {
196 if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return;
197
198 CHECK(config != NULL);
199 CHECK(config_timer != NULL);
200
201 LOG_INFO("evt=%d", event);
202 std::unique_lock<std::mutex> lock(config_lock);
203 if (event == IOT_CONFIG_SAVE_TIMER_FIRED_EVT) {
204 device_iot_config_set_modified_time();
205 }
206
207 rename(IOT_CONFIG_FILE_PATH, IOT_CONFIG_BACKUP_PATH);
208 device_iot_config_restrict_device_num(*config);
209 device_iot_config_sections_sort_by_entry_key(*config,
210 device_iot_config_compare_key);
211 config_save(*config, IOT_CONFIG_FILE_PATH);
212 }
213
device_iot_config_sections_sort_by_entry_key(config_t & config,compare_func comp)214 void device_iot_config_sections_sort_by_entry_key(config_t& config,
215 compare_func comp) {
216 for (auto& entry : config.sections) {
217 entry.entries.sort(comp);
218 }
219 }
220
device_iot_config_has_key_value(const std::string & section,const std::string & key,const std::string & value_str)221 bool device_iot_config_has_key_value(const std::string& section,
222 const std::string& key,
223 const std::string& value_str) {
224 CHECK(config != NULL);
225
226 const std::string* stored_value =
227 config_get_string(*config, section, key, NULL);
228
229 if (!stored_value || value_str.compare(*stored_value) != 0) return false;
230
231 return true;
232 }
233
device_iot_config_save_async(void)234 void device_iot_config_save_async(void) {
235 if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return;
236
237 CHECK(config != NULL);
238 CHECK(config_timer != NULL);
239
240 LOG_VERBOSE("");
241 alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS,
242 device_iot_config_timer_save_cb, NULL);
243 }
244
device_iot_config_get_device_num(const config_t & conf)245 int device_iot_config_get_device_num(const config_t& conf) {
246 if (!InitFlags::IsDeviceIotConfigLoggingEnabled()) return 0;
247
248 int devices = 0;
249
250 for (const auto& entry : conf.sections) {
251 if (RawAddress::IsValidAddress(entry.name)) {
252 devices++;
253 }
254 }
255 return devices;
256 }
257
device_iot_config_restrict_device_num(config_t & config)258 void device_iot_config_restrict_device_num(config_t& config) {
259 int curr_num = device_iot_config_get_device_num(config);
260 int removed_devices = 0;
261 int need_remove_devices_num;
262
263 if (curr_num <= DEVICES_MAX_NUM_IN_IOT_INFO_FILE) {
264 return;
265 }
266
267 need_remove_devices_num =
268 curr_num - DEVICES_MAX_NUM_IN_IOT_INFO_FILE + DEVICES_NUM_MARGIN;
269 LOG_INFO("curr_num=%d, need_remove_num=%d", curr_num,
270 need_remove_devices_num);
271
272 std::list<section_t>::iterator i = config.sections.begin();
273 while (i != config.sections.end()) {
274 if (!RawAddress::IsValidAddress(i->name)) {
275 ++i;
276 continue;
277 }
278
279 i = config.sections.erase(i);
280 if (++removed_devices >= need_remove_devices_num) {
281 break;
282 }
283 }
284 }
285
device_iot_config_compare_key(const entry_t & first,const entry_t & second)286 bool device_iot_config_compare_key(const entry_t& first,
287 const entry_t& second) {
288 bool first_is_profile_key = strncasecmp(first.key.c_str(), "Profile", 7) == 0;
289 bool second_is_profile_key =
290 strncasecmp(second.key.c_str(), "Profile", 7) == 0;
291 if (!first_is_profile_key && !second_is_profile_key) {
292 return true;
293 } else if (first_is_profile_key && second_is_profile_key) {
294 return strcasecmp(first.key.c_str(), second.key.c_str()) <= 0;
295 } else {
296 return !first_is_profile_key;
297 }
298 }
299
device_iot_config_timer_save_cb(UNUSED_ATTR void * data)300 void device_iot_config_timer_save_cb(UNUSED_ATTR void* data) {
301 // Moving file I/O to btif context instead of timer callback because
302 // it usually takes a lot of time to be completed, introducing
303 // delays during A2DP playback causing blips or choppiness.
304 LOG_VERBOSE("");
305 btif_transfer_context(device_iot_config_write,
306 IOT_CONFIG_SAVE_TIMER_FIRED_EVT, NULL, 0, NULL);
307 }
308
device_iot_config_set_modified_time()309 void device_iot_config_set_modified_time() {
310 time_t current_time = time(NULL);
311 struct tm* time_modified = localtime(¤t_time);
312 char device_iot_config_time_modified[TIME_STRING_LENGTH];
313 if (time_modified) {
314 strftime(device_iot_config_time_modified, TIME_STRING_LENGTH,
315 TIME_STRING_FORMAT, time_modified);
316 config_set_string(config.get(), INFO_SECTION, FILE_MODIFIED_TIMESTAMP,
317 device_iot_config_time_modified);
318 }
319 }
320
device_iot_config_is_factory_reset(void)321 bool device_iot_config_is_factory_reset(void) {
322 return osi_property_get_bool(PROPERTY_FACTORY_RESET, false);
323 }
324
device_iot_config_delete_files(void)325 void device_iot_config_delete_files(void) {
326 remove(IOT_CONFIG_FILE_PATH);
327 remove(IOT_CONFIG_BACKUP_PATH);
328 }