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

Commit 6cfea86b authored by Chen Chen's avatar Chen Chen Committed by Automerger Merge Worker
Browse files

Merge "Add metric_id_manager to GD using existing Address and LruCache of GD" am: f40ecd9c

Original change: https://android-review.googlesource.com/c/platform/system/bt/+/1619844

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I538bc191faabe9d7c38d46d1c7b15a3cfe372753
parents a8507f27 f40ecd9c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -371,6 +371,7 @@ cc_test {
        unit_test: true,
    },
    srcs: [
        "hci/address.cc",
        ":BluetoothCommonSources",
        ":BluetoothPacketSources",
        ":BluetoothPacketParserTestPacketTestSources",
+2 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ filegroup {
    name: "BluetoothCommonSources",
    srcs: [
        "init_flags.cc",
        "metric_id_manager.cc",
        "strings.cc",
        "stop_watch.cc",
    ],
@@ -27,6 +28,7 @@ filegroup {
        "init_flags_test.cc",
        "list_map_test.cc",
        "lru_cache_test.cc",
        "metric_id_manager_unittest.cc",
        "multi_priority_queue_test.cc",
        "numbers_test.cc",
        "strings_test.cc",
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
source_set("BluetoothCommonSources") {
  sources = [
    "init_flags.cc",
    "metric_id_manager.cc"
    "stop_watch.cc",
    "strings.cc",
  ]
+203 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2020 Google, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/
#define LOG_TAG "BluetoothMetricIdManager"

#include <functional>
#include <iterator>
#include <mutex>
#include <optional>
#include <thread>

#include "os/log.h"
#include "common/metric_id_manager.h"

namespace bluetooth {
namespace common {

using hci::Address;

const size_t MetricIdManager::kMaxNumUnpairedDevicesInMemory = 200;
const size_t MetricIdManager::kMaxNumPairedDevicesInMemory = 65000;
const int MetricIdManager::kMinId = 1;
const int MetricIdManager::kMaxId = 65534;  // 2^16 - 2

// id space should always be larger than kMaxNumPairedDevicesInMemory +
// kMaxNumUnpairedDevicesInMemory
static_assert((MetricIdManager::kMaxNumUnpairedDevicesInMemory +
               MetricIdManager::kMaxNumPairedDevicesInMemory) <
                  (MetricIdManager::kMaxId - MetricIdManager::kMinId),
              "id space should always be larger than "
              "kMaxNumPairedDevicesInMemory + MaxNumUnpairedDevicesInMemory");

MetricIdManager::MetricIdManager()
    : paired_device_cache_(kMaxNumPairedDevicesInMemory),
      temporary_device_cache_(kMaxNumUnpairedDevicesInMemory) {}

bool MetricIdManager::Init(
    const std::unordered_map<Address, int>& paired_device_map,
    Callback save_id_callback, Callback forget_device_callback) {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  if (initialized_) {
    return false;
  }

  // init paired_devices_map
  if (paired_device_map.size() > kMaxNumPairedDevicesInMemory) {
    LOG_ALWAYS_FATAL(
        "Paired device map has size %zu, which is bigger than "
        "kMaxNumPairedDevicesInMemory %zu",
        paired_device_map.size(), kMaxNumPairedDevicesInMemory);
    // fail loudly to let caller know
    return false;
  }

  next_id_ = kMinId;
  for (const auto& p : paired_device_map) {
    if (p.second < kMinId || p.second > kMaxId) {
      LOG_ALWAYS_FATAL("Invalid Bluetooth Metric Id in config. "
                       "Id %d of %s is out of range [%d, %d]",
                       p.second, p.first.ToString().c_str(), kMinId, kMaxId);
    }
    auto evicted = paired_device_cache_.insert_or_assign(p.first, p.second);
    if (evicted) {
      ForgetDevicePostprocess(evicted->first, evicted->second);
    }
    id_set_.insert(p.second);
    next_id_ = std::max(next_id_, p.second + 1);
  }
  if (next_id_ > kMaxId) {
    next_id_ = kMinId;
  }

  // init callbacks
  save_id_callback_ = save_id_callback;
  forget_device_callback_ = forget_device_callback;

  return initialized_ = true;
}

MetricIdManager::~MetricIdManager() { Close(); }

bool MetricIdManager::Close() {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  if (!initialized_) {
    return false;
  }
  paired_device_cache_.clear();
  temporary_device_cache_.clear();
  id_set_.clear();
  initialized_ = false;
  return true;
}

MetricIdManager& MetricIdManager::GetInstance() {
  static MetricIdManager metric_id_allocator;
  return metric_id_allocator;
}

bool MetricIdManager::IsEmpty() const {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  return paired_device_cache_.size() == 0 &&
         temporary_device_cache_.size() == 0;
}

// call this function when a new device is scanned
int MetricIdManager::AllocateId(const Address& mac_address) {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  auto it = paired_device_cache_.find(mac_address);
  // if already have an id, return it
  if (it != paired_device_cache_.end()) {
    return it->second;
  }
  it = temporary_device_cache_.find(mac_address);
  if (it != temporary_device_cache_.end()) {
    return it->second;
  }

  // find next available id
  while (id_set_.count(next_id_) > 0) {
    next_id_++;
    if (next_id_ > kMaxId) {
      next_id_ = kMinId;
      LOG_WARN("Bluetooth metric id overflow.");
    }
  }
  int id = next_id_++;
  id_set_.insert(id);
  auto evicted = temporary_device_cache_.insert_or_assign(mac_address, id);
  if (evicted) {
    this->id_set_.extract(evicted->second);
  }

  if (next_id_ > kMaxId) {
    next_id_ = kMinId;
  }
  return id;
}

// call this function when a device is paired
bool MetricIdManager::SaveDevice(const Address& mac_address) {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  if (paired_device_cache_.contains(mac_address)) {
    return true;
  }
  if (!temporary_device_cache_.contains(mac_address)) {
    LOG_ERROR("Failed to save device because device is not in "
              "temporary_device_cache_");
    return false;
  }
  auto opt = temporary_device_cache_.extract(mac_address);
  if (!opt) {
    LOG_ERROR("Failed to remove device from temporary_device_cache_");
    return false;
  }
  int id = opt->second;
  auto evicted = paired_device_cache_.insert_or_assign(mac_address, id);
  if (evicted) {
    ForgetDevicePostprocess(evicted->first, evicted->second);
  }
  if (!save_id_callback_(mac_address, id)) {
    LOG_ERROR("Callback returned false after saving the device");
    return false;
  }
  return true;
}

// call this function when a device is forgotten
void MetricIdManager::ForgetDevice(const Address& mac_address) {
  std::lock_guard<std::mutex> lock(id_allocator_mutex_);
  auto opt = paired_device_cache_.extract(mac_address);
  if (!opt) {
    LOG_ERROR("Failed to remove device from paired_device_cache_");
    return;
  }
  ForgetDevicePostprocess(mac_address, opt->second);
}

bool MetricIdManager::IsValidId(const int id) {
  return id >= kMinId && id <= kMaxId;
}

void MetricIdManager::ForgetDevicePostprocess(const Address& mac_address,
                                                const int id) {
  id_set_.erase(id);
  forget_device_callback_(mac_address, id);
}

}  // namespace common
}  // namespace bluetooth
+140 −0
Original line number Diff line number Diff line
/******************************************************************************
 *
 *  Copyright 2020 Google, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

#pragma once

#include <mutex>
#include <string>
#include <thread>
#include <unordered_set>

#include "common/lru_cache.h"
#include "hci/address.h"

namespace bluetooth {
namespace common {

class MetricIdManager {
 public:
  using Callback = std::function<bool(const hci::Address& address, const int id)>;

  static const size_t kMaxNumUnpairedDevicesInMemory;
  static const size_t kMaxNumPairedDevicesInMemory;

  static const int kMinId;
  static const int kMaxId;

  ~MetricIdManager();

  /**
   * Get the instance of singleton
   *
   * @return MetricIdManager&
   */
  static MetricIdManager& GetInstance();

  /**
   * Initialize the allocator
   *
   * @param paired_device_map map from mac_address to id already saved
   * in the disk before init
   * @param save_id_callback a callback that will be called after successfully
   * saving id for a paired device
   * @param forget_device_callback a callback that will be called after
   * successful id deletion for forgotten device,
   * @return true if successfully initialized
   */
  bool Init(
      const std::unordered_map<hci::Address, int>& paired_device_map,
      Callback save_id_callback,
      Callback forget_device_callback);

  /**
   * Close the allocator. should be called when Bluetooth process is killed
   *
   * @return true if successfully close
   */
  bool Close();

  /**
   * Check if no id saved in memory
   *
   * @return true if no id is saved
   */
  bool IsEmpty() const;

  /**
   * Allocate an id for a scanned device, or return the id if there is already
   * one
   *
   * @param mac_address mac address of Bluetooth device
   * @return the id of device
   */
  int AllocateId(const hci::Address& mac_address);

  /**
   * Save the id for a paired device
   *
   * @param mac_address mac address of Bluetooth device
   * @return true if save successfully
   */
  bool SaveDevice(const hci::Address& mac_address);

  /**
   * Delete the id for a device to be forgotten
   *
   * @param mac_address mac address of Bluetooth device
   */
  void ForgetDevice(const hci::Address& mac_address);

  /**
   * Check if an id is valid.
   * The id should be less than or equal to kMaxId and bigger than or equal to
   * kMinId
   *
   * @param mac_address mac address of Bluetooth device
   * @return true if delete successfully
   */
  static bool IsValidId(const int id);

 protected:
  // Singleton
  MetricIdManager();

 private:
  mutable std::mutex id_allocator_mutex_;

  LruCache<hci::Address, int> paired_device_cache_;
  LruCache<hci::Address, int> temporary_device_cache_;
  std::unordered_set<int> id_set_;

  int next_id_{kMinId};
  bool initialized_{false};
  Callback save_id_callback_;
  Callback forget_device_callback_;

  void ForgetDevicePostprocess(const hci::Address& mac_address,
      const int id);

  // delete copy constructor for singleton
  MetricIdManager(MetricIdManager const&) = delete;
  MetricIdManager& operator=(MetricIdManager const&) = delete;
};

}  // namespace common
}  // namespace bluetooth
Loading