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

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: Fix trybot compilation error. 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
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) {
tbarzic 2014/01/28 18:10:43 nit: drop {}
mef 2014/01/28 18:54:32 Done.
298 return;
299 }
300
301 NSSet* networks = [interface_
302 scanForNetworksWithName:SSIDFromGUID(network_guid)
303 error:&ns_error];
304
305 if (CheckError(ns_error, kErrorScanForNetworksWithName, error))
306 return;
307
308 CWNetwork* network = [networks anyObject];
309 if (network == nil) {
310 // System can't find the network, remove it from the |networks_| and notify
311 // observers.
312 NetworkList::iterator it = FindNetwork(connected_network_guid);
313 if (it != networks_.end()) {
314 networks_.erase(it);
315 // Notify observers that list has changed.
316 NotifyNetworkListChanged(networks_);
317 }
318
319 *error = kErrorNotFound;
320 return;
321 }
322
323 // Check whether WiFi Password is set in |network_properties_|.
324 base::DictionaryValue* properties;
325 base::DictionaryValue* wifi;
326 std::string passphrase;
327 NSString* ns_password = nil;
328 if (network_properties_.GetDictionaryWithoutPathExpansion(network_guid,
329 &properties) &&
330 properties->GetDictionary(onc::network_type::kWiFi, &wifi) &&
331 wifi->GetString(onc::wifi::kPassphrase, &passphrase)) {
332 ns_password = base::SysUTF8ToNSString(passphrase);
333 }
334
335 // Number of attempts to associate to network.
336 static const int kMaxAssociationAttempts = 3;
337 // Try to associate to network several times if timeout or PMK error occurs.
338 for (int i = 0; i < kMaxAssociationAttempts; ++i) {
339 // Nil out the PMK to prevent stale data from causing invalid PMK error
340 // (CoreWLANTypes -3924).
341 [interface_ setPairwiseMasterKey:nil error:&ns_error];
342 if (![interface_ associateToNetwork:network
343 password:ns_password
344 error:&ns_error]) {
345 NSInteger error_code = [ns_error code];
346 if (error_code != kCWTimeoutErr && error_code != kCWInvalidPMKErr) {
347 break;
348 }
349 }
350 }
351 CheckError(ns_error, kErrorAssociateToNetwork, error);
352 }
353
354 void WiFiServiceMac::StartDisconnect(const std::string& network_guid,
355 std::string* error) {
356 DVLOG(1) << "*** StartDisconnect: " << network_guid;
357
358 if (network_guid == GUIDFromSSID([interface_ ssid])) {
359 // Power-cycle the interface to disconnect from current network and connect
360 // to default network.
361 NSError* ns_error = nil;
362 [interface_ setPower:NO error:&ns_error];
363 CheckError(ns_error, kErrorAssociateToNetwork, error);
364 [interface_ setPower:YES error:&ns_error];
365 CheckError(ns_error, kErrorAssociateToNetwork, error);
366 } else {
367 *error = kErrorNotConnected;
368 }
369 }
370
371 void WiFiServiceMac::SetEventObservers(
372 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
373 const NetworkGuidListCallback& networks_changed_observer,
374 const NetworkGuidListCallback& network_list_changed_observer) {
375 message_loop_proxy_.swap(message_loop_proxy);
376 networks_changed_observer_ = networks_changed_observer;
377 network_list_changed_observer_ = network_list_changed_observer;
378
379 // Remove previous OS notifications observer.
380 if (wlan_observer_) {
381 [[NSNotificationCenter defaultCenter] removeObserver:wlan_observer_];
382 wlan_observer_ = nil;
383 }
384
385 // Subscribe to OS notifications.
386 if (!networks_changed_observer_.is_null()) {
387 void (^ns_observer) (NSNotification* notification) =
388 ^(NSNotification* notification) {
389 DVLOG(1) << "Received CWSSIDDidChangeNotification";
390 task_runner_->PostTask(
391 FROM_HERE,
392 base::Bind(&WiFiServiceMac::OnWlanObserverNotification,
393 base::Unretained(this)));
394 };
395
396 wlan_observer_ = [[NSNotificationCenter defaultCenter]
397 addObserverForName:kCWSSIDDidChangeNotification
398 object:nil
399 queue:nil
400 usingBlock:ns_observer];
401 }
402 }
403
404 void WiFiServiceMac::RequestConnectedNetworkUpdate() {
405 OnWlanObserverNotification();
406 }
407
408 std::string WiFiServiceMac::GetNetworkConnectionState(
409 const std::string& network_guid) const {
410 if (network_guid != GUIDFromSSID([interface_ ssid]))
411 return onc::connection_state::kNotConnected;
412
413 // Check whether WiFi network is reachable.
414 struct sockaddr_in local_wifi_address;
415 bzero(&local_wifi_address, sizeof(local_wifi_address));
416 local_wifi_address.sin_len = sizeof(local_wifi_address);
417 local_wifi_address.sin_family = AF_INET;
418 local_wifi_address.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
419 base::ScopedCFTypeRef<SCNetworkReachabilityRef> reachability(
420 SCNetworkReachabilityCreateWithAddress(
421 kCFAllocatorDefault,
422 reinterpret_cast<const struct sockaddr*>(&local_wifi_address)));
423 SCNetworkReachabilityFlags flags = 0u;
424 if (SCNetworkReachabilityGetFlags(reachability, &flags) &&
425 (flags & kSCNetworkReachabilityFlagsReachable) &&
426 (flags & kSCNetworkReachabilityFlagsIsDirect)) {
427 // Network is reachable, report is as |kConnected|.
428 return onc::connection_state::kConnected;
429 }
430 // Network is not reachable yet, so it must be |kConnecting|.
431 return onc::connection_state::kConnecting;
432 }
433
434 void WiFiServiceMac::UpdateNetworks() {
435 NSError* ns_error = nil;
436 NSSet* cw_networks = [interface_ scanForNetworksWithName:nil
437 error:&ns_error];
438 if (ns_error != nil)
439 return;
440
441 std::string connected_bssid = base::SysNSStringToUTF8([interface_ bssid]);
442 std::map<std::string, NetworkProperties*> network_properties_map;
443 networks_.clear();
444
445 // There is one |cw_network| per BSS in |cw_networks|, so go through the set
446 // and combine them, paying attention to supported frequencies.
447 for (CWNetwork* cw_network in cw_networks) {
448 std::string network_guid = GUIDFromSSID([cw_network ssid]);
449 bool update_all_properties = false;
450
451 if (network_properties_map.find(network_guid) ==
452 network_properties_map.end()) {
453 networks_.push_back(NetworkProperties());
454 network_properties_map[network_guid] = &networks_.back();
455 update_all_properties = true;
456 }
457 // If current network is connected, use its properties for this network.
458 if (base::SysNSStringToUTF8([cw_network bssid]) == connected_bssid)
459 update_all_properties = true;
460
461 NetworkProperties* properties = network_properties_map.at(network_guid);
462 if (update_all_properties) {
463 NetworkPropertiesFromCWNetwork(cw_network, properties);
464 } else {
465 properties->frequency_set.insert(FrequencyFromCWChannelBand(
466 [[cw_network wlanChannel] channelBand]));
467 }
468 }
469 // Sort networks, so connected/connecting is up front.
470 networks_.sort(NetworkProperties::OrderByType);
471 // Notify observers that list has changed.
472 NotifyNetworkListChanged(networks_);
473 }
474
475 bool WiFiServiceMac::CheckError(NSError* ns_error,
476 const char* error_name,
477 std::string* error) const {
478 if (ns_error != nil) {
479 DLOG(ERROR) << "*** Error:" << error_name << ":" << [ns_error code];
480 *error = error_name;
481 return true;
482 }
483 return false;
484 }
485
486 void WiFiServiceMac::NetworkPropertiesFromCWNetwork(
487 const CWNetwork* network,
488 NetworkProperties* properties) const {
489 std::string network_guid = GUIDFromSSID([network ssid]);
490
491 properties->connection_state = GetNetworkConnectionState(network_guid);
492 properties->ssid = base::SysNSStringToUTF8([network ssid]);
493 properties->name = properties->ssid;
494 properties->guid = network_guid;
495 properties->type = onc::network_type::kWiFi;
496
497 properties->bssid = base::SysNSStringToUTF8([network bssid]);
498 properties->frequency = FrequencyFromCWChannelBand(
499 static_cast<CWChannelBand>([[network wlanChannel] channelBand]));
500 properties->frequency_set.insert(properties->frequency);
501 properties->security = SecurityFromCWSecurityMode(
502 static_cast<CWSecurityMode>([[network securityMode] intValue]));
503
504 properties->signal_strength = [[network rssi] intValue];
505 }
506
507 std::string WiFiServiceMac::SecurityFromCWSecurityMode(
508 CWSecurityMode security) const {
509 switch (security) {
510 case kCWSecurityModeWPA_Enterprise:
511 case kCWSecurityModeWPA2_Enterprise:
512 return onc::wifi::kWPA_EAP;
513 case kCWSecurityModeWPA_PSK:
514 case kCWSecurityModeWPA2_PSK:
515 return onc::wifi::kWPA_PSK;
516 case kCWSecurityModeWEP:
517 return onc::wifi::kWEP_PSK;
518 case kCWSecurityModeOpen:
519 return onc::wifi::kNone;
520 // TODO(mef): Figure out correct mapping.
521 case kCWSecurityModeWPS:
522 case kCWSecurityModeDynamicWEP:
523 return onc::wifi::kWPA_EAP;
524 }
525 return onc::wifi::kWPA_EAP;
526 }
527
528
529 WiFiService::Frequency WiFiServiceMac::FrequencyFromCWChannelBand(
530 CWChannelBand band) const {
531 return band == kCWChannelBand2GHz ? kFrequency2400 : kFrequency5000;
532 }
533
534 WiFiService::NetworkList::iterator WiFiServiceMac::FindNetwork(
535 const std::string& network_guid) {
536 for (NetworkList::iterator it = networks_.begin();
537 it != networks_.end();
538 ++it) {
539 if (it->guid == network_guid)
540 return it;
541 }
542 return networks_.end();
543 }
544
545 void WiFiServiceMac::OnWlanObserverNotification() {
546 std::string connected_network_guid = GUIDFromSSID([interface_ ssid]);
547 DVLOG(1) << " *** Got Notification: " << connected_network_guid;
548 // Connected network has changed, mark previous one disconnected.
549 if (connected_network_guid != connected_network_guid_) {
550 // Update connection_state of newly connected network.
551 NetworkList::iterator it = FindNetwork(connected_network_guid_);
552 if (it != networks_.end()) {
553 it->connection_state = onc::connection_state::kNotConnected;
554 NotifyNetworkChanged(connected_network_guid_);
555 }
556 connected_network_guid_ = connected_network_guid;
557 }
558
559 if (!connected_network_guid.empty()) {
560 // Update connection_state of newly connected network.
561 NetworkList::iterator it = FindNetwork(connected_network_guid);
562 if (it != networks_.end()) {
563 it->connection_state = GetNetworkConnectionState(connected_network_guid);
564 } else {
565 // Can't find |connected_network_guid| in |networks_|, try to update it.
566 UpdateNetworks();
567 }
568 // Notify that network is connecting.
569 NotifyNetworkChanged(connected_network_guid);
570 // Further network change notification will be sent by detector.
571 }
572 }
573
574 void WiFiServiceMac::NotifyNetworkListChanged(const NetworkList& networks) {
575 if (network_list_changed_observer_.is_null())
576 return;
577
578 NetworkGuidList current_networks;
579 for (NetworkList::const_iterator it = networks.begin();
580 it != networks.end();
581 ++it) {
582 current_networks.push_back(it->guid);
583 }
584
585 message_loop_proxy_->PostTask(
586 FROM_HERE,
587 base::Bind(network_list_changed_observer_, current_networks));
588 }
589
590 void WiFiServiceMac::NotifyNetworkChanged(const std::string& network_guid) {
591 if (networks_changed_observer_.is_null())
592 return;
593
594 DVLOG(1) << "NotifyNetworkChanged: " << network_guid;
595 NetworkGuidList changed_networks(1, network_guid);
596 message_loop_proxy_->PostTask(
597 FROM_HERE,
598 base::Bind(networks_changed_observer_, changed_networks));
599 }
600
601 // static
602 WiFiService* WiFiService::Create() { return new WiFiServiceMac(); }
603
604 } // namespace wifi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698