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