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 "chrome/browser/local_discovery/wifi/bootstrapping_device_lister.h"
      6 
      7 #include <algorithm>
      8 #include <iterator>
      9 
     10 #include "base/bind.h"
     11 #include "base/location.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/strings/string_util.h"
     14 
     15 namespace local_discovery {
     16 
     17 namespace wifi {
     18 
     19 namespace {
     20 
     21 const char kPrivetSuffix[] = "prv";
     22 // 3 for prv, 3 for type, one for connection status.
     23 const size_t kPrivetCharactersAfterSeparator = 7;
     24 
     25 const struct {
     26   const char* const short_name;
     27   const char* const long_name;
     28 } kPrivetShortNames[] = {{"cam", "camera"}, {"pri", "printer"}};
     29 
     30 const struct {
     31   char signifier;
     32   BootstrappingDeviceDescription::ConnectionStatus status;
     33 } kPrivetConnectionStatuses[] = {
     34       {'C', BootstrappingDeviceDescription::CONNECTING},
     35       {'F', BootstrappingDeviceDescription::OFFLINE},
     36       {'L', BootstrappingDeviceDescription::LOCAL_ONLY},
     37       {'N', BootstrappingDeviceDescription::NOT_CONFIGURED},
     38       {'O', BootstrappingDeviceDescription::ONLINE},
     39 };
     40 
     41 const char kPrivetDeviceLongName[] = "device";
     42 
     43 std::string ExpandDeviceKind(const std::string& device_kind_short) {
     44   for (size_t i = 0; i < arraysize(kPrivetShortNames); i++) {
     45     if (device_kind_short == kPrivetShortNames[i].short_name) {
     46       return kPrivetShortNames[i].long_name;
     47     }
     48   }
     49   return kPrivetDeviceLongName;
     50 }
     51 
     52 BootstrappingDeviceDescription::ConnectionStatus GetConnectionStatus(
     53     char signifier) {
     54   for (size_t i = 0; i < arraysize(kPrivetConnectionStatuses); i++) {
     55     if (signifier == kPrivetConnectionStatuses[i].signifier) {
     56       return kPrivetConnectionStatuses[i].status;
     57     }
     58   }
     59 
     60   LOG(WARNING) << "Unknown WiFi connection state signifier " << signifier;
     61   return BootstrappingDeviceDescription::NOT_CONFIGURED;
     62 }
     63 
     64 // Return true if the SSID is a privet ssid and fills |description| with its
     65 // attributes.
     66 bool ParsePrivetSSID(const std::string& internal_id,
     67                      const std::string& ssid,
     68                      BootstrappingDeviceDescription* description) {
     69   if (!EndsWith(ssid, kPrivetSuffix, true))
     70     return false;
     71 
     72   size_t at_pos = ssid.length() - kPrivetCharactersAfterSeparator - 1;
     73 
     74   if (ssid[at_pos] != '@' || ssid.find('@', at_pos + 1) != std::string::npos)
     75     return false;
     76 
     77   description->device_network_id = internal_id;
     78   description->device_ssid = ssid;
     79 
     80   description->device_name = ssid.substr(0, at_pos);
     81   description->device_kind = ExpandDeviceKind(ssid.substr(at_pos + 1, 3));
     82   description->connection_status = GetConnectionStatus(ssid.at(at_pos + 4));
     83 
     84   return true;
     85 }
     86 
     87 }  // namespace
     88 
     89 BootstrappingDeviceDescription::BootstrappingDeviceDescription()
     90     : connection_status(NOT_CONFIGURED) {
     91 }
     92 
     93 BootstrappingDeviceDescription::~BootstrappingDeviceDescription() {
     94 }
     95 
     96 BootstrappingDeviceLister::BootstrappingDeviceLister(
     97     WifiManager* wifi_manager,
     98     const UpdateCallback& update_callback)
     99     : wifi_manager_(wifi_manager),
    100       update_callback_(update_callback),
    101       started_(false),
    102       weak_factory_(this) {
    103 }
    104 
    105 BootstrappingDeviceLister::~BootstrappingDeviceLister() {
    106   if (started_)
    107     wifi_manager_->RemoveNetworkListObserver(this);
    108 }
    109 
    110 void BootstrappingDeviceLister::Start() {
    111   DCHECK(!started_);
    112 
    113   started_ = true;
    114 
    115   wifi_manager_->AddNetworkListObserver(this);
    116 
    117   wifi_manager_->GetSSIDList(
    118       base::Bind(&BootstrappingDeviceLister::OnNetworkListChanged,
    119                  weak_factory_.GetWeakPtr()));
    120 }
    121 
    122 void BootstrappingDeviceLister::OnNetworkListChanged(
    123     const std::vector<NetworkProperties>& ssids) {
    124   ActiveDeviceList new_devices;
    125 
    126   for (size_t i = 0; i < ssids.size(); i++) {
    127     new_devices.push_back(make_pair(ssids[i].ssid, ssids[i].guid));
    128   }
    129 
    130   std::sort(new_devices.begin(), new_devices.end());
    131 
    132   base::WeakPtr<BootstrappingDeviceLister> weak_this =
    133       weak_factory_.GetWeakPtr();
    134   // Find new or changed SSIDs
    135   UpdateChangedSSIDs(true, new_devices, active_devices_);
    136   if (!weak_this)
    137     return;
    138 
    139   // Find removed SSIDs
    140   UpdateChangedSSIDs(false, active_devices_, new_devices);
    141   if (!weak_this)
    142     return;
    143 
    144   active_devices_.swap(new_devices);
    145 }
    146 
    147 void BootstrappingDeviceLister::UpdateChangedSSIDs(
    148     bool available,
    149     const ActiveDeviceList& changed,
    150     const ActiveDeviceList& original) {
    151   base::WeakPtr<BootstrappingDeviceLister> weak_this =
    152       weak_factory_.GetWeakPtr();
    153 
    154   ActiveDeviceList changed_devices;
    155   std::set_difference(changed.begin(),
    156                       changed.end(),
    157                       original.begin(),
    158                       original.end(),
    159                       std::back_inserter(changed_devices));
    160 
    161   for (ActiveDeviceList::iterator i = changed_devices.begin();
    162        weak_this && i != changed_devices.end();
    163        i++) {
    164     BootstrappingDeviceDescription description;
    165     if (ParsePrivetSSID(i->second, i->first, &description)) {
    166       update_callback_.Run(available, description);
    167     }
    168   }
    169 }
    170 
    171 }  // namespace wifi
    172 
    173 }  // namespace local_discovery
    174