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
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/network_config_watcher_mac.h"
#include <algorithm>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
namespace net {
namespace {
// SCDynamicStore API does not exist on iOS.
#if !BUILDFLAG(IS_IOS)
const base::TimeDelta kRetryInterval = base::Seconds(1);
const int kMaxRetry = 5;
// Called back by OS. Calls OnNetworkConfigChange().
void DynamicStoreCallback(SCDynamicStoreRef /* store */,
CFArrayRef changed_keys,
void* config_delegate) {
NetworkConfigWatcherMac::Delegate* net_config_delegate =
static_cast<NetworkConfigWatcherMac::Delegate*>(config_delegate);
net_config_delegate->OnNetworkConfigChange(changed_keys);
}
#endif // !BUILDFLAG(IS_IOS)
} // namespace
class NetworkConfigWatcherMacThread : public base::Thread {
public:
explicit NetworkConfigWatcherMacThread(
NetworkConfigWatcherMac::Delegate* delegate);
NetworkConfigWatcherMacThread(const NetworkConfigWatcherMacThread&) = delete;
NetworkConfigWatcherMacThread& operator=(
const NetworkConfigWatcherMacThread&) = delete;
~NetworkConfigWatcherMacThread() override;
protected:
// base::Thread
void Init() override;
void CleanUp() override;
private:
// The SystemConfiguration calls in this function can lead to contention early
// on, so we invoke this function later on in startup to keep it fast.
void InitNotifications();
// Returns whether initializing notifications has succeeded.
bool InitNotificationsHelper();
base::apple::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source_;
const raw_ptr<NetworkConfigWatcherMac::Delegate> delegate_;
#if !BUILDFLAG(IS_IOS)
int num_retry_ = 0;
#endif // !BUILDFLAG(IS_IOS)
base::WeakPtrFactory<NetworkConfigWatcherMacThread> weak_factory_;
};
NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread(
NetworkConfigWatcherMac::Delegate* delegate)
: base::Thread("NetworkConfigWatcher"),
delegate_(delegate),
weak_factory_(this) {}
NetworkConfigWatcherMacThread::~NetworkConfigWatcherMacThread() {
// This is expected to be invoked during shutdown.
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_thread_join;
Stop();
}
void NetworkConfigWatcherMacThread::Init() {
delegate_->Init();
// TODO(willchan): Look to see if there's a better signal for when it's ok to
// initialize this, rather than just delaying it by a fixed time.
const base::TimeDelta kInitializationDelay = base::Seconds(1);
task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
weak_factory_.GetWeakPtr()),
kInitializationDelay);
}
void NetworkConfigWatcherMacThread::CleanUp() {
if (!run_loop_source_.get())
return;
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
kCFRunLoopCommonModes);
run_loop_source_.reset();
}
void NetworkConfigWatcherMacThread::InitNotifications() {
// If initialization fails, retry after a 1s delay.
bool success = InitNotificationsHelper();
#if !BUILDFLAG(IS_IOS)
if (!success && num_retry_ < kMaxRetry) {
LOG(ERROR) << "Retrying SystemConfiguration registration in 1 second.";
task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
weak_factory_.GetWeakPtr()),
kRetryInterval);
num_retry_++;
return;
}
#else
DCHECK(success);
#endif // !BUILDFLAG(IS_IOS)
}
bool NetworkConfigWatcherMacThread::InitNotificationsHelper() {
#if !BUILDFLAG(IS_IOS)
// SCDynamicStore API does not exist on iOS.
// Add a run loop source for a dynamic store to the current run loop.
SCDynamicStoreContext context = {
0, // Version 0.
delegate_, // User data.
nullptr, // This is not reference counted. No retain function.
nullptr, // This is not reference counted. No release function.
nullptr, // No description for this.
};
base::apple::ScopedCFTypeRef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
nullptr, CFSTR("org.chromium"), DynamicStoreCallback, &context));
if (!store) {
int error = SCError();
LOG(ERROR) << "SCDynamicStoreCreate failed with Error: " << error << " - "
<< SCErrorString(error);
return false;
}
run_loop_source_.reset(
SCDynamicStoreCreateRunLoopSource(nullptr, store.get(), 0));
if (!run_loop_source_) {
int error = SCError();
LOG(ERROR) << "SCDynamicStoreCreateRunLoopSource failed with Error: "
<< error << " - " << SCErrorString(error);
return false;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
kCFRunLoopCommonModes);
#endif // !BUILDFLAG(IS_IOS)
// Set up notifications for interface and IP address changes.
delegate_->StartReachabilityNotifications();
#if !BUILDFLAG(IS_IOS)
delegate_->SetDynamicStoreNotificationKeys(store.get());
#endif // !BUILDFLAG(IS_IOS)
return true;
}
NetworkConfigWatcherMac::NetworkConfigWatcherMac(Delegate* delegate)
: notifier_thread_(
std::make_unique<NetworkConfigWatcherMacThread>(delegate)) {
// We create this notifier thread because the notification implementation
// needs a thread with a CFRunLoop, and there's no guarantee that
// CurrentThread::Get() meets that criterion.
base::Thread::Options thread_options(base::MessagePumpType::UI, 0);
notifier_thread_->StartWithOptions(std::move(thread_options));
}
NetworkConfigWatcherMac::~NetworkConfigWatcherMac() = default;
} // namespace net
|