blob: 9e6eda1eeac521c0a769fd3a73b18590d2bc9bfb [file] [log] [blame]
[email protected]a301055d2012-01-11 10:58:171// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[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]9be87ba2011-09-30 01:49:259#include "base/bind.h"
[email protected]f7b773c2010-10-23 14:50:2710#include "base/compiler_specific.h"
Hans Wennborg725d0432020-06-18 13:54:1611#include "base/logging.h"
Avi Drissman13fc8932015-12-20 04:40:4612#include "base/macros.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"
fdoray5eeb7642016-06-22 16:11:2816#include "base/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.
[email protected]a27065b2012-08-23 14:32:5626#if !defined(OS_IOS)
Helen Lia4dda2522018-04-03 15:09:1227const base::TimeDelta kRetryInterval = base::TimeDelta::FromSeconds(1);
28const int kMaxRetry = 5;
29
30// Maps SCError to an enum for UMA logging. These values are persisted to logs,
31// and should not be renumbered. Added to investigate https://crbug.com/547877.
32enum class SCStatusCode {
33 // Unmapped error codes.
34 SC_UNKNOWN = 0,
35
36 // These map to the corresponding SCError.
37 SC_OK = 1,
38 SC_FAILED = 2,
39 SC_INVALID_ARGUMENT = 3,
40 SC_ACCESS_ERROR = 4,
41 SC_NO_KEY = 5,
42 SC_KEY_EXISTS = 6,
43 SC_LOCKED = 7,
44 SC_NEED_LOCK = 8,
45 SC_NO_STORE_SESSION = 9,
46 SC_NO_STORE_SERVER = 10,
47 SC_NOTIFIER_ACTIVE = 11,
48 SC_NO_PREFS_SESSION = 12,
49 SC_PREFS_BUSY = 13,
50 SC_NO_CONFIG_FILE = 14,
51 SC_NO_LINK = 15,
52 SC_STALE = 16,
53 SC_MAX_LINK = 17,
54 SC_REACHABILITY_UNKNOWN = 18,
55 SC_CONNECTION_NO_SERVICE = 19,
56 SC_CONNECTION_IGNORE = 20,
57
58 // Maximum value for histogram bucket.
59 SC_COUNT,
60};
61
62SCStatusCode ConvertToSCStatusCode(int sc_error) {
63 switch (sc_error) {
64 case kSCStatusOK:
65 return SCStatusCode::SC_OK;
66 case kSCStatusFailed:
67 return SCStatusCode::SC_FAILED;
68 case kSCStatusInvalidArgument:
69 return SCStatusCode::SC_INVALID_ARGUMENT;
70 case kSCStatusAccessError:
71 return SCStatusCode::SC_ACCESS_ERROR;
72 case kSCStatusNoKey:
73 return SCStatusCode::SC_NO_KEY;
74 case kSCStatusKeyExists:
75 return SCStatusCode::SC_KEY_EXISTS;
76 case kSCStatusLocked:
77 return SCStatusCode::SC_LOCKED;
78 case kSCStatusNeedLock:
79 return SCStatusCode::SC_NEED_LOCK;
80 case kSCStatusNoStoreSession:
81 return SCStatusCode::SC_NO_STORE_SESSION;
82 case kSCStatusNoStoreServer:
83 return SCStatusCode::SC_NO_STORE_SERVER;
84 case kSCStatusNotifierActive:
85 return SCStatusCode::SC_NOTIFIER_ACTIVE;
86 case kSCStatusNoPrefsSession:
87 return SCStatusCode::SC_NO_PREFS_SESSION;
88 case kSCStatusPrefsBusy:
89 return SCStatusCode::SC_PREFS_BUSY;
90 case kSCStatusNoConfigFile:
91 return SCStatusCode::SC_NO_CONFIG_FILE;
92 case kSCStatusNoLink:
93 return SCStatusCode::SC_NO_LINK;
94 case kSCStatusStale:
95 return SCStatusCode::SC_STALE;
96 case kSCStatusMaxLink:
97 return SCStatusCode::SC_MAX_LINK;
98 case kSCStatusReachabilityUnknown:
99 return SCStatusCode::SC_REACHABILITY_UNKNOWN;
100 case kSCStatusConnectionNoService:
101 return SCStatusCode::SC_CONNECTION_NO_SERVICE;
102 case kSCStatusConnectionIgnore:
103 return SCStatusCode::SC_CONNECTION_IGNORE;
104 default:
105 return SCStatusCode::SC_UNKNOWN;
106 }
107}
108
[email protected]6688a4962010-09-07 19:41:36109// Called back by OS. Calls OnNetworkConfigChange().
110void DynamicStoreCallback(SCDynamicStoreRef /* store */,
111 CFArrayRef changed_keys,
112 void* config_delegate) {
113 NetworkConfigWatcherMac::Delegate* net_config_delegate =
114 static_cast<NetworkConfigWatcherMac::Delegate*>(config_delegate);
115 net_config_delegate->OnNetworkConfigChange(changed_keys);
116}
[email protected]a27065b2012-08-23 14:32:56117#endif // !defined(OS_IOS)
[email protected]6688a4962010-09-07 19:41:36118
Gabriel Charette888ce272018-12-20 04:34:49119} // namespace
120
[email protected]f7b773c2010-10-23 14:50:27121class NetworkConfigWatcherMacThread : public base::Thread {
122 public:
123 NetworkConfigWatcherMacThread(NetworkConfigWatcherMac::Delegate* delegate);
dchengb03027d2014-10-21 12:00:20124 ~NetworkConfigWatcherMacThread() override;
[email protected]6688a4962010-09-07 19:41:36125
[email protected]f7b773c2010-10-23 14:50:27126 protected:
127 // base::Thread
dchengb03027d2014-10-21 12:00:20128 void Init() override;
129 void CleanUp() override;
[email protected]f7b773c2010-10-23 14:50:27130
131 private:
132 // The SystemConfiguration calls in this function can lead to contention early
133 // on, so we invoke this function later on in startup to keep it fast.
134 void InitNotifications();
135
Helen Lia4dda2522018-04-03 15:09:12136 // Returns whether initializing notifications has succeeded.
137 bool InitNotificationsHelper();
138
[email protected]3df79f42013-06-24 18:49:05139 base::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source_;
[email protected]f7b773c2010-10-23 14:50:27140 NetworkConfigWatcherMac::Delegate* const delegate_;
Helen Lia4dda2522018-04-03 15:09:12141#if !defined(OS_IOS)
142 int num_retry_;
143#endif // !defined(OS_IOS)
[email protected]9be87ba2011-09-30 01:49:25144 base::WeakPtrFactory<NetworkConfigWatcherMacThread> weak_factory_;
[email protected]f7b773c2010-10-23 14:50:27145
146 DISALLOW_COPY_AND_ASSIGN(NetworkConfigWatcherMacThread);
147};
148
149NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread(
150 NetworkConfigWatcherMac::Delegate* delegate)
151 : base::Thread("NetworkConfigWatcher"),
152 delegate_(delegate),
Helen Lia4dda2522018-04-03 15:09:12153#if !defined(OS_IOS)
154 num_retry_(0),
155#endif // !defined(OS_IOS)
156 weak_factory_(this) {
157}
[email protected]f7b773c2010-10-23 14:50:27158
159NetworkConfigWatcherMacThread::~NetworkConfigWatcherMacThread() {
Gabriel Charette888ce272018-12-20 04:34:49160 // This is expected to be invoked during shutdown.
161 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_thread_join;
[email protected]f7b773c2010-10-23 14:50:27162 Stop();
163}
164
165void NetworkConfigWatcherMacThread::Init() {
jkarlin5780bb02017-05-31 16:39:49166 base::ThreadRestrictions::SetIOAllowed(true);
[email protected]f671d792011-09-02 18:11:47167 delegate_->Init();
168
[email protected]9328443b2010-07-30 06:09:40169 // TODO(willchan): Look to see if there's a better signal for when it's ok to
170 // initialize this, rather than just delaying it by a fixed time.
[email protected]4a9d9d142012-04-09 16:32:39171 const base::TimeDelta kInitializationDelay = base::TimeDelta::FromSeconds(1);
fdoray5eeb7642016-06-22 16:11:28172 task_runner()->PostDelayedTask(
kylecharf4fe5172019-02-15 18:53:49173 FROM_HERE,
174 base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
175 weak_factory_.GetWeakPtr()),
[email protected]4a9d9d142012-04-09 16:32:39176 kInitializationDelay);
[email protected]9328443b2010-07-30 06:09:40177}
178
[email protected]f7b773c2010-10-23 14:50:27179void NetworkConfigWatcherMacThread::CleanUp() {
180 if (!run_loop_source_.get())
181 return;
[email protected]9328443b2010-07-30 06:09:40182
[email protected]9328443b2010-07-30 06:09:40183 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
184 kCFRunLoopCommonModes);
185 run_loop_source_.reset();
186}
187
[email protected]f7b773c2010-10-23 14:50:27188void NetworkConfigWatcherMacThread::InitNotifications() {
Helen Lia4dda2522018-04-03 15:09:12189 // If initialization fails, retry after a 1s delay.
190 bool success = InitNotificationsHelper();
191
192#if !defined(OS_IOS)
193 if (!success && num_retry_ < kMaxRetry) {
194 LOG(ERROR) << "Retrying SystemConfiguration registration in 1 second.";
195 task_runner()->PostDelayedTask(
196 FROM_HERE,
197 base::BindOnce(&NetworkConfigWatcherMacThread::InitNotifications,
198 weak_factory_.GetWeakPtr()),
199 kRetryInterval);
200 num_retry_++;
201 return;
202 }
203
204 // There are kMaxRetry + 2 buckets. The 0 bucket is where no retry is
205 // performed. The kMaxRetry + 1 bucket is where all retries have failed.
206 int histogram_bucket = num_retry_;
207 if (!success) {
208 DCHECK_EQ(kMaxRetry, num_retry_);
209 histogram_bucket = kMaxRetry + 1;
210 }
211 UMA_HISTOGRAM_EXACT_LINEAR(
212 "Net.NetworkConfigWatcherMac.SCDynamicStore.NumRetry", histogram_bucket,
213 kMaxRetry + 2);
214#else
215 DCHECK(success);
216#endif // !defined(OS_IOS)
217}
218
219bool NetworkConfigWatcherMacThread::InitNotificationsHelper() {
[email protected]a27065b2012-08-23 14:32:56220#if !defined(OS_IOS)
221 // SCDynamicStore API does not exist on iOS.
[email protected]9328443b2010-07-30 06:09:40222 // Add a run loop source for a dynamic store to the current run loop.
223 SCDynamicStoreContext context = {
[email protected]6688a4962010-09-07 19:41:36224 0, // Version 0.
225 delegate_, // User data.
226 NULL, // This is not reference counted. No retain function.
227 NULL, // This is not reference counted. No release function.
228 NULL, // No description for this.
[email protected]9328443b2010-07-30 06:09:40229 };
[email protected]3df79f42013-06-24 18:49:05230 base::ScopedCFTypeRef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
[email protected]9328443b2010-07-30 06:09:40231 NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context));
Helen Lia4dda2522018-04-03 15:09:12232 if (!store) {
233 int error = SCError();
234 LOG(ERROR) << "SCDynamicStoreCreate failed with Error: " << error << " - "
235 << SCErrorString(error);
236 UMA_HISTOGRAM_ENUMERATION(
237 "Net.NetworkConfigWatcherMac.SCDynamicStore.Create",
238 ConvertToSCStatusCode(error), SCStatusCode::SC_COUNT);
239 return false;
240 }
[email protected]9328443b2010-07-30 06:09:40241 run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource(
242 NULL, store.get(), 0));
Helen Lia4dda2522018-04-03 15:09:12243 if (!run_loop_source_) {
244 int error = SCError();
245 LOG(ERROR) << "SCDynamicStoreCreateRunLoopSource failed with Error: "
246 << error << " - " << SCErrorString(error);
247 UMA_HISTOGRAM_ENUMERATION(
248 "Net.NetworkConfigWatcherMac.SCDynamicStore.Create.RunLoopSource",
249 ConvertToSCStatusCode(error), SCStatusCode::SC_COUNT);
250 return false;
251 }
[email protected]9328443b2010-07-30 06:09:40252 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
253 kCFRunLoopCommonModes);
[email protected]a27065b2012-08-23 14:32:56254#endif // !defined(OS_IOS)
[email protected]9328443b2010-07-30 06:09:40255
256 // Set up notifications for interface and IP address changes.
[email protected]a301055d2012-01-11 10:58:17257 delegate_->StartReachabilityNotifications();
[email protected]a27065b2012-08-23 14:32:56258#if !defined(OS_IOS)
[email protected]6688a4962010-09-07 19:41:36259 delegate_->SetDynamicStoreNotificationKeys(store.get());
[email protected]a27065b2012-08-23 14:32:56260#endif // !defined(OS_IOS)
Helen Lia4dda2522018-04-03 15:09:12261 return true;
[email protected]9328443b2010-07-30 06:09:40262}
263
[email protected]f7b773c2010-10-23 14:50:27264NetworkConfigWatcherMac::NetworkConfigWatcherMac(Delegate* delegate)
265 : notifier_thread_(new NetworkConfigWatcherMacThread(delegate)) {
266 // We create this notifier thread because the notification implementation
267 // needs a thread with a CFRunLoop, and there's no guarantee that
Carlos Caballerob25fe8472020-07-17 10:27:17268 // CurrentThread::Get() meets that criterion.
Carlos Caballerodd8bf7b042019-07-30 14:14:15269 base::Thread::Options thread_options(base::MessagePumpType::UI, 0);
[email protected]f7b773c2010-10-23 14:50:27270 notifier_thread_->StartWithOptions(thread_options);
271}
272
273NetworkConfigWatcherMac::~NetworkConfigWatcherMac() {}
274
[email protected]9328443b2010-07-30 06:09:40275} // namespace net