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