1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/tether/top_level_host_scan_cache.h"
#include <algorithm>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/tether/active_host.h"
#include "chromeos/ash/components/tether/persistent_host_scan_cache.h"
#include "chromeos/ash/components/timer_factory/timer_factory.h"
namespace ash {
namespace tether {
TopLevelHostScanCache::TopLevelHostScanCache(
std::unique_ptr<ash::timer_factory::TimerFactory> timer_factory,
ActiveHost* active_host,
HostScanCache* network_host_scan_cache,
PersistentHostScanCache* persistent_host_scan_cache)
: timer_factory_(std::move(timer_factory)),
active_host_(active_host),
network_host_scan_cache_(network_host_scan_cache),
persistent_host_scan_cache_(persistent_host_scan_cache) {
InitializeFromPersistentCache();
}
TopLevelHostScanCache::~TopLevelHostScanCache() {
DCHECK(ActiveHost::ActiveHostStatus::DISCONNECTED ==
active_host_->GetActiveHostStatus());
is_shutting_down_ = true;
for (const auto& tether_guid : GetTetherGuidsInCache())
RemoveHostScanResult(tether_guid);
}
void TopLevelHostScanCache::SetHostScanResult(const HostScanCacheEntry& entry) {
auto found_iter = tether_guid_to_timer_map_.find(entry.tether_network_guid);
if (found_iter == tether_guid_to_timer_map_.end()) {
// Only check whether this entry exists in the cache after intialization
// completes; otherwise, this will cause an error when the persistent cache
// has entries that the other caches do not have.
DCHECK(is_initializing_ || !ExistsInCache(entry.tether_network_guid));
// If no Timer exists in the map, add one.
tether_guid_to_timer_map_.emplace(entry.tether_network_guid,
timer_factory_->CreateOneShotTimer());
} else {
DCHECK(ExistsInCache(entry.tether_network_guid));
// If a timer was already running for this entry, stop it. It is started
// again in the StartTimer() call below since the entry now has fresh data.
found_iter->second->Stop();
}
// Set the result in the sub-caches.
network_host_scan_cache_->SetHostScanResult(entry);
persistent_host_scan_cache_->SetHostScanResult(entry);
StartTimer(entry.tether_network_guid);
}
bool TopLevelHostScanCache::RemoveHostScanResultImpl(
const std::string& tether_network_guid) {
DCHECK(!tether_network_guid.empty());
if (active_host_->GetTetherNetworkGuid() == tether_network_guid) {
DCHECK(ExistsInCache(tether_network_guid));
PA_LOG(VERBOSE) << "RemoveHostScanResult() called for Tether network with "
<< "GUID " << tether_network_guid << ", but the "
<< "corresponding device is the active host. Not removing "
<< "this scan result from the cache.";
return false;
}
if (!ExistsInCache(tether_network_guid)) {
PA_LOG(ERROR) << "Attempted to remove a host scan result which does not "
<< "exist in the cache. GUID: " << tether_network_guid;
return false;
}
bool removed_from_network =
network_host_scan_cache_->RemoveHostScanResult(tether_network_guid);
bool removed_from_persistent =
persistent_host_scan_cache_->RemoveHostScanResult(tether_network_guid);
bool removed_from_timer_map =
tether_guid_to_timer_map_.erase(tether_network_guid) == 1u;
// The caches are expected to remain in sync, so it should not be possible
// for one of them to be removed successfully while the other one fails.
DCHECK(removed_from_network && removed_from_persistent &&
removed_from_timer_map);
PA_LOG(VERBOSE) << "Removed cache entry with GUID \"" << tether_network_guid
<< "\".";
// We already DCHECK()ed above that this evaluates to true, but we return the
// AND'ed value here because without this, release builds (without DCHECK())
// will produce a compiler warning of unused variables.
return removed_from_network && removed_from_persistent &&
removed_from_timer_map;
}
std::unordered_set<std::string> TopLevelHostScanCache::GetTetherGuidsInCache() {
std::unordered_set<std::string> tether_guids;
for (const auto& entry : tether_guid_to_timer_map_)
tether_guids.insert(entry.first);
CHECK(tether_guids == persistent_host_scan_cache_->GetTetherGuidsInCache());
// It is expected that `network_host_scan_cache` is empty during shutdown
// (but `tether_guid_to_timer_map_` may still contain recently seen hosts).
if (!is_shutting_down_) {
CHECK(tether_guids == network_host_scan_cache_->GetTetherGuidsInCache());
}
return tether_guids;
}
bool TopLevelHostScanCache::ExistsInCache(
const std::string& tether_network_guid) {
bool exists_in_network_cache =
network_host_scan_cache_->ExistsInCache(tether_network_guid);
bool exists_in_persistent_cache =
persistent_host_scan_cache_->ExistsInCache(tether_network_guid);
bool exists_in_timer_map =
base::Contains(tether_guid_to_timer_map_, tether_network_guid);
// The caches are expected to remain in sync.
DCHECK(exists_in_network_cache == exists_in_persistent_cache &&
exists_in_persistent_cache == exists_in_timer_map);
// We already DCHECK()ed above that these are equal, but we return the AND'ed
// value here because without this, release builds (without DCHECK())
// will produce a compiler warning of unused variables.
return exists_in_network_cache && exists_in_persistent_cache &&
exists_in_timer_map;
}
bool TopLevelHostScanCache::DoesHostRequireSetup(
const std::string& tether_network_guid) {
// |network_host_scan_cache_| does not keep track of this value since the
// networking stack does not store it internally. Instead, query
// |persistent_host_scan_cache_|.
return persistent_host_scan_cache_->DoesHostRequireSetup(tether_network_guid);
}
void TopLevelHostScanCache::InitializeFromPersistentCache() {
is_initializing_ = true;
// If a crash occurs, Tether networks which were previously present will no
// longer be available since they are only stored within NetworkStateHandler
// and not within Shill. Thus, utilize |persistent_host_scan_cache_| to fetch
// metadata about all Tether networks which were present before the crash and
// restore |network_host_scan_cache_|.
std::unordered_map<std::string, HostScanCacheEntry> persisted_entries =
persistent_host_scan_cache_->GetStoredCacheEntries();
for (const auto& it : persisted_entries) {
SetHostScanResult(it.second);
}
is_initializing_ = false;
}
void TopLevelHostScanCache::StartTimer(const std::string& tether_network_guid) {
auto found_iter = tether_guid_to_timer_map_.find(tether_network_guid);
DCHECK(found_iter != tether_guid_to_timer_map_.end());
DCHECK(!found_iter->second->IsRunning());
PA_LOG(VERBOSE)
<< "Starting host scan cache timer for Tether network with GUID "
<< "\"" << tether_network_guid << "\". Will fire in "
<< kNumMinutesBeforeCacheEntryExpires << " minutes.";
found_iter->second->Start(
FROM_HERE, base::Minutes(kNumMinutesBeforeCacheEntryExpires),
base::BindOnce(&TopLevelHostScanCache::OnTimerFired,
weak_ptr_factory_.GetWeakPtr(), tether_network_guid));
}
void TopLevelHostScanCache::OnTimerFired(
const std::string& tether_network_guid) {
if (active_host_->GetTetherNetworkGuid() == tether_network_guid) {
// Log as a warning. This situation should be uncommon in practice since
// KeepAliveScheduler should schedule a new keep-alive status update every
// 4 minutes.
PA_LOG(WARNING) << "Timer fired for Tether network GUID \""
<< tether_network_guid << "\", but the corresponding "
<< "device is the active host. Restarting timer.";
// If the Timer which fired corresponds to the active host, do not remove
// the cache entry. The active host must always remain in the cache so that
// the UI can reflect that it is the connecting/connected network. In this
// case, just restart the timer.
StartTimer(tether_network_guid);
return;
}
PA_LOG(VERBOSE) << "Timer fired for Tether network GUID "
<< tether_network_guid << ". Removing stale scan result.";
RemoveHostScanResult(tether_network_guid);
}
} // namespace tether
} // namespace ash
|