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/strings/string_number_conversions.h>
22 #include <bluetooth/log.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 "gatt/database.h"
31 #include "os/log.h"
32 #include "stack/include/gattdefs.h"
33 #include "types/bluetooth/uuid.h"
34
35 using namespace bluetooth;
36
37 using gatt::StoredAttribute;
38 using std::string;
39 using std::vector;
40
41 #ifdef TARGET_FLOSS
42 #define GATT_CACHE_PREFIX "/var/lib/bluetooth/gatt/gatt_cache_"
43 #define GATT_CACHE_VERSION 6
44
45 #define GATT_HASH_MAX_SIZE 30
46 #define GATT_HASH_PATH_PREFIX "/var/lib/bluetooth/gatt/gatt_hash_"
47 #define GATT_HASH_PATH "/var/lib/bluetooth/gatt"
48 #define GATT_HASH_FILE_PREFIX "gatt_hash_"
49 #else
50 #define GATT_CACHE_PREFIX "/data/misc/bluetooth/gatt_cache_"
51 #define GATT_CACHE_VERSION 6
52
53 #define GATT_HASH_MAX_SIZE 30
54 #define GATT_HASH_PATH_PREFIX "/data/misc/bluetooth/gatt_hash_"
55 #define GATT_HASH_PATH "/data/misc/bluetooth"
56 #define GATT_HASH_FILE_PREFIX "gatt_hash_"
57 #endif
58
59 // Default expired time is 7 days
60 #define GATT_HASH_EXPIRED_TIME 604800
61
62 static void bta_gattc_hash_remove_least_recently_used_if_possible();
63
bta_gattc_generate_cache_file_name(char * buffer,size_t buffer_len,const RawAddress & bda)64 static void bta_gattc_generate_cache_file_name(char* buffer, size_t buffer_len,
65 const RawAddress& bda) {
66 snprintf(buffer, buffer_len, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX,
67 bda.address[0], bda.address[1], bda.address[2], bda.address[3],
68 bda.address[4], bda.address[5]);
69 }
70
bta_gattc_generate_hash_file_name(char * buffer,size_t buffer_len,const Octet16 & hash)71 static void bta_gattc_generate_hash_file_name(char* buffer, size_t buffer_len,
72 const Octet16& hash) {
73 snprintf(buffer, buffer_len, "%s%s", GATT_HASH_PATH_PREFIX,
74 base::HexEncode(hash.data(), 16).c_str());
75 }
76
77 static gatt::Database EMPTY_DB;
78
79 /*******************************************************************************
80 *
81 * Function bta_gattc_load_db
82 *
83 * Description Load GATT database from storage.
84 *
85 * Parameter fname: input file name
86 *
87 * Returns non-empty GATT database on success, empty GATT database
88 * otherwise
89 *
90 ******************************************************************************/
bta_gattc_load_db(const char * fname)91 static gatt::Database bta_gattc_load_db(const char* fname) {
92 FILE* fd = fopen(fname, "rb");
93 if (!fd) {
94 log::error("can't open GATT cache file {} for reading, error: {}", fname,
95 strerror(errno));
96 return EMPTY_DB;
97 }
98
99 uint16_t cache_ver = 0;
100 uint16_t num_attr = 0;
101
102 if (fread(&cache_ver, sizeof(uint16_t), 1, fd) != 1) {
103 log::error("can't read GATT cache version from: {}", fname);
104 goto done;
105 }
106
107 if (cache_ver != GATT_CACHE_VERSION) {
108 log::error("wrong GATT cache version: {}", fname);
109 goto done;
110 }
111
112 if (fread(&num_attr, sizeof(uint16_t), 1, fd) != 1) {
113 log::error("can't read number of GATT attributes: {}", fname);
114 goto done;
115 }
116
117 {
118 std::vector<StoredAttribute> attr(num_attr);
119
120 if (fread(attr.data(), sizeof(StoredAttribute), num_attr, fd) != num_attr) {
121 log::error("can't read GATT attributes: {}", fname);
122 goto done;
123 }
124 fclose(fd);
125
126 bool success = false;
127 gatt::Database result = gatt::Database::Deserialize(attr, &success);
128 return success ? result : EMPTY_DB;
129 }
130
131 done:
132 fclose(fd);
133 return EMPTY_DB;
134 }
135
136 /*******************************************************************************
137 *
138 * Function bta_gattc_cache_load
139 *
140 * Description Load GATT cache from storage for server.
141 *
142 * Parameter bd_address: remote device address
143 *
144 * Returns non-empty GATT database on success, empty GATT database
145 * otherwise
146 *
147 ******************************************************************************/
bta_gattc_cache_load(const RawAddress & server_bda)148 gatt::Database bta_gattc_cache_load(const RawAddress& server_bda) {
149 char fname[255] = {0};
150 bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
151 return bta_gattc_load_db(fname);
152 }
153
154 /*******************************************************************************
155 *
156 * Function bta_gattc_hash_load
157 *
158 * Description Load GATT cache from storage for server.
159 *
160 * Parameter hash: 16-byte value
161 *
162 * Returns non-empty GATT database on success, empty GATT database
163 * otherwise
164 *
165 ******************************************************************************/
bta_gattc_hash_load(const Octet16 & hash)166 gatt::Database bta_gattc_hash_load(const Octet16& hash) {
167 char fname[255] = {0};
168 bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
169 return bta_gattc_load_db(fname);
170 }
171
SerializeStoredAttribute(const StoredAttribute & attr,std::vector<uint8_t> & bytes)172 void StoredAttribute::SerializeStoredAttribute(const StoredAttribute& attr,
173 std::vector<uint8_t>& bytes) {
174 size_t original_size = bytes.size();
175 // handle
176 bytes.push_back(attr.handle & 0xff);
177 bytes.push_back(attr.handle >> 8);
178 auto uuid = attr.type.To128BitBE();
179 bytes.insert(bytes.cend(), uuid.cbegin(), uuid.cend());
180
181 if (attr.type.Is16Bit()) {
182 switch (attr.type.As16Bit()) {
183 /* primary or secondary service definition */
184 case GATT_UUID_PRI_SERVICE:
185 case GATT_UUID_SEC_SERVICE:
186 uuid = attr.value.service.uuid.To128BitBE();
187 bytes.insert(bytes.cend(), uuid.cbegin(), uuid.cend());
188 bytes.push_back(attr.value.service.end_handle & 0xff);
189 bytes.push_back(attr.value.service.end_handle >> 8);
190 break;
191 case GATT_UUID_INCLUDE_SERVICE:
192 /* included service definition */
193 bytes.push_back(attr.value.included_service.handle & 0xff);
194 bytes.push_back(attr.value.included_service.handle >> 8);
195 bytes.push_back(attr.value.included_service.end_handle & 0xff);
196 bytes.push_back(attr.value.included_service.end_handle >> 8);
197 uuid = attr.value.included_service.uuid.To128BitBE();
198 bytes.insert(bytes.cend(), uuid.cbegin(), uuid.cend());
199 break;
200 case GATT_UUID_CHAR_DECLARE:
201 /* characteristic definition */
202 bytes.push_back(attr.value.characteristic.properties);
203 bytes.push_back(0); // Padding byte
204 bytes.push_back(attr.value.characteristic.value_handle & 0xff);
205 bytes.push_back(attr.value.characteristic.value_handle >> 8);
206 uuid = attr.value.characteristic.uuid.To128BitBE();
207 bytes.insert(bytes.cend(), uuid.cbegin(), uuid.cend());
208 break;
209 case GATT_UUID_CHAR_EXT_PROP:
210 /* for descriptor we store value only for
211 * «Characteristic Extended Properties» */
212 bytes.push_back(attr.value.characteristic_extended_properties & 0xff);
213 bytes.push_back(attr.value.characteristic_extended_properties >> 8);
214 break;
215 default:
216 // log::verbose("Unhandled type UUID 0x{:04x}", attr.type.As16Bit());
217 break;
218 }
219 }
220 // padding
221 for (size_t i = bytes.size() - original_size;
222 i < StoredAttribute::kSizeOnDisk; i++) {
223 bytes.push_back(0);
224 }
225 }
226
227 /*******************************************************************************
228 *
229 * Function bta_gattc_store_db
230 *
231 * Description Storess GATT db.
232 *
233 * Parameter fname: output file name
234 * attr: attributes to save.
235 *
236 * Returns true on success, false otherwise
237 *
238 ******************************************************************************/
bta_gattc_store_db(const char * fname,const std::vector<StoredAttribute> & attr)239 static bool bta_gattc_store_db(const char* fname,
240 const std::vector<StoredAttribute>& attr) {
241 FILE* fd = fopen(fname, "wb");
242 if (!fd) {
243 log::error("can't open GATT cache file for writing: {}", fname);
244 return false;
245 }
246
247 uint16_t cache_ver = GATT_CACHE_VERSION;
248 if (fwrite(&cache_ver, sizeof(uint16_t), 1, fd) != 1) {
249 log::error("can't write GATT cache version: {}", fname);
250 fclose(fd);
251 return false;
252 }
253
254 uint16_t num_attr = attr.size();
255 if (fwrite(&num_attr, sizeof(uint16_t), 1, fd) != 1) {
256 log::error("can't write GATT cache attribute count: {}", fname);
257 fclose(fd);
258 return false;
259 }
260
261 std::vector<uint8_t> db_bytes;
262 db_bytes.reserve(num_attr * StoredAttribute::kSizeOnDisk);
263 for (const auto attribute : attr) {
264 StoredAttribute::SerializeStoredAttribute(attribute, db_bytes);
265 }
266
267 if (fwrite(db_bytes.data(), sizeof(uint8_t), db_bytes.size(), fd) !=
268 db_bytes.size()) {
269 log::error("can't write GATT cache attributes: {}", fname);
270 fclose(fd);
271 return false;
272 }
273
274 fclose(fd);
275 return true;
276 }
277
278 /*******************************************************************************
279 *
280 * Function bta_gattc_cache_write
281 *
282 * Description This callout function is executed by GATT when a server
283 * cache is available to save. Before calling this API, make
284 * sure the device is bonded. Otherwise you might get lots of
285 * address caches for unbonded devices.
286 *
287 * Parameter server_bda: server bd address of this cache belongs to
288 * database: attributes to save.
289 * Returns
290 *
291 ******************************************************************************/
bta_gattc_cache_write(const RawAddress & server_bda,const gatt::Database & database)292 void bta_gattc_cache_write(const RawAddress& server_bda,
293 const gatt::Database& database) {
294 char addr_file[255] = {0};
295 char hash_file[255] = {0};
296 Octet16 hash = database.Hash();
297 bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
298 bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);
299
300 bool result = bta_gattc_hash_write(hash, database);
301 // Only link addr_file to hash file when hash_file is created successfully.
302 if (result) {
303 bta_gattc_cache_link(server_bda, hash);
304 }
305 }
306
307 /*******************************************************************************
308 *
309 * Function bta_gattc_cache_link
310 *
311 * Description Link address-database file to hash-database file
312 *
313 * Parameter server_bda: server bd address of this cache belongs to
314 * hash: 16-byte value
315 *
316 * Returns true on success, false otherwise
317 *
318 ******************************************************************************/
bta_gattc_cache_link(const RawAddress & server_bda,const Octet16 & hash)319 void bta_gattc_cache_link(const RawAddress& server_bda, const Octet16& hash) {
320 char addr_file[255] = {0};
321 char hash_file[255] = {0};
322 bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
323 bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);
324
325 unlink(addr_file); // remove addr file first if the file exists
326 if (link(hash_file, addr_file) == -1) {
327 log::error("link {} to {}, errno={}", addr_file, hash_file, errno);
328 }
329 }
330
331 /*******************************************************************************
332 *
333 * Function bta_gattc_hash_write
334 *
335 * Description This callout function is executed by GATT when a server
336 * cache is available to save for specific hash.
337 *
338 * Parameter hash: 16-byte value
339 * database: gatt::Database instance.
340 *
341 * Returns true on success, false otherwise
342 *
343 ******************************************************************************/
bta_gattc_hash_write(const Octet16 & hash,const gatt::Database & database)344 bool bta_gattc_hash_write(const Octet16& hash, const gatt::Database& database) {
345 char fname[255] = {0};
346 bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
347 bta_gattc_hash_remove_least_recently_used_if_possible();
348 return bta_gattc_store_db(fname, database.Serialize());
349 }
350
351 /*******************************************************************************
352 *
353 * Function bta_gattc_cache_reset
354 *
355 * Description This callout function is executed by GATTC to reset cache in
356 * application
357 *
358 * Parameter server_bda: server bd address of this cache belongs to
359 *
360 * Returns void.
361 *
362 ******************************************************************************/
bta_gattc_cache_reset(const RawAddress & server_bda)363 void bta_gattc_cache_reset(const RawAddress& server_bda) {
364 log::verbose("");
365 char fname[255] = {0};
366 bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
367 unlink(fname);
368 }
369
370 /*******************************************************************************
371 *
372 * Function bta_gattc_hash_remove_least_recently_used_if_possible
373 *
374 * Description When the max size reaches, find the oldest item and remove
375 * it if possible
376 *
377 * Parameter
378 *
379 * Returns void
380 *
381 ******************************************************************************/
bta_gattc_hash_remove_least_recently_used_if_possible()382 static void bta_gattc_hash_remove_least_recently_used_if_possible() {
383 std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir(GATT_HASH_PATH),
384 &closedir);
385 if (dirp == nullptr) {
386 log::error("open dir error, dir={}", GATT_HASH_PATH);
387 return;
388 }
389
390 time_t current_time = time(NULL);
391 time_t lru_time = current_time;
392 size_t count = 0;
393 string candidate_item;
394 vector<string> expired_items;
395
396 log::debug("<-----------Start Local Hash Cache---------->");
397 dirent* dp;
398 while ((dp = readdir(dirp.get())) != nullptr) {
399 if (strncmp(".", dp->d_name, 1) == 0 || strncmp("..", dp->d_name, 2) == 0) {
400 continue;
401 }
402
403 // pattern match: gatt_hash_
404 size_t fname_len = strlen(dp->d_name);
405 size_t pattern_len = strlen(GATT_HASH_FILE_PREFIX);
406 if (pattern_len > fname_len) {
407 continue;
408 }
409
410 // check if the file name has gatt_hash_ as prefix
411 char tmp[255] = {0};
412 strncpy(tmp, dp->d_name, pattern_len);
413 if (strncmp(tmp, GATT_HASH_FILE_PREFIX, pattern_len) != 0) {
414 continue;
415 }
416
417 // increase hash file count
418 count++;
419
420 // generate the full path, in order to get the state of the file
421 snprintf(tmp, 255, "%s/%s", GATT_HASH_PATH, dp->d_name);
422
423 struct stat buf;
424 int result = lstat(tmp, &buf);
425 log::debug("name={}, result={}, linknum={}, mtime={}", dp->d_name, result,
426 (unsigned long)buf.st_nlink, (unsigned long)buf.st_mtime);
427
428 // if hard link count of the file is 1, it means no trusted device links to
429 // the inode. It is safe to be a candidate to be removed
430 if (buf.st_nlink == 1) {
431 if (buf.st_mtime < lru_time) {
432 lru_time = buf.st_mtime;
433 // Find the LRU candidate during for-loop itreation.
434 candidate_item.assign(tmp);
435 }
436
437 if (buf.st_mtime + GATT_HASH_EXPIRED_TIME < current_time) {
438 // Add expired item.
439 expired_items.emplace_back(tmp);
440 }
441 }
442 }
443 log::debug("<-----------End Local Hash Cache------------>");
444
445 // if the number of hash files exceeds the limit, remove the cadidate item.
446 if (count > GATT_HASH_MAX_SIZE && !candidate_item.empty()) {
447 unlink(candidate_item.c_str());
448 log::debug("delete hash file (size), name={}", candidate_item);
449 }
450
451 // If there is any file expired, also delete it.
452 for (string expired_item : expired_items) {
453 unlink(expired_item.c_str());
454 log::debug("delete hash file (expired), name={}", expired_item);
455 }
456 }
457