• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current_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(&current_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 }