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