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