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
ExpandDeviceKind(const std::string & device_kind_short)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
GetConnectionStatus(char signifier)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.
ParsePrivetSSID(const std::string & internal_id,const std::string & ssid,BootstrappingDeviceDescription * description)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
BootstrappingDeviceDescription()89 BootstrappingDeviceDescription::BootstrappingDeviceDescription()
90 : connection_status(NOT_CONFIGURED) {
91 }
92
~BootstrappingDeviceDescription()93 BootstrappingDeviceDescription::~BootstrappingDeviceDescription() {
94 }
95
BootstrappingDeviceLister(WifiManager * wifi_manager,const UpdateCallback & update_callback)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
~BootstrappingDeviceLister()105 BootstrappingDeviceLister::~BootstrappingDeviceLister() {
106 if (started_)
107 wifi_manager_->RemoveNetworkListObserver(this);
108 }
109
Start()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
OnNetworkListChanged(const std::vector<NetworkProperties> & ssids)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
UpdateChangedSSIDs(bool available,const ActiveDeviceList & changed,const ActiveDeviceList & original)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