• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright 2022 The Android Open Source Project
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #define LOG_TAG "bt_bta_gattc"
20 
21 #include <base/logging.h>
22 #include <base/strings/string_number_conversions.h>
23 #include <dirent.h>
24 #include <sys/stat.h>
25 
26 #include <string>
27 #include <vector>
28 
29 #include "bta/gatt/bta_gattc_int.h"
30 #include "osi/include/log.h"
31 
32 using gatt::StoredAttribute;
33 using std::string;
34 using std::vector;
35 
36 #ifdef TARGET_FLOSS
37 #define GATT_CACHE_PREFIX "/var/lib/bluetooth/gatt/gatt_cache_"
38 #define GATT_CACHE_VERSION 6
39 
40 #define GATT_HASH_MAX_SIZE 30
41 #define GATT_HASH_PATH_PREFIX "/var/lib/bluetooth/gatt/gatt_hash_"
42 #define GATT_HASH_PATH "/var/lib/bluetooth/gatt"
43 #define GATT_HASH_FILE_PREFIX "gatt_hash_"
44 #else
45 #define GATT_CACHE_PREFIX "/data/misc/bluetooth/gatt_cache_"
46 #define GATT_CACHE_VERSION 6
47 
48 #define GATT_HASH_MAX_SIZE 30
49 #define GATT_HASH_PATH_PREFIX "/data/misc/bluetooth/gatt_hash_"
50 #define GATT_HASH_PATH "/data/misc/bluetooth"
51 #define GATT_HASH_FILE_PREFIX "gatt_hash_"
52 #endif
53 
54 // Default expired time is 7 days
55 #define GATT_HASH_EXPIRED_TIME 604800
56 
57 static void bta_gattc_hash_remove_least_recently_used_if_possible();
58 
bta_gattc_generate_cache_file_name(char * buffer,size_t buffer_len,const RawAddress & bda)59 static void bta_gattc_generate_cache_file_name(char* buffer, size_t buffer_len,
60                                                const RawAddress& bda) {
61   snprintf(buffer, buffer_len, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX,
62            bda.address[0], bda.address[1], bda.address[2], bda.address[3],
63            bda.address[4], bda.address[5]);
64 }
65 
bta_gattc_generate_hash_file_name(char * buffer,size_t buffer_len,const Octet16 & hash)66 static void bta_gattc_generate_hash_file_name(char* buffer, size_t buffer_len,
67                                               const Octet16& hash) {
68   snprintf(buffer, buffer_len, "%s%s", GATT_HASH_PATH_PREFIX,
69            base::HexEncode(hash.data(), 16).c_str());
70 }
71 
72 static gatt::Database EMPTY_DB;
73 
74 /*******************************************************************************
75  *
76  * Function         bta_gattc_load_db
77  *
78  * Description      Load GATT database from storage.
79  *
80  * Parameter        fname: input file name
81  *
82  * Returns          non-empty GATT database on success, empty GATT database
83  *                  otherwise
84  *
85  ******************************************************************************/
bta_gattc_load_db(const char * fname)86 static gatt::Database bta_gattc_load_db(const char* fname) {
87   FILE* fd = fopen(fname, "rb");
88   if (!fd) {
89     LOG(ERROR) << __func__ << ": can't open GATT cache file " << fname
90                << " for reading, error: " << strerror(errno);
91     return EMPTY_DB;
92   }
93 
94   uint16_t cache_ver = 0;
95   uint16_t num_attr = 0;
96 
97   if (fread(&cache_ver, sizeof(uint16_t), 1, fd) != 1) {
98     LOG(ERROR) << __func__ << ": can't read GATT cache version from: " << fname;
99     goto done;
100   }
101 
102   if (cache_ver != GATT_CACHE_VERSION) {
103     LOG(ERROR) << __func__ << ": wrong GATT cache version: " << fname;
104     goto done;
105   }
106 
107   if (fread(&num_attr, sizeof(uint16_t), 1, fd) != 1) {
108     LOG(ERROR) << __func__
109                << ": can't read number of GATT attributes: " << fname;
110     goto done;
111   }
112 
113   {
114     std::vector<StoredAttribute> attr(num_attr);
115 
116     if (fread(attr.data(), sizeof(StoredAttribute), num_attr, fd) != num_attr) {
117       LOG(ERROR) << __func__ << ": can't read GATT attributes: " << fname;
118       goto done;
119     }
120     fclose(fd);
121 
122     bool success = false;
123     gatt::Database result = gatt::Database::Deserialize(attr, &success);
124     return success ? result : EMPTY_DB;
125   }
126 
127 done:
128   fclose(fd);
129   return EMPTY_DB;
130 }
131 
132 /*******************************************************************************
133  *
134  * Function         bta_gattc_cache_load
135  *
136  * Description      Load GATT cache from storage for server.
137  *
138  * Parameter        bd_address: remote device address
139  *
140  * Returns          non-empty GATT database on success, empty GATT database
141  *                  otherwise
142  *
143  ******************************************************************************/
bta_gattc_cache_load(const RawAddress & server_bda)144 gatt::Database bta_gattc_cache_load(const RawAddress& server_bda) {
145   char fname[255] = {0};
146   bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
147   return bta_gattc_load_db(fname);
148 }
149 
150 /*******************************************************************************
151  *
152  * Function         bta_gattc_hash_load
153  *
154  * Description      Load GATT cache from storage for server.
155  *
156  * Parameter        hash: 16-byte value
157  *
158  * Returns          non-empty GATT database on success, empty GATT database
159  *                  otherwise
160  *
161  ******************************************************************************/
bta_gattc_hash_load(const Octet16 & hash)162 gatt::Database bta_gattc_hash_load(const Octet16& hash) {
163   char fname[255] = {0};
164   bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
165   return bta_gattc_load_db(fname);
166 }
167 
168 /*******************************************************************************
169  *
170  * Function         bta_gattc_store_db
171  *
172  * Description      Storess GATT db.
173  *
174  * Parameter        fname: output file name
175  *                  attr: attributes to save.
176  *
177  * Returns          true on success, false otherwise
178  *
179  ******************************************************************************/
bta_gattc_store_db(const char * fname,const std::vector<StoredAttribute> & attr)180 static bool bta_gattc_store_db(const char* fname,
181                                const std::vector<StoredAttribute>& attr) {
182   FILE* fd = fopen(fname, "wb");
183   if (!fd) {
184     LOG(ERROR) << __func__
185                << ": can't open GATT cache file for writing: " << fname;
186     return false;
187   }
188 
189   uint16_t cache_ver = GATT_CACHE_VERSION;
190   if (fwrite(&cache_ver, sizeof(uint16_t), 1, fd) != 1) {
191     LOG(ERROR) << __func__ << ": can't write GATT cache version: " << fname;
192     fclose(fd);
193     return false;
194   }
195 
196   uint16_t num_attr = attr.size();
197   if (fwrite(&num_attr, sizeof(uint16_t), 1, fd) != 1) {
198     LOG(ERROR) << __func__
199                << ": can't write GATT cache attribute count: " << fname;
200     fclose(fd);
201     return false;
202   }
203 
204   if (fwrite(attr.data(), sizeof(StoredAttribute), num_attr, fd) != num_attr) {
205     LOG(ERROR) << __func__ << ": can't write GATT cache attributes: " << fname;
206     fclose(fd);
207     return false;
208   }
209 
210   fclose(fd);
211   return true;
212 }
213 
214 /*******************************************************************************
215  *
216  * Function         bta_gattc_cache_write
217  *
218  * Description      This callout function is executed by GATT when a server
219  *                  cache is available to save. Before calling this API, make
220  *                  sure the device is bonded. Otherwise you might get lots of
221  *                  address caches for unbonded devices.
222  *
223  * Parameter        server_bda: server bd address of this cache belongs to
224  *                  database: attributes to save.
225  * Returns
226  *
227  ******************************************************************************/
bta_gattc_cache_write(const RawAddress & server_bda,const gatt::Database & database)228 void bta_gattc_cache_write(const RawAddress& server_bda,
229                            const gatt::Database& database) {
230   char addr_file[255] = {0};
231   char hash_file[255] = {0};
232   Octet16 hash = database.Hash();
233   bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
234   bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);
235 
236   bool result = bta_gattc_hash_write(hash, database);
237   // Only link addr_file to hash file when hash_file is created successfully.
238   if (result) {
239     bta_gattc_cache_link(server_bda, hash);
240   }
241 }
242 
243 /*******************************************************************************
244  *
245  * Function         bta_gattc_cache_link
246  *
247  * Description      Link address-database file to hash-database file
248  *
249  * Parameter        server_bda: server bd address of this cache belongs to
250  *                  hash: 16-byte value
251  *
252  * Returns          true on success, false otherwise
253  *
254  ******************************************************************************/
bta_gattc_cache_link(const RawAddress & server_bda,const Octet16 & hash)255 void bta_gattc_cache_link(const RawAddress& server_bda, const Octet16& hash) {
256   char addr_file[255] = {0};
257   char hash_file[255] = {0};
258   bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
259   bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);
260 
261   unlink(addr_file);  // remove addr file first if the file exists
262   if (link(hash_file, addr_file) == -1) {
263     LOG_ERROR("link %s to %s, errno=%d", addr_file, hash_file, errno);
264   }
265 }
266 
267 /*******************************************************************************
268  *
269  * Function         bta_gattc_hash_write
270  *
271  * Description      This callout function is executed by GATT when a server
272  *                  cache is available to save for specific hash.
273  *
274  * Parameter        hash: 16-byte value
275  *                  database: gatt::Database instance.
276  *
277  * Returns          true on success, false otherwise
278  *
279  ******************************************************************************/
bta_gattc_hash_write(const Octet16 & hash,const gatt::Database & database)280 bool bta_gattc_hash_write(const Octet16& hash, const gatt::Database& database) {
281   char fname[255] = {0};
282   bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
283   bta_gattc_hash_remove_least_recently_used_if_possible();
284   return bta_gattc_store_db(fname, database.Serialize());
285 }
286 
287 /*******************************************************************************
288  *
289  * Function         bta_gattc_cache_reset
290  *
291  * Description      This callout function is executed by GATTC to reset cache in
292  *                  application
293  *
294  * Parameter        server_bda: server bd address of this cache belongs to
295  *
296  * Returns          void.
297  *
298  ******************************************************************************/
bta_gattc_cache_reset(const RawAddress & server_bda)299 void bta_gattc_cache_reset(const RawAddress& server_bda) {
300   VLOG(1) << __func__;
301   char fname[255] = {0};
302   bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
303   unlink(fname);
304 }
305 
306 /*******************************************************************************
307  *
308  * Function         bta_gattc_hash_remove_least_recently_used_if_possible
309  *
310  * Description      When the max size reaches, find the oldest item and remove
311  *                  it if possible
312  *
313  * Parameter
314  *
315  * Returns          void
316  *
317  ******************************************************************************/
bta_gattc_hash_remove_least_recently_used_if_possible()318 static void bta_gattc_hash_remove_least_recently_used_if_possible() {
319   std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir(GATT_HASH_PATH),
320                                                  &closedir);
321   if (dirp == nullptr) {
322     LOG_ERROR("open dir error, dir=%s", GATT_HASH_PATH);
323     return;
324   }
325 
326   time_t current_time = time(NULL);
327   time_t lru_time = current_time;
328   size_t count = 0;
329   string candidate_item;
330   vector<string> expired_items;
331 
332   LOG_DEBUG("<-----------Start Local Hash Cache---------->");
333   dirent* dp;
334   while ((dp = readdir(dirp.get())) != nullptr) {
335     if (strncmp(".", dp->d_name, 1) == 0 || strncmp("..", dp->d_name, 2) == 0) {
336       continue;
337     }
338 
339     // pattern match: gatt_hash_
340     size_t fname_len = strlen(dp->d_name);
341     size_t pattern_len = strlen(GATT_HASH_FILE_PREFIX);
342     if (pattern_len > fname_len) {
343       continue;
344     }
345 
346     // check if the file name has gatt_hash_ as prefix
347     char tmp[255] = {0};
348     strncpy(tmp, dp->d_name, pattern_len);
349     if (strncmp(tmp, GATT_HASH_FILE_PREFIX, pattern_len) != 0) {
350       continue;
351     }
352 
353     // increase hash file count
354     count++;
355 
356     // generate the full path, in order to get the state of the file
357     snprintf(tmp, 255, "%s/%s", GATT_HASH_PATH, dp->d_name);
358 
359     struct stat buf;
360     int result = lstat(tmp, &buf);
361     LOG_DEBUG("name=%s, result=%d, linknum=%lu, mtime=%lu", dp->d_name, result,
362               (unsigned long)buf.st_nlink, (unsigned long)buf.st_mtime);
363 
364     // if hard link count of the file is 1, it means no trusted device links to
365     // the inode. It is safe to be a candidate to be removed
366     if (buf.st_nlink == 1) {
367       if (buf.st_mtime < lru_time) {
368         lru_time = buf.st_mtime;
369         // Find the LRU candidate during for-loop itreation.
370         candidate_item.assign(tmp);
371       }
372 
373       if (buf.st_mtime + GATT_HASH_EXPIRED_TIME < current_time) {
374         // Add expired item.
375         expired_items.emplace_back(tmp);
376       }
377     }
378   }
379   LOG_DEBUG("<-----------End Local Hash Cache------------>");
380 
381   // if the number of hash files exceeds the limit, remove the cadidate item.
382   if (count > GATT_HASH_MAX_SIZE && !candidate_item.empty()) {
383     unlink(candidate_item.c_str());
384     LOG_DEBUG("delete hash file (size), name=%s", candidate_item.c_str());
385   }
386 
387   // If there is any file expired, also delete it.
388   for (string expired_item : expired_items) {
389     unlink(expired_item.c_str());
390     LOG_DEBUG("delete hash file (expired), name=%s", expired_item.c_str());
391   }
392 }
393