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