blob: 1d46bfcc12885869d2c27a3c20692471b1b888ce [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/geolocation/position_cache_impl.h"
#include <algorithm>
#include "base/strings/utf_string_conversions.h"
#include "components/device_event_log/device_event_log.h"
#include "services/device/geolocation/wifi_data.h"
#include "services/device/public/mojom/geolocation_internals.mojom.h"
#include "services/device/public/mojom/geoposition.mojom.h"
namespace device {
// static
const size_t PositionCacheImpl::kMaximumSize = 10;
// static
const base::TimeDelta PositionCacheImpl::kMaximumLifetime = base::Days(1);
PositionCacheImpl::CacheEntry::CacheEntry(
const Hash& hash,
mojom::GeopositionPtr position,
std::unique_ptr<base::OneShotTimer> eviction_timer)
: hash_(hash),
position_(std::move(position)),
eviction_timer_(std::move(eviction_timer)) {}
PositionCacheImpl::CacheEntry::~CacheEntry() = default;
PositionCacheImpl::CacheEntry::CacheEntry(CacheEntry&&) = default;
PositionCacheImpl::CacheEntry& PositionCacheImpl::CacheEntry::operator=(
CacheEntry&&) = default;
// static
PositionCacheImpl::Hash PositionCacheImpl::MakeKey(const WifiData& wifi_data) {
// Currently we use only WiFi data and base the key only on the MAC addresses.
std::string key;
const size_t kCharsPerMacAddress = 6 * 3 + 1; // e.g. "11:22:33:44:55:66|"
key.reserve(wifi_data.access_point_data.size() * kCharsPerMacAddress);
const std::string separator("|");
for (const auto& access_point_data : wifi_data.access_point_data) {
key += separator;
key += access_point_data.mac_address;
key += separator;
}
return key;
}
PositionCacheImpl::PositionCacheImpl(const base::TickClock* clock)
: clock_(clock) {
net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
}
PositionCacheImpl::~PositionCacheImpl() {
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
void PositionCacheImpl::CachePosition(const WifiData& wifi_data,
const mojom::Geoposition& position) {
const Hash key = MakeKey(wifi_data);
// If the cache is full, remove the oldest entry.
if (data_.size() == kMaximumSize) {
data_.erase(data_.begin());
}
DCHECK_LT(data_.size(), kMaximumSize);
auto eviction_timer = std::make_unique<base::OneShotTimer>(clock_);
// Ensure that the entry we're adding will be evicted after kMaximumLifetime.
// base::Unretained safe because the timer is indirectly owned by |this|.
eviction_timer->Start(FROM_HERE, kMaximumLifetime,
base::BindOnce(&PositionCacheImpl::EvictEntry,
base::Unretained(this), key));
data_.emplace_back(key, position.Clone(), std::move(eviction_timer));
}
const mojom::Geoposition* PositionCacheImpl::FindPosition(
const WifiData& wifi_data) {
const Hash key = MakeKey(wifi_data);
auto it = std::ranges::find_if(
data_, [key](const CacheEntry& entry) { return entry == key; });
if (it == data_.end()) {
++miss_count_;
last_miss_ = base::Time::Now();
return nullptr;
}
++hit_count_;
last_hit_ = base::Time::Now();
return it->position();
}
size_t PositionCacheImpl::GetPositionCacheSize() const {
return data_.size();
}
const mojom::GeopositionResult* PositionCacheImpl::GetLastUsedNetworkPosition()
const {
GEOLOCATION_LOG(DEBUG) << "Get last used network position";
return last_used_result_.get();
}
void PositionCacheImpl::SetLastUsedNetworkPosition(
const mojom::GeopositionResult& result) {
last_used_result_ = result.Clone();
}
void PositionCacheImpl::FillDiagnostics(
mojom::PositionCacheDiagnostics& diagnostics) {
diagnostics.cache_size = data_.size();
if (last_hit_) {
diagnostics.last_hit = *last_hit_;
}
if (last_miss_) {
diagnostics.last_miss = *last_miss_;
}
if (hit_count_ || miss_count_) {
diagnostics.hit_rate =
static_cast<double>(hit_count_) / (hit_count_ + miss_count_);
}
if (last_used_result_) {
diagnostics.last_network_result = last_used_result_.Clone();
}
}
void PositionCacheImpl::OnNetworkChanged(
net::NetworkChangeNotifier::ConnectionType) {
GEOLOCATION_LOG(DEBUG) << "Network changed";
// OnNetworkChanged is called " when a change occurs to the host
// computer's hardware or software that affects the route network packets
// take to any network server.". This means that whatever position we had
// stored for a wired connection (empty WifiData) could have become stale.
EvictEntry(MakeKey(WifiData()));
last_used_result_.reset();
}
void PositionCacheImpl::EvictEntry(const Hash& hash) {
std::erase(data_, hash);
}
} // namespace device