Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: components/wifi/wifi_service_mac.mm

Issue 64683014: Mac OS X-specific implementation of Networking Private API. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address codereview comments. Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « components/wifi/wifi_service.cc ('k') | components/wifi/wifi_service_win.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/wifi/wifi_service.h"
6
7 #import <netinet/in.h>
8 #import <CoreWLAN/CoreWLAN.h>
9 #import <SystemConfiguration/SystemConfiguration.h>
10
11 #include "base/bind.h"
12 #include "base/mac/scoped_cftyperef.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "components/onc/onc_constants.h"
17
18 #if !defined(MAC_OS_X_VERSION_10_7) || \
19 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
20
21 // Local definitions of API added in Mac OS X 10.7
22
23 @interface CWInterface (LionAPI)
24 - (BOOL)associateToNetwork:(CWNetwork*)network
25 password:(NSString*)password
26 error:(NSError**)error;
27 - (NSSet*)scanForNetworksWithName:(NSString*)networkName
28 error:(NSError**)error;
29 @end
30
31 enum CWChannelBand {
32 kCWChannelBandUnknown = 0,
33 kCWChannelBand2GHz = 1,
34 kCWChannelBand5GHz = 2,
35 };
36
37 @interface CWChannel : NSObject
38 @property(readonly) CWChannelBand channelBand;
39 @end
40
41 @interface CWNetwork (LionAPI)
42 @property(readonly) CWChannel* wlanChannel;
43 @end
44
45 #endif // 10.7
46
47 namespace wifi {
48
49 const char kErrorAssociateToNetwork[] = "Error.AssociateToNetwork";
50 const char kErrorInvalidData[] = "Error.InvalidData";
51 const char kErrorNotConnected[] = "Error.NotConnected";
52 const char kErrorNotFound[] = "Error.NotFound";
53 const char kErrorNotImplemented[] = "Error.NotImplemented";
54 const char kErrorScanForNetworksWithName[] = "Error.ScanForNetworksWithName";
55
56 // Implementation of WiFiService for Mac OS X.
57 class WiFiServiceMac : public WiFiService {
58 public:
59 WiFiServiceMac();
60 virtual ~WiFiServiceMac();
61
62 // WiFiService interface implementation.
63 virtual void Initialize(
64 scoped_refptr<base::SequencedTaskRunner> task_runner) OVERRIDE;
65
66 virtual void UnInitialize() OVERRIDE;
67
68 virtual void GetProperties(const std::string& network_guid,
69 base::DictionaryValue* properties,
70 std::string* error) OVERRIDE;
71
72 virtual void GetManagedProperties(const std::string& network_guid,
73 base::DictionaryValue* managed_properties,
74 std::string* error) OVERRIDE;
75
76 virtual void GetState(const std::string& network_guid,
77 base::DictionaryValue* properties,
78 std::string* error) OVERRIDE;
79
80 virtual void SetProperties(const std::string& network_guid,
81 scoped_ptr<base::DictionaryValue> properties,
82 std::string* error) OVERRIDE;
83
84 virtual void CreateNetwork(bool shared,
85 scoped_ptr<base::DictionaryValue> properties,
86 std::string* network_guid,
87 std::string* error) OVERRIDE;
88
89 virtual void GetVisibleNetworks(const std::string& network_type,
90 base::ListValue* network_list) OVERRIDE;
91
92 virtual void RequestNetworkScan() OVERRIDE;
93
94 virtual void StartConnect(const std::string& network_guid,
95 std::string* error) OVERRIDE;
96
97 virtual void StartDisconnect(const std::string& network_guid,
98 std::string* error) OVERRIDE;
99
100 virtual void SetEventObservers(
101 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
102 const NetworkGuidListCallback& networks_changed_observer,
103 const NetworkGuidListCallback& network_list_changed_observer) OVERRIDE;
104
105 virtual void RequestConnectedNetworkUpdate() OVERRIDE;
106
107 private:
108 // Checks |ns_error| and if is not |nil|, then stores |error_name|
109 // into |error|.
110 bool CheckError(NSError* ns_error,
111 const char* error_name,
112 std::string* error) const;
113
114 // Gets |ssid| from unique |network_guid|.
115 NSString* SSIDFromGUID(const std::string& network_guid) const {
116 return base::SysUTF8ToNSString(network_guid);
117 }
118
119 // Gets unique |network_guid| string based on |ssid|.
120 std::string GUIDFromSSID(NSString* ssid) const {
121 return base::SysNSStringToUTF8(ssid);
122 }
123
124 // Populates |properties| from |network|.
125 void NetworkPropertiesFromCWNetwork(const CWNetwork* network,
126 NetworkProperties* properties) const;
127
128 // Converts |CWSecurityMode| into onc::wifi::k{WPA|WEP}* security constant.
129 std::string SecurityFromCWSecurityMode(CWSecurityMode security) const;
130
131 // Converts |CWChannelBand| into WiFiService::Frequency constant.
132 Frequency FrequencyFromCWChannelBand(CWChannelBand band) const;
133
134 // Gets current |onc::connection_state| for given |network_guid|.
135 std::string GetNetworkConnectionState(const std::string& network_guid) const;
136
137 // Updates |networks_| with the list of visible wireless networks.
138 void UpdateNetworks();
139
140 // Find network by |network_guid| and return iterator to its entry in
141 // |networks_|.
142 NetworkList::iterator FindNetwork(const std::string& network_guid);
143
144 // Handles notification from |wlan_observer_|.
145 void OnWlanObserverNotification();
146
147 // Notifies |network_list_changed_observer_| that list of visible networks has
148 // changed to |networks|.
149 void NotifyNetworkListChanged(const NetworkList& networks);
150
151 // Notifies |networks_changed_observer_| that network |network_guid|
152 // connection state has changed.
153 void NotifyNetworkChanged(const std::string& network_guid);
154
155 // Default interface.
156 base::scoped_nsobject<CWInterface> interface_;
157 // WLAN Notifications observer. |this| doesn't own this reference.
158 id wlan_observer_;
159
160 // Observer to get notified when network(s) have changed (e.g. connect).
161 NetworkGuidListCallback networks_changed_observer_;
162 // Observer to get notified when network list has changed.
163 NetworkGuidListCallback network_list_changed_observer_;
164 // MessageLoopProxy to which events should be posted.
165 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
166 // Task runner for worker tasks.
167 scoped_refptr<base::SequencedTaskRunner> task_runner_;
168 // Cached list of visible networks. Updated by |UpdateNetworks|.
169 NetworkList networks_;
170 // Guid of last known connected network.
171 std::string connected_network_guid_;
172 // Temporary storage of network properties indexed by |network_guid|.
173 base::DictionaryValue network_properties_;
174
175 DISALLOW_COPY_AND_ASSIGN(WiFiServiceMac);
176 };
177
178 WiFiServiceMac::WiFiServiceMac() : wlan_observer_(nil) {
179 }
180
181 WiFiServiceMac::~WiFiServiceMac() {
182 }
183
184 void WiFiServiceMac::Initialize(
185 scoped_refptr<base::SequencedTaskRunner> task_runner) {
186 task_runner_.swap(task_runner);
187 interface_.reset([[CWInterface interface] retain]);
188 if (!interface_) {
189 DVLOG(1) << "Failed to initialize default interface.";
190 return;
191 }
192
193 if (![interface_
194 respondsToSelector:@selector(associateToNetwork:password:error:)]) {
195 DVLOG(1) << "CWInterface does not support associateToNetwork.";
196 interface_.reset();
197 return;
198 }
199 }
200
201 void WiFiServiceMac::UnInitialize() {
202 if (wlan_observer_)
203 [[NSNotificationCenter defaultCenter] removeObserver:wlan_observer_];
204 interface_.reset();
205 }
206
207 void WiFiServiceMac::GetProperties(const std::string& network_guid,
208 base::DictionaryValue* properties,
209 std::string* error) {
210 NetworkList::iterator it = FindNetwork(network_guid);
211 if (it == networks_.end()) {
212 DVLOG(1) << "Network not found:" << network_guid;
213 *error = kErrorNotFound;
214 return;
215 }
216
217 it->connection_state = GetNetworkConnectionState(network_guid);
218 scoped_ptr<base::DictionaryValue> network(it->ToValue(false));
219 properties->Swap(network.get());
220 DVLOG(1) << *properties;
221 }
222
223 void WiFiServiceMac::GetManagedProperties(
224 const std::string& network_guid,
225 base::DictionaryValue* managed_properties,
226 std::string* error) {
227 *error = kErrorNotImplemented;
228 }
229
230 void WiFiServiceMac::GetState(const std::string& network_guid,
231 base::DictionaryValue* properties,
232 std::string* error) {
233 *error = kErrorNotImplemented;
234 }
235
236 void WiFiServiceMac::SetProperties(
237 const std::string& network_guid,
238 scoped_ptr<base::DictionaryValue> properties,
239 std::string* error) {
240 network_properties_.SetWithoutPathExpansion(network_guid,
241 properties.release());
242 }
243
244 void WiFiServiceMac::CreateNetwork(
245 bool shared,
246 scoped_ptr<base::DictionaryValue> properties,
247 std::string* network_guid,
248 std::string* error) {
249 WiFiService::NetworkProperties network_properties;
250 if (!network_properties.UpdateFromValue(*properties)) {
251 *error = kErrorInvalidData;
252 return;
253 }
254
255 std::string guid = network_properties.ssid;
256 if (FindNetwork(guid) != networks_.end()) {
257 *error = kErrorInvalidData;
258 return;
259 }
260 network_properties_.SetWithoutPathExpansion(guid,
261 properties.release());
262 *network_guid = guid;
263 }
264
265 void WiFiServiceMac::GetVisibleNetworks(const std::string& network_type,
266 base::ListValue* network_list) {
267 if (!network_type.empty() &&
268 network_type != onc::network_type::kAllTypes &&
269 network_type != onc::network_type::kWiFi) {
270 return;
271 }
272
273 if (networks_.empty())
274 UpdateNetworks();
275
276 for (WiFiService::NetworkList::const_iterator it = networks_.begin();
277 it != networks_.end();
278 ++it) {
279 scoped_ptr<base::DictionaryValue> network(it->ToValue(true));
280 network_list->Append(network.release());
281 }
282 }
283
284 void WiFiServiceMac::RequestNetworkScan() {
285 DVLOG(1) << "*** RequestNetworkScan";
286 UpdateNetworks();
287 }
288
289 void WiFiServiceMac::StartConnect(const std::string& network_guid,
290 std::string* error) {
291 NSError* ns_error = nil;
292
293 DVLOG(1) << "*** StartConnect: " << network_guid;
294 // Remember previously connected network.
295 std::string connected_network_guid = GUIDFromSSID([interface_ ssid]);
296 // Check whether desired network is already connected.
297 if (network_guid == connected_network_guid)
298 return;
299
300 NSSet* networks = [interface_
301 scanForNetworksWithName:SSIDFromGUID(network_guid)
302 error:&ns_error];
303
304 if (CheckError(ns_error, kErrorScanForNetworksWithName, error))
305 return;
306
307 CWNetwork* network = [networks anyObject];
308 if (network == nil) {
309 // System can't find the network, remove it from the |networks_| and notify
310 // observers.
311 NetworkList::iterator it = FindNetwork(connected_network_guid);
312 if (it != networks_.end()) {
313 networks_.erase(it);
314 // Notify observers that list has changed.
315 NotifyNetworkListChanged(networks_);
316 }
317
318 *error = kErrorNotFound;
319 return;
320 }
321
322 // Check whether WiFi Password is set in |network_properties_|.
323 base::DictionaryValue* properties;
324 base::DictionaryValue* wifi;
325 std::string passphrase;
326 NSString* ns_password = nil;
327 if (network_properties_.GetDictionaryWithoutPathExpansion(network_guid,
328 &properties) &&
329 properties->GetDictionary(onc::network_type::kWiFi, &wifi) &&
330 wifi->GetString(onc::wifi::kPassphrase, &passphrase)) {
331 ns_password = base::SysUTF8ToNSString(passphrase);
332 }
333
334 // Number of attempts to associate to network.
335 static const int kMaxAssociationAttempts = 3;
336 // Try to associate to network several times if timeout or PMK error occurs.
337 for (int i = 0; i < kMaxAssociationAttempts; ++i) {
338 // Nil out the PMK to prevent stale data from causing invalid PMK error
339 // (CoreWLANTypes -3924).
340 [interface_ setPairwiseMasterKey:nil error:&ns_error];
341 if (![interface_ associateToNetwork:network
342 password:ns_password
343 error:&ns_error]) {
344 NSInteger error_code = [ns_error code];
345 if (error_code != kCWTimeoutErr && error_code != kCWInvalidPMKErr) {
346 break;
347 }
348 }
349 }
350 CheckError(ns_error, kErrorAssociateToNetwork, error);
351 }
352
353 void WiFiServiceMac::StartDisconnect(const std::string& network_guid,
354 std::string* error) {
355 DVLOG(1) << "*** StartDisconnect: " << network_guid;
356
357 if (network_guid == GUIDFromSSID([interface_ ssid])) {
358 // Power-cycle the interface to disconnect from current network and connect
359 // to default network.
360 NSError* ns_error = nil;
361 [interface_ setPower:NO error:&ns_error];
362 CheckError(ns_error, kErrorAssociateToNetwork, error);
363 [interface_ setPower:YES error:&ns_error];
364 CheckError(ns_error, kErrorAssociateToNetwork, error);
365 } else {
366 *error = kErrorNotConnected;
367 }
368 }
369
370 void WiFiServiceMac::SetEventObservers(
371 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
372 const NetworkGuidListCallback& networks_changed_observer,
373 const NetworkGuidListCallback& network_list_changed_observer) {
374 message_loop_proxy_.swap(message_loop_proxy);
375 networks_changed_observer_ = networks_changed_observer;
376 network_list_changed_observer_ = network_list_changed_observer;
377
378 // Remove previous OS notifications observer.
379 if (wlan_observer_) {
380 [[NSNotificationCenter defaultCenter] removeObserver:wlan_observer_];
381 wlan_observer_ = nil;
382 }
383
384 // Subscribe to OS notifications.
385 if (!networks_changed_observer_.is_null()) {
386 void (^ns_observer) (NSNotification* notification) =
387 ^(NSNotification* notification) {
388 DVLOG(1) << "Received CWSSIDDidChangeNotification";
389 task_runner_->PostTask(
390 FROM_HERE,
391 base::Bind(&WiFiServiceMac::OnWlanObserverNotification,
392 base::Unretained(this)));
393 };
394
395 wlan_observer_ = [[NSNotificationCenter defaultCenter]
396 addObserverForName:kCWSSIDDidChangeNotification
397 object:nil
398 queue:nil
399 usingBlock:ns_observer];
400 }
401 }
402
403 void WiFiServiceMac::RequestConnectedNetworkUpdate() {
404 OnWlanObserverNotification();
405 }
406
407 std::string WiFiServiceMac::GetNetworkConnectionState(
408 const std::string& network_guid) const {
409 if (network_guid != GUIDFromSSID([interface_ ssid]))
410 return onc::connection_state::kNotConnected;
411
412 // Check whether WiFi network is reachable.
413 struct sockaddr_in local_wifi_address;
414 bzero(&local_wifi_address, sizeof(local_wifi_address));
415 local_wifi_address.sin_len = sizeof(local_wifi_address);
416 local_wifi_address.sin_family = AF_INET;
417 local_wifi_address.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
418 base::ScopedCFTypeRef<SCNetworkReachabilityRef> reachability(
419 SCNetworkReachabilityCreateWithAddress(
420 kCFAllocatorDefault,
421 reinterpret_cast<const struct sockaddr*>(&local_wifi_address)));
422 SCNetworkReachabilityFlags flags = 0u;
423 if (SCNetworkReachabilityGetFlags(reachability, &flags) &&
424 (flags & kSCNetworkReachabilityFlagsReachable) &&
425 (flags & kSCNetworkReachabilityFlagsIsDirect)) {
426 // Network is reachable, report is as |kConnected|.
427 return onc::connection_state::kConnected;
428 }
429 // Network is not reachable yet, so it must be |kConnecting|.
430 return onc::connection_state::kConnecting;
431 }
432
433 void WiFiServiceMac::UpdateNetworks() {
434 NSError* ns_error = nil;
435 NSSet* cw_networks = [interface_ scanForNetworksWithName:nil
436 error:&ns_error];
437 if (ns_error != nil)
438 return;
439
440 std::string connected_bssid = base::SysNSStringToUTF8([interface_ bssid]);
441 std::map<std::string, NetworkProperties*> network_properties_map;
442 networks_.clear();
443
444 // There is one |cw_network| per BSS in |cw_networks|, so go through the set
445 // and combine them, paying attention to supported frequencies.
446 for (CWNetwork* cw_network in cw_networks) {
447 std::string network_guid = GUIDFromSSID([cw_network ssid]);
448 bool update_all_properties = false;
449
450 if (network_properties_map.find(network_guid) ==
451 network_properties_map.end()) {
452 networks_.push_back(NetworkProperties());
453 network_properties_map[network_guid] = &networks_.back();
454 update_all_properties = true;
455 }
456 // If current network is connected, use its properties for this network.
457 if (base::SysNSStringToUTF8([cw_network bssid]) == connected_bssid)
458 update_all_properties = true;
459
460 NetworkProperties* properties = network_properties_map.at(network_guid);
461 if (update_all_properties) {
462 NetworkPropertiesFromCWNetwork(cw_network, properties);
463 } else {
464 properties->frequency_set.insert(FrequencyFromCWChannelBand(
465 [[cw_network wlanChannel] channelBand]));
466 }
467 }
468 // Sort networks, so connected/connecting is up front.
469 networks_.sort(NetworkProperties::OrderByType);
470 // Notify observers that list has changed.
471 NotifyNetworkListChanged(networks_);
472 }
473
474 bool WiFiServiceMac::CheckError(NSError* ns_error,
475 const char* error_name,
476 std::string* error) const {
477 if (ns_error != nil) {
478 DLOG(ERROR) << "*** Error:" << error_name << ":" << [ns_error code];
479 *error = error_name;
480 return true;
481 }
482 return false;
483 }
484
485 void WiFiServiceMac::NetworkPropertiesFromCWNetwork(
486 const CWNetwork* network,
487 NetworkProperties* properties) const {
488 std::string network_guid = GUIDFromSSID([network ssid]);
489
490 properties->connection_state = GetNetworkConnectionState(network_guid);
491 properties->ssid = base::SysNSStringToUTF8([network ssid]);
492 properties->name = properties->ssid;
493 properties->guid = network_guid;
494 properties->type = onc::network_type::kWiFi;
495
496 properties->bssid = base::SysNSStringToUTF8([network bssid]);
497 properties->frequency = FrequencyFromCWChannelBand(
498 static_cast<CWChannelBand>([[network wlanChannel] channelBand]));
499 properties->frequency_set.insert(properties->frequency);
500 properties->security = SecurityFromCWSecurityMode(
501 static_cast<CWSecurityMode>([[network securityMode] intValue]));
502
503 properties->signal_strength = [[network rssi] intValue];
504 }
505
506 std::string WiFiServiceMac::SecurityFromCWSecurityMode(
507 CWSecurityMode security) const {
508 switch (security) {
509 case kCWSecurityModeWPA_Enterprise:
510 case kCWSecurityModeWPA2_Enterprise:
511 return onc::wifi::kWPA_EAP;
512 case kCWSecurityModeWPA_PSK:
513 case kCWSecurityModeWPA2_PSK:
514 return onc::wifi::kWPA_PSK;
515 case kCWSecurityModeWEP:
516 return onc::wifi::kWEP_PSK;
517 case kCWSecurityModeOpen:
518 return onc::wifi::kNone;
519 // TODO(mef): Figure out correct mapping.
520 case kCWSecurityModeWPS:
521 case kCWSecurityModeDynamicWEP:
522 return onc::wifi::kWPA_EAP;
523 }
524 return onc::wifi::kWPA_EAP;
525 }
526
527
528 WiFiService::Frequency WiFiServiceMac::FrequencyFromCWChannelBand(
529 CWChannelBand band) const {
530 return band == kCWChannelBand2GHz ? kFrequency2400 : kFrequency5000;
531 }
532
533 WiFiService::NetworkList::iterator WiFiServiceMac::FindNetwork(
534 const std::string& network_guid) {
535 for (NetworkList::iterator it = networks_.begin();
536 it != networks_.end();
537 ++it) {
538 if (it->guid == network_guid)
539 return it;
540 }
541 return networks_.end();
542 }
543
544 void WiFiServiceMac::OnWlanObserverNotification() {
545 std::string connected_network_guid = GUIDFromSSID([interface_ ssid]);
546 DVLOG(1) << " *** Got Notification: " << connected_network_guid;
547 // Connected network has changed, mark previous one disconnected.
548 if (connected_network_guid != connected_network_guid_) {
549 // Update connection_state of newly connected network.
550 NetworkList::iterator it = FindNetwork(connected_network_guid_);
551 if (it != networks_.end()) {
552 it->connection_state = onc::connection_state::kNotConnected;
553 NotifyNetworkChanged(connected_network_guid_);
554 }
555 connected_network_guid_ = connected_network_guid;
556 }
557
558 if (!connected_network_guid.empty()) {
559 // Update connection_state of newly connected network.
560 NetworkList::iterator it = FindNetwork(connected_network_guid);
561 if (it != networks_.end()) {
562 it->connection_state = GetNetworkConnectionState(connected_network_guid);
563 } else {
564 // Can't find |connected_network_guid| in |networks_|, try to update it.
565 UpdateNetworks();
566 }
567 // Notify that network is connecting.
568 NotifyNetworkChanged(connected_network_guid);
569 // Further network change notification will be sent by detector.
570 }
571 }
572
573 void WiFiServiceMac::NotifyNetworkListChanged(const NetworkList& networks) {
574 if (network_list_changed_observer_.is_null())
575 return;
576
577 NetworkGuidList current_networks;
578 for (NetworkList::const_iterator it = networks.begin();
579 it != networks.end();
580 ++it) {
581 current_networks.push_back(it->guid);
582 }
583
584 message_loop_proxy_->PostTask(
585 FROM_HERE,
586 base::Bind(network_list_changed_observer_, current_networks));
587 }
588
589 void WiFiServiceMac::NotifyNetworkChanged(const std::string& network_guid) {
590 if (networks_changed_observer_.is_null())
591 return;
592
593 DVLOG(1) << "NotifyNetworkChanged: " << network_guid;
594 NetworkGuidList changed_networks(1, network_guid);
595 message_loop_proxy_->PostTask(
596 FROM_HERE,
597 base::Bind(networks_changed_observer_, changed_networks));
598 }
599
600 // static
601 WiFiService* WiFiService::Create() { return new WiFiServiceMac(); }
602
603 } // namespace wifi
OLDNEW
« no previous file with comments | « components/wifi/wifi_service.cc ('k') | components/wifi/wifi_service_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698