Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3efb32b authored by HsingYuan Lo's avatar HsingYuan Lo
Browse files

Add hash API to manage persistent data

Added APIs
- bta_gattc_hash_load
- bta_gattc_hash_write
- bta_gattc_cache_link

Modified API
- bta_gattc_cache_write

Bug: 154056389
Test: build
Tag: #feature
Change-Id: I60d0b82be278ec514c4aa929f24b7c663cf0f4a2
parent 76e55632
Loading
Loading
Loading
Loading
+189 −3
Original line number Diff line number Diff line
@@ -16,16 +16,36 @@
 *
 ******************************************************************************/

#define LOG_TAG "bt_bta_gattc"

#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <dirent.h>
#include <sys/stat.h>

#include <string>
#include <vector>

#include "bta/gatt/bta_gattc_int.h"
#include "osi/include/log.h"

using gatt::StoredAttribute;
using std::string;
using std::vector;

#define GATT_CACHE_PREFIX "/data/misc/bluetooth/gatt_cache_"
#define GATT_CACHE_VERSION 6

#define GATT_HASH_MAX_SIZE 30
#define GATT_HASH_PATH_PREFIX "/data/misc/bluetooth/gatt_hash_"
#define GATT_HASH_PATH "/data/misc/bluetooth"
#define GATT_HASH_FILE_PREFIX "gatt_hash_"

// Default expired time is 7 days
#define GATT_HASH_EXPIRED_TIME 604800

static void bta_gattc_hash_remove_least_recently_used_if_possible();

static void bta_gattc_generate_cache_file_name(char* buffer, size_t buffer_len,
                                               const RawAddress& bda) {
  snprintf(buffer, buffer_len, "%s%02x%02x%02x%02x%02x%02x", GATT_CACHE_PREFIX,
@@ -33,6 +53,12 @@ static void bta_gattc_generate_cache_file_name(char* buffer, size_t buffer_len,
           bda.address[4], bda.address[5]);
}

static void bta_gattc_generate_hash_file_name(char* buffer, size_t buffer_len,
                                              const Octet16& hash) {
  snprintf(buffer, buffer_len, "%s%s", GATT_HASH_PATH_PREFIX,
           base::HexEncode(hash.data(), 16).c_str());
}

static gatt::Database EMPTY_DB;

/*******************************************************************************
@@ -111,6 +137,24 @@ gatt::Database bta_gattc_cache_load(const RawAddress& server_bda) {
  return bta_gattc_load_db(fname);
}

/*******************************************************************************
 *
 * Function         bta_gattc_hash_load
 *
 * Description      Load GATT cache from storage for server.
 *
 * Parameter        hash: 16-byte value
 *
 * Returns          non-empty GATT database on success, empty GATT database
 *                  otherwise
 *
 ******************************************************************************/
gatt::Database bta_gattc_hash_load(const Octet16& hash) {
  char fname[255] = {0};
  bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
  return bta_gattc_load_db(fname);
}

/*******************************************************************************
 *
 * Function         bta_gattc_store_db
@@ -162,7 +206,9 @@ static bool bta_gattc_store_db(const char* fname,
 * Function         bta_gattc_cache_write
 *
 * Description      This callout function is executed by GATT when a server
 *                  cache is available to save.
 *                  cache is available to save. Before calling this API, make
 *                  sure the device is bonded. Otherwise you might get lots of
 *                  address caches for unbonded devices.
 *
 * Parameter        server_bda: server bd address of this cache belongs to
 *                  database: attributes to save.
@@ -171,9 +217,61 @@ static bool bta_gattc_store_db(const char* fname,
 ******************************************************************************/
void bta_gattc_cache_write(const RawAddress& server_bda,
                           const gatt::Database& database) {
  char addr_file[255] = {0};
  char hash_file[255] = {0};
  Octet16 hash = database.Hash();
  bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
  bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);

  bool result = bta_gattc_hash_write(hash, database);
  // Only link addr_file to hash file when hash_file is created successfully.
  if (result) {
    bta_gattc_cache_link(server_bda, hash);
  }
}

/*******************************************************************************
 *
 * Function         bta_gattc_cache_link
 *
 * Description      Link address-database file to hash-database file
 *
 * Parameter        server_bda: server bd address of this cache belongs to
 *                  hash: 16-byte value
 *
 * Returns          true on success, false otherwise
 *
 ******************************************************************************/
void bta_gattc_cache_link(const RawAddress& server_bda, const Octet16& hash) {
  char addr_file[255] = {0};
  char hash_file[255] = {0};
  bta_gattc_generate_cache_file_name(addr_file, sizeof(addr_file), server_bda);
  bta_gattc_generate_hash_file_name(hash_file, sizeof(hash_file), hash);

  unlink(addr_file);  // remove addr file first if the file exists
  if (link(hash_file, addr_file) == -1) {
    LOG_ERROR("link %s to %s, errno=%d", addr_file, hash_file, errno);
  }
}

/*******************************************************************************
 *
 * Function         bta_gattc_hash_write
 *
 * Description      This callout function is executed by GATT when a server
 *                  cache is available to save for specific hash.
 *
 * Parameter        hash: 16-byte value
 *                  database: gatt::Database instance.
 *
 * Returns          true on success, false otherwise
 *
 ******************************************************************************/
bool bta_gattc_hash_write(const Octet16& hash, const gatt::Database& database) {
  char fname[255] = {0};
  bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
  bta_gattc_store_db(fname, database.Serialize());
  bta_gattc_generate_hash_file_name(fname, sizeof(fname), hash);
  bta_gattc_hash_remove_least_recently_used_if_possible();
  return bta_gattc_store_db(fname, database.Serialize());
}

/*******************************************************************************
@@ -194,3 +292,91 @@ void bta_gattc_cache_reset(const RawAddress& server_bda) {
  bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
  unlink(fname);
}

/*******************************************************************************
 *
 * Function         bta_gattc_hash_remove_least_recently_used_if_possible
 *
 * Description      When the max size reaches, find the oldest item and remove
 *                  it if possible
 *
 * Parameter
 *
 * Returns          void
 *
 ******************************************************************************/
static void bta_gattc_hash_remove_least_recently_used_if_possible() {
  std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir(GATT_HASH_PATH),
                                                 &closedir);
  if (dirp == nullptr) {
    LOG_ERROR("open dir error, dir=%s", GATT_HASH_PATH);
    return;
  }

  time_t current_time = time(NULL);
  time_t lru_time = current_time;
  size_t count = 0;
  string candidate_item;
  vector<string> expired_items;

  LOG_DEBUG("<-----------Start Local Hash Cache---------->");
  dirent* dp;
  while ((dp = readdir(dirp.get())) != nullptr) {
    if (strncmp(".", dp->d_name, 1) == 0 || strncmp("..", dp->d_name, 2) == 0) {
      continue;
    }

    // pattern match: gatt_hash_
    size_t fname_len = strlen(dp->d_name);
    size_t pattern_len = strlen(GATT_HASH_FILE_PREFIX);
    if (pattern_len > fname_len) {
      continue;
    }

    // check if the file name has gatt_hash_ as prefix
    char tmp[255] = {0};
    strncpy(tmp, dp->d_name, pattern_len);
    if (strncmp(tmp, GATT_HASH_FILE_PREFIX, pattern_len) != 0) {
      continue;
    }

    // increase hash file count
    count++;

    // generate the full path, in order to get the state of the file
    snprintf(tmp, 255, "%s/%s", GATT_HASH_PATH, dp->d_name);

    struct stat buf;
    int result = lstat(tmp, &buf);
    LOG_DEBUG("name=%s, result=%d, linknum=%lu, mtime=%lu", dp->d_name, result,
              (unsigned long)buf.st_nlink, (unsigned long)buf.st_mtime);

    // if hard link count of the file is 1, it means no trusted device links to
    // the inode. It is safe to be a candidate to be removed
    if (buf.st_nlink == 1) {
      if (buf.st_mtime < lru_time) {
        lru_time = buf.st_mtime;
        // Find the LRU candidate during for-loop itreation.
        candidate_item.assign(tmp);
      }

      if (buf.st_mtime + GATT_HASH_EXPIRED_TIME < current_time) {
        // Add expired item.
        expired_items.emplace_back(tmp);
      }
    }
  }
  LOG_DEBUG("<-----------End Local Hash Cache------------>");

  // if the number of hash files exceeds the limit, remove the cadidate item.
  if (count > GATT_HASH_MAX_SIZE && !candidate_item.empty()) {
    unlink(candidate_item.c_str());
    LOG_DEBUG("delete hash file (size), name=%s", candidate_item.c_str());
  }

  // If there is any file expired, also delete it.
  for (string expired_item : expired_items) {
    unlink(expired_item.c_str());
    LOG_DEBUG("delete hash file (expired), name=%s", expired_item.c_str());
  }
}
+8 −2
Original line number Diff line number Diff line
@@ -476,11 +476,17 @@ extern tBTA_GATTC_CONN* bta_gattc_conn_find(const RawAddress& remote_bda);
extern tBTA_GATTC_CONN* bta_gattc_conn_find_alloc(const RawAddress& remote_bda);
extern bool bta_gattc_conn_dealloc(const RawAddress& remote_bda);

/* bta_gattc_cache */
extern bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb);

/* bta_gattc_db_storage */
extern gatt::Database bta_gattc_hash_load(const Octet16& hash);
extern bool bta_gattc_hash_write(const Octet16& hash, const gatt::Database& database);
extern gatt::Database bta_gattc_cache_load(const RawAddress& server_bda);
extern void bta_gattc_cache_write(const RawAddress& server_bda,
                                  const gatt::Database& database);
extern void bta_gattc_cache_link(const RawAddress& server_bda,
                                 const Octet16& hash);
extern void bta_gattc_cache_reset(const RawAddress& server_bda);

extern bool bta_gattc_read_db_hash(tBTA_GATTC_CLCB* p_clcb);

#endif /* BTA_GATTC_INT_H */