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

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: Observe OnNetworkChanged in NPServiceClient and post them to WiFiService. 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 OnNetworkChangeNotification() 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 // Gets current |onc::connection_state| for given |network_guid|.
132 std::string GetNetworkConnectionState(const std::string& network_guid) const;
133
134 // Updates |networks_| with the list of visible wireless networks.
135 void UpdateNetworks();
136
137 // Find network by |network_guid| and return iterator to its entry in
138 // |networks_|.
139 NetworkList::iterator FindNetwork(const std::string& network_guid);
140
141 // Handles notification from |wlan_observer_|.
142 void OnWlanObserverNotification();
143
144 // Notifies |network_list_changed_observer_| that list of visible networks has
145 // changed to |networks|.
146 void NotifyNetworkListChanged(const NetworkList& networks);
147
148 // Notifies |networks_changed_observer_| that network |network_guid|
149 // connection state has changed.
150 void NotifyNetworkChanged(const std::string& network_guid);
151
152 // Default interface.
153 base::scoped_nsobject<CWInterface> interface_;
154 // WLAN Notifications observer. |this| doesn't own this reference.
155 id wlan_observer_;
156
157 // Observer to get notified when network(s) have changed (e.g. connect).
158 NetworkGuidListCallback networks_changed_observer_;
159 // Observer to get notified when network list has changed (scan complete).
tbarzic 2014/01/28 02:05:03 I'd drop "scan complete" part
mef 2014/01/28 14:39:36 Done.
160 NetworkGuidListCallback network_list_changed_observer_;
161 // MessageLoopProxy to post events on UI thread.
tbarzic 2014/01/28 02:05:03 MessageLoopProxy to which events should be posted.
mef 2014/01/28 14:39:36 Done.
162 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
163 // Task runner for worker tasks.
164 scoped_refptr<base::SequencedTaskRunner> task_runner_;
165 // Cache of network list collected by GetVisibleNetworks.
tbarzic 2014/01/28 02:05:03 Cached list of visible networks. Updated by |Updat
mef 2014/01/28 14:39:36 Done.
166 NetworkList networks_;
167 // Guid of last known connected network.
168 std::string connected_network_guid_;
169 // Temporary storage of network properties indexed by |network_guid|.
170 base::DictionaryValue network_properties_;
171 // Use WeakPtrs for callbacks from |network_change_detector_|.
172 base::WeakPtrFactory<WiFiServiceMac> weak_factory_;
tbarzic 2014/01/28 02:05:03 not needed anymore
mef 2014/01/28 14:39:36 Done.
173
174 DISALLOW_COPY_AND_ASSIGN(WiFiServiceMac);
175
176 };
177
178 WiFiServiceMac::WiFiServiceMac() : wlan_observer_(nil), weak_factory_(this) {
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 NotifyNetworkChanged(connected_network_guid);
tbarzic 2014/01/28 02:05:03 can this just return now?
mef 2014/01/28 14:39:36 Done.
299 return;
300 }
301
302 NSSet* networks = [interface_
303 scanForNetworksWithName:SSIDFromGUID(network_guid)
304 error:&ns_error];
305
306 if (CheckError(ns_error, kErrorScanForNetworksWithName, error))
307 return;
308
309 CWNetwork* network = [networks anyObject];
310 if (network == nil) {
311 // System can't find the network, remove it from the |networks_| and notify
312 // observers.
313 NetworkList::iterator it = FindNetwork(connected_network_guid);
314 if (it != networks_.end()) {
315 networks_.erase(it);
316 // Notify observers that list has changed.
317 NotifyNetworkListChanged(networks_);
318 }
319
320 *error = kErrorNotFound;
321 return;
322 }
323
324 // Check whether WiFi Password is set in |network_properties_|.
325 base::DictionaryValue* properties;
326 base::DictionaryValue* wifi;
327 std::string passphrase;
328 NSString* ns_password = nil;
329 if (network_properties_.GetDictionaryWithoutPathExpansion(network_guid,
330 &properties) &&
331 properties->GetDictionary(onc::network_type::kWiFi, &wifi) &&
332 wifi->GetString(onc::wifi::kPassphrase, &passphrase)) {
333 ns_password = base::SysUTF8ToNSString(passphrase);
334 }
335
336 // Number of attempts to associate to network.
337 static const int kMaxAssociationAttempts = 3;
338 // Try to associate to network several times if timeout or PMK error occurs.
339 for (int i = 0; i < kMaxAssociationAttempts; ++i) {
340 // Nil out the PMK to prevent stale data from causing invalid PMK error
341 // (CoreWLANTypes -3924).
342 [interface_ setPairwiseMasterKey:nil error:&ns_error];
343 if (![interface_ associateToNetwork:network
344 password:ns_password
345 error:&ns_error]) {
346 NSInteger error_code = [ns_error code];
347 if (error_code != kCWTimeoutErr && error_code != kCWInvalidPMKErr) {
348 break;
349 }
350 }
351 }
352 CheckError(ns_error, kErrorAssociateToNetwork, error);
353 }
354
355 void WiFiServiceMac::StartDisconnect(const std::string& network_guid,
356 std::string* error) {
357 DVLOG(1) << "*** StartDisconnect: " << network_guid;
358
359 if (network_guid == GUIDFromSSID([interface_ ssid])) {
360 // Power-cycle the interface to disconnect from current network and connect
361 // to default network.
362 NSError* ns_error = nil;
363 [interface_ setPower:NO error:&ns_error];
364 CheckError(ns_error, kErrorAssociateToNetwork, error);
365 [interface_ setPower:YES error:&ns_error];
366 CheckError(ns_error, kErrorAssociateToNetwork, error);
367 } else {
368 *error = kErrorNotConnected;
369 }
370 }
371
372 void WiFiServiceMac::SetEventObservers(
373 scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
374 const NetworkGuidListCallback& networks_changed_observer,
375 const NetworkGuidListCallback& network_list_changed_observer) {
376 message_loop_proxy_.swap(message_loop_proxy);
377 networks_changed_observer_ = networks_changed_observer;
378 network_list_changed_observer_ = network_list_changed_observer;
379
380 // Remove previous OS notifications observer.
381 if (wlan_observer_) {
tbarzic 2014/01/28 02:05:03 I'd rather do a CHECK (as calling SetEventObserver
mef 2014/01/28 14:39:36 Ok
382 [[NSNotificationCenter defaultCenter] removeObserver:wlan_observer_];
383 wlan_observer_ = nil;
384 }
385
386 // Subscribe to OS notifications.
387 if (!networks_changed_observer_.is_null()) {
388 void (^ns_observer) (NSNotification* notification) =
389 ^(NSNotification* notification) {
390 DVLOG(1) << "Received CWSSIDDidChangeNotification";
391 task_runner_->PostTask(
392 FROM_HERE,
393 base::Bind(&WiFiServiceMac::OnWlanObserverNotification,
394 base::Unretained(this)));
395 };
396
397 wlan_observer_ = [[NSNotificationCenter defaultCenter]
398 addObserverForName:CWSSIDDidChangeNotification
399 object:nil
400 queue:nil
401 usingBlock:ns_observer];
402 }
403 }
404
405 void WiFiServiceMac::OnNetworkChangeNotification() {
406 OnWlanObserverNotification();
407 }
408
409 std::string WiFiServiceMac::GetNetworkConnectionState(
410 const std::string& network_guid) const {
411 if (network_guid != GUIDFromSSID([interface_ ssid]))
412 return onc::connection_state::kNotConnected;
413
414 // Check whether WiFi network is reachable.
415 struct sockaddr_in local_wifi_address;
416 bzero(&local_wifi_address, sizeof(local_wifi_address));
417 local_wifi_address.sin_len = sizeof(local_wifi_address);
418 local_wifi_address.sin_family = AF_INET;
419 local_wifi_address.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
420 base::ScopedCFTypeRef<SCNetworkReachabilityRef> reachability(
421 SCNetworkReachabilityCreateWithAddress(
422 kCFAllocatorDefault,
423 reinterpret_cast<const struct sockaddr*>(&local_wifi_address)));
424 SCNetworkReachabilityFlags flags = 0u;
425 if (SCNetworkReachabilityGetFlags(reachability, &flags) &&
426 (flags & kSCNetworkReachabilityFlagsReachable) &&
427 (flags & kSCNetworkReachabilityFlagsIsDirect)) {
428 // Network is reachable, report is as |kConnected|.
429 return onc::connection_state::kConnected;
430 }
431 // Network is not reachable yet, so it must be |kConnecting|.
432 return onc::connection_state::kConnecting;
433 }
434
435 void WiFiServiceMac::UpdateNetworks() {
436 NSError* ns_error = nil;
437 NSSet* cw_networks = [interface_ scanForNetworksWithName:nil
438 error:&ns_error];
439
440 if (ns_error != nil)
441 return;
442
443 std::map<std::string, NetworkProperties*> network_properties_map;
444 networks_.clear();
445
446 // There is one |cw_network| per BSS in |cw_networks|, so go through the set
447 // and combine them, paying attention to supported frequencies.
448 for (CWNetwork* cw_network in cw_networks) {
449 NetworkProperties network_properties;
tbarzic 2014/01/28 02:05:03 if the network is connected, you should make sure
mef 2014/01/28 14:39:36 Done.
450 NetworkPropertiesFromCWNetwork(cw_network, &network_properties);
451
452 if (network_properties_map.find(network_properties.guid) ==
tbarzic 2014/01/28 02:05:03 you could check this before NetworkPropertiesFromC
mef 2014/01/28 14:39:36 Done.
453 network_properties_map.end()) {
454 networks_.push_back(network_properties);
455 network_properties_map[network_properties.guid] = &networks_.back();
456 } else {
457 NetworkProperties* existing = network_properties_map.at(
458 network_properties.guid);
459 existing->frequency_set.insert(*network_properties.frequency_set.begin());
460 }
461 }
462 // Sort networks, so connected/connecting is up front.
463 networks_.sort(NetworkProperties::OrderByType);
464 // Notify observers that list has changed.
465 NotifyNetworkListChanged(networks_);
466 }
467
468 bool WiFiServiceMac::CheckError(NSError* ns_error,
469 const char* error_name,
470 std::string* error) const {
471 if (ns_error != nil) {
472 DLOG(ERROR) << "*** Error:" << error_name << ":" << [ns_error code];
473 *error = error_name;
474 return true;
475 }
476 return false;
477 }
478
479 void WiFiServiceMac::NetworkPropertiesFromCWNetwork(
480 const CWNetwork* network,
481 NetworkProperties* properties) const {
482 std::string network_guid = GUIDFromSSID([network ssid]);
483
484 properties->connection_state = GetNetworkConnectionState(network_guid);
485 properties->ssid = base::SysNSStringToUTF8([network ssid]);
486 properties->name = properties->ssid;
487 properties->guid = network_guid;
488 properties->type = onc::network_type::kWiFi;
489
490 properties->bssid = base::SysNSStringToUTF8([network bssid]);
491 if ([[network wlanChannel] channelBand] == kCWChannelBand2GHz)
492 properties->frequency = kFrequency2400;
493 else
494 properties->frequency = kFrequency5000;
495 properties->frequency_set.insert(properties->frequency);
496 properties->security = SecurityFromCWSecurityMode(
497 static_cast<CWSecurityMode>([[network securityMode] intValue]));
498
499 properties->signal_strength = [[network rssi] intValue];
500 }
501
502 std::string WiFiServiceMac::SecurityFromCWSecurityMode(
503 CWSecurityMode security) const {
504 switch (security) {
505 case kCWSecurityModeWPA_Enterprise:
506 case kCWSecurityModeWPA2_Enterprise:
507 return onc::wifi::kWPA_EAP;
508 case kCWSecurityModeWPA_PSK:
509 case kCWSecurityModeWPA2_PSK:
510 return onc::wifi::kWPA_PSK;
511 case kCWSecurityModeWEP:
512 return onc::wifi::kWEP_PSK;
513 case kCWSecurityModeOpen:
514 return onc::wifi::kNone;
515 // TODO(mef): Figure out correct mapping.
516 case kCWSecurityModeWPS:
517 case kCWSecurityModeDynamicWEP:
518 return onc::wifi::kWPA_EAP;
519 }
520 return onc::wifi::kWPA_EAP;
521 }
522
523 WiFiService::NetworkList::iterator WiFiServiceMac::FindNetwork(
524 const std::string& network_guid) {
525 for (NetworkList::iterator it = networks_.begin();
526 it != networks_.end();
527 ++it) {
528 if (it->guid == network_guid)
529 return it;
530 }
531 return networks_.end();
532 }
533
534 void WiFiServiceMac::OnWlanObserverNotification() {
535 std::string connected_network_guid = GUIDFromSSID([interface_ ssid]);
536 DVLOG(1) << " *** Got Notification: " << connected_network_guid;
537 // Connected network has changed, mark previous one disconnected.
538 if (connected_network_guid != connected_network_guid_) {
539 // Update connection_state of newly connected network.
540 NetworkList::iterator it = FindNetwork(connected_network_guid_);
541 if (it != networks_.end()) {
542 it->connection_state = onc::connection_state::kNotConnected;
543 NotifyNetworkChanged(connected_network_guid_);
544 }
545 connected_network_guid_ = connected_network_guid;
546 }
547
548 if (!connected_network_guid.empty()) {
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 = GetNetworkConnectionState(connected_network_guid);
553 } else {
554 // Can't find |connected_network_guid| in |networks_|, try to update it.
555 UpdateNetworks();
556 }
557 // Notify that network is connecting.
558 NotifyNetworkChanged(connected_network_guid);
559 // Further network change notification will be sent by detector.
560 }
561 }
562
563 void WiFiServiceMac::NotifyNetworkListChanged(const NetworkList& networks) {
564 if (network_list_changed_observer_.is_null())
565 return;
566
567 NetworkGuidList current_networks;
568 for (NetworkList::const_iterator it = networks.begin();
569 it != networks.end();
570 ++it) {
571 current_networks.push_back(it->guid);
572 }
573
574 message_loop_proxy_->PostTask(
575 FROM_HERE,
576 base::Bind(network_list_changed_observer_, current_networks));
577 }
578
579 void WiFiServiceMac::NotifyNetworkChanged(const std::string& network_guid) {
580 if (networks_changed_observer_.is_null())
581 return;
582
583 DVLOG(1) << "NotifyNetworkChanged: " << network_guid;
584 NetworkGuidList changed_networks(1, network_guid);
585 message_loop_proxy_->PostTask(
586 FROM_HERE,
587 base::Bind(networks_changed_observer_, changed_networks));
588 }
589
590 // static
591 WiFiService* WiFiService::Create() { return new WiFiServiceMac(); }
592
593 } // namespace wifi
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698