bootstrapping_device_lister.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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
15namespace local_discovery {
16
17namespace wifi {
18
19namespace {
20
21const char kPrivetSuffix[] = "prv";
22// 3 for prv, 3 for type, one for connection status.
23const size_t kPrivetCharactersAfterSeparator = 7;
24
25const struct {
26  const char* const short_name;
27  const char* const long_name;
28} kPrivetShortNames[] = {{"cam", "camera"}, {"pri", "printer"}};
29
30const 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
41const char kPrivetDeviceLongName[] = "device";
42
43std::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
52BootstrappingDeviceDescription::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.
66bool 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
89BootstrappingDeviceDescription::BootstrappingDeviceDescription()
90    : connection_status(NOT_CONFIGURED) {
91}
92
93BootstrappingDeviceDescription::~BootstrappingDeviceDescription() {
94}
95
96BootstrappingDeviceLister::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
105BootstrappingDeviceLister::~BootstrappingDeviceLister() {
106  if (started_)
107    wifi_manager_->RemoveNetworkListObserver(this);
108}
109
110void 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
122void 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
147void 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