blob: 5dbcb96a301c5f3a71f354677fee326bcd0f7f5b [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2012 The Chromium Authors
[email protected]9328443b2010-07-30 06:09:402// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/base/network_config_watcher_mac.h"
6
[email protected]9328443b2010-07-30 06:09:407#include <algorithm>
8
[email protected]f7b773c2010-10-23 14:50:279#include "base/compiler_specific.h"
Avi Drissman41c4a412023-01-11 22:45:3710#include "base/functional/bind.h"
Hans Wennborg725d0432020-06-18 13:54:1611#include "base/logging.h"
Keishi Hattori7c3c7182022-06-24 22:18:1412#include "base/memory/raw_ptr.h"
[email protected]9be87ba2011-09-30 01:49:2513#include "base/memory/weak_ptr.h"
Carlos Caballerodd8bf7b042019-07-30 14:14:1514#include "base/message_loop/message_pump_type.h"
Helen Lia4dda2522018-04-03 15:09:1215#include "base/metrics/histogram_macros.h"
Patrick Monette643cdf62021-10-15 19:13:4216#include "base/task/single_thread_task_runner.h"
[email protected]34b99632011-01-01 01:01:0617#include "base/threading/thread.h"
[email protected]221c38d2011-01-20 22:55:2718#include "base/threading/thread_restrictions.h"
Helen Lia4dda2522018-04-03 15:09:1219#include "build/build_config.h"
[email protected]9328443b2010-07-30 06:09:4020
[email protected]9328443b2010-07-30 06:09:4021namespace net {
22
[email protected]6688a4962010-09-07 19:41:3623namespace {
24
Helen Lia4dda2522018-04-03 15:09:1225// SCDynamicStore API does not exist on iOS.
Xiaohan Wang2a6845b2022-01-08 04:40:5726#if !BUILDFLAG(IS_IOS)
Peter Kastinge5a38ed2021-10-02 03:06:3527const base::TimeDelta kRetryInterval = base::Seconds(1);
Helen Lia4dda2522018-04-03 15:09:1228const int kMaxRetry = 5;
29
[email protected]6688a4962010-09-07 19:41:3630// Called back by OS. Calls OnNetworkConfigChange().
31void DynamicStoreCallback(SCDynamicStoreRef /* store */,
32 CFArrayRef changed_keys,
33 void* config_delegate) {
34 NetworkConfigWatcherMac::Delegate* net_config_delegate =
35 static_cast<NetworkConfigWatcherMac::Delegate*>(config_delegate);
36 net_config_delegate->OnNetworkConfigChange(changed_keys);
37}
Xiaohan Wang2a6845b2022-01-08 04:40:5738#endif // !BUILDFLAG(IS_IOS)
[email protected]6688a4962010-09-07 19:41:3639
Gabriel Charette888ce272018-12-20 04:34:4940} // namespace
41
[email protected]f7b773c2010-10-23 14:50:2742class NetworkConfigWatcherMacThread : public base::Thread {
43 public:
David Bienvenua03ac8c2020-11-06 15:55:3944 explicit NetworkConfigWatcherMacThread(
45 NetworkConfigWatcherMac::Delegate* delegate);
46 NetworkConfigWatcherMacThread(const NetworkConfigWatcherMacThread&) = delete;
47 NetworkConfigWatcherMacThread& operator=(
48 const NetworkConfigWatcherMacThread&) = delete;
dchengb03027d2014-10-21 12:00:2049 ~NetworkConfigWatcherMacThread() override;
[email protected]6688a4962010-09-07 19:41:3650
[email protected]f7b773c2010-10-23 14:50:2751 protected:
52 // base::Thread
dchengb03027d2014-10-21 12:00:2053 void Init() override;
54 void CleanUp() override;
[email protected]f7b773c2010-10-23 14:50:2755
56 private:
57 // The SystemConfiguration calls in this function can lead to contention early
58 // on, so we invoke this function later on in startup to keep it fast.
59 void InitNotifications();
60
Helen Lia4dda2522018-04-03 15:09:1261 // Returns whether initializing notifications has succeeded.
62 bool InitNotificationsHelper();
63
Avi Drissman28154a62023-08-22 04:06:4564 base::apple::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source_;
Keishi Hattori7c3c7182022-06-24 22:18:1465 const raw_ptr<NetworkConfigWatcherMac::Delegate> delegate_;
Xiaohan Wang2a6845b2022-01-08 04:40:5766#if !BUILDFLAG(IS_IOS)
Tsuyoshi Horo432981d52022-06-09 09:50:1367 int num_retry_ = 0;
Xiaohan Wang2a6845b2022-01-08 04:40:5768#endif // !BUILDFLAG(IS_IOS)
[email protected]9be87ba2011-09-30 01:49:2569 base::WeakPtrFactory<NetworkConfigWatcherMacThread> weak_factory_;
[email protected]f7b773c2010-10-23 14:50:2770};
71
72NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread(
73 NetworkConfigWatcherMac::Delegate* delegate)
74 : base::Thread("NetworkConfigWatcher"),
75 delegate_(delegate),
Tsuyoshi Horo432981d52022-06-09 09:50:1376 weak_factory_(this) {}
[email protected]f7b773c2010-10-23 14:50:2777
78NetworkConfigWatcherMacThread::~NetworkConfigWatcherMacThread() {
Gabriel Charette888ce272018-12-20 04:34:4979 // This is expected to be invoked during shutdown.
80 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_thread_join;
[email protected]f7b773c2010-10-23 14:50:2781 Stop();
82}
83
84void NetworkConfigWatcherMacThread::Init() {
[email protected]f671d792011-09-02 18:11:4785 delegate_->Init();
86
[email protected]9328443b2010-07-30 06:09:4087 // TODO(willchan): Look to see if there's a better signal for when it's ok to
88 // initialize this, rather than just delaying it by a fixed time.
Peter Kastinge5a38ed2021-10-02 03:06:3589 const base::TimeDelta kInitializationDelay = base::Seconds(1);
fdoray5eeb7642016-06-22 16:11:2890 task_runner()->PostDelayedTask(
kylecharf4fe5172019-02-15 18:53:4991 FROM_HERE,
92 base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
93 weak_factory_.GetWeakPtr()),
[email protected]4a9d9d142012-04-09 16:32:3994 kInitializationDelay);
[email protected]9328443b2010-07-30 06:09:4095}
96
[email protected]f7b773c2010-10-23 14:50:2797void NetworkConfigWatcherMacThread::CleanUp() {
98 if (!run_loop_source_.get())
99 return;
[email protected]9328443b2010-07-30 06:09:40100
[email protected]9328443b2010-07-30 06:09:40101 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
102 kCFRunLoopCommonModes);
103 run_loop_source_.reset();
104}
105
[email protected]f7b773c2010-10-23 14:50:27106void NetworkConfigWatcherMacThread::InitNotifications() {
Helen Lia4dda2522018-04-03 15:09:12107 // If initialization fails, retry after a 1s delay.
108 bool success = InitNotificationsHelper();
109
Xiaohan Wang2a6845b2022-01-08 04:40:57110#if !BUILDFLAG(IS_IOS)
Helen Lia4dda2522018-04-03 15:09:12111 if (!success && num_retry_ < kMaxRetry) {
112 LOG(ERROR) << "Retrying SystemConfiguration registration in 1 second.";
113 task_runner()->PostDelayedTask(
114 FROM_HERE,
115 base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
116 weak_factory_.GetWeakPtr()),
117 kRetryInterval);
118 num_retry_++;
119 return;
120 }
121
Helen Lia4dda2522018-04-03 15:09:12122#else
123 DCHECK(success);
Xiaohan Wang2a6845b2022-01-08 04:40:57124#endif // !BUILDFLAG(IS_IOS)
Helen Lia4dda2522018-04-03 15:09:12125}
126
127bool NetworkConfigWatcherMacThread::InitNotificationsHelper() {
Xiaohan Wang2a6845b2022-01-08 04:40:57128#if !BUILDFLAG(IS_IOS)
[email protected]a27065b2012-08-23 14:32:56129 // SCDynamicStore API does not exist on iOS.
[email protected]9328443b2010-07-30 06:09:40130 // Add a run loop source for a dynamic store to the current run loop.
131 SCDynamicStoreContext context = {
Tsuyoshi Horo291961af2022-06-16 08:51:27132 0, // Version 0.
133 delegate_, // User data.
134 nullptr, // This is not reference counted. No retain function.
135 nullptr, // This is not reference counted. No release function.
136 nullptr, // No description for this.
[email protected]9328443b2010-07-30 06:09:40137 };
Avi Drissman28154a62023-08-22 04:06:45138 base::apple::ScopedCFTypeRef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
Tsuyoshi Horo291961af2022-06-16 08:51:27139 nullptr, CFSTR("org.chromium"), DynamicStoreCallback, &context));
Helen Lia4dda2522018-04-03 15:09:12140 if (!store) {
141 int error = SCError();
142 LOG(ERROR) << "SCDynamicStoreCreate failed with Error: " << error << " - "
143 << SCErrorString(error);
Helen Lia4dda2522018-04-03 15:09:12144 return false;
145 }
Tsuyoshi Horo291961af2022-06-16 08:51:27146 run_loop_source_.reset(
147 SCDynamicStoreCreateRunLoopSource(nullptr, store.get(), 0));
Helen Lia4dda2522018-04-03 15:09:12148 if (!run_loop_source_) {
149 int error = SCError();
150 LOG(ERROR) << "SCDynamicStoreCreateRunLoopSource failed with Error: "
151 << error << " - " << SCErrorString(error);
Helen Lia4dda2522018-04-03 15:09:12152 return false;
153 }
[email protected]9328443b2010-07-30 06:09:40154 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
155 kCFRunLoopCommonModes);
Xiaohan Wang2a6845b2022-01-08 04:40:57156#endif // !BUILDFLAG(IS_IOS)
[email protected]9328443b2010-07-30 06:09:40157
158 // Set up notifications for interface and IP address changes.
[email protected]a301055d2012-01-11 10:58:17159 delegate_->StartReachabilityNotifications();
Xiaohan Wang2a6845b2022-01-08 04:40:57160#if !BUILDFLAG(IS_IOS)
[email protected]6688a4962010-09-07 19:41:36161 delegate_->SetDynamicStoreNotificationKeys(store.get());
Xiaohan Wang2a6845b2022-01-08 04:40:57162#endif // !BUILDFLAG(IS_IOS)
Helen Lia4dda2522018-04-03 15:09:12163 return true;
[email protected]9328443b2010-07-30 06:09:40164}
165
[email protected]f7b773c2010-10-23 14:50:27166NetworkConfigWatcherMac::NetworkConfigWatcherMac(Delegate* delegate)
Tsuyoshi Horof8861cb2022-07-05 23:50:20167 : notifier_thread_(
168 std::make_unique<NetworkConfigWatcherMacThread>(delegate)) {
[email protected]f7b773c2010-10-23 14:50:27169 // We create this notifier thread because the notification implementation
170 // needs a thread with a CFRunLoop, and there's no guarantee that
Carlos Caballerob25fe8472020-07-17 10:27:17171 // CurrentThread::Get() meets that criterion.
Carlos Caballerodd8bf7b042019-07-30 14:14:15172 base::Thread::Options thread_options(base::MessagePumpType::UI, 0);
Olivier Li28a52472021-05-12 03:08:28173 notifier_thread_->StartWithOptions(std::move(thread_options));
[email protected]f7b773c2010-10-23 14:50:27174}
175
Tsuyoshi Horo07c3f0e2022-06-16 07:30:47176NetworkConfigWatcherMac::~NetworkConfigWatcherMac() = default;
[email protected]f7b773c2010-10-23 14:50:27177
[email protected]9328443b2010-07-30 06:09:40178} // namespace net