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