blob: c8396e72597a82ea78a399e3ab4e3cc9ab186a21 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/local_discovery/wifi/bootstrapping_device_lister.h"
#include <algorithm>
#include <iterator>
#include "base/bind.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
namespace local_discovery {
namespace wifi {
namespace {
const char kPrivetSuffix[] = "prv";
// 3 for prv, 3 for type, one for connection status.
const size_t kPrivetCharactersAfterSeparator = 7;
const struct {
const char* const short_name;
const char* const long_name;
} kPrivetShortNames[] = {{"cam", "camera"}, {"pri", "printer"}};
const struct {
char signifier;
BootstrappingDeviceDescription::ConnectionStatus status;
} kPrivetConnectionStatuses[] = {
{'C', BootstrappingDeviceDescription::CONNECTING},
{'F', BootstrappingDeviceDescription::OFFLINE},
{'L', BootstrappingDeviceDescription::LOCAL_ONLY},
{'N', BootstrappingDeviceDescription::NOT_CONFIGURED},
{'O', BootstrappingDeviceDescription::ONLINE},
};
const char kPrivetDeviceLongName[] = "device";
std::string ExpandDeviceKind(const std::string& device_kind_short) {
for (size_t i = 0; i < arraysize(kPrivetShortNames); i++) {
if (device_kind_short == kPrivetShortNames[i].short_name) {
return kPrivetShortNames[i].long_name;
}
}
return kPrivetDeviceLongName;
}
BootstrappingDeviceDescription::ConnectionStatus GetConnectionStatus(
char signifier) {
for (size_t i = 0; i < arraysize(kPrivetConnectionStatuses); i++) {
if (signifier == kPrivetConnectionStatuses[i].signifier) {
return kPrivetConnectionStatuses[i].status;
}
}
LOG(WARNING) << "Unknown WiFi connection state signifier " << signifier;
return BootstrappingDeviceDescription::NOT_CONFIGURED;
}
// Return true if the SSID is a privet ssid and fills |description| with its
// attributes.
bool ParsePrivetSSID(const std::string& internal_id,
const std::string& ssid,
BootstrappingDeviceDescription* description) {
if (!EndsWith(ssid, kPrivetSuffix, true))
return false;
size_t at_pos = ssid.length() - kPrivetCharactersAfterSeparator - 1;
if (ssid[at_pos] != '@' || ssid.find('@', at_pos + 1) != std::string::npos)
return false;
description->device_network_id = internal_id;
description->device_ssid = ssid;
description->device_name = ssid.substr(0, at_pos);
description->device_kind = ExpandDeviceKind(ssid.substr(at_pos + 1, 3));
description->connection_status = GetConnectionStatus(ssid.at(at_pos + 4));
return true;
}
} // namespace
BootstrappingDeviceDescription::BootstrappingDeviceDescription()
: connection_status(NOT_CONFIGURED) {
}
BootstrappingDeviceDescription::~BootstrappingDeviceDescription() {
}
BootstrappingDeviceLister::BootstrappingDeviceLister(
WifiManager* wifi_manager,
const UpdateCallback& update_callback)
: wifi_manager_(wifi_manager),
update_callback_(update_callback),
started_(false),
weak_factory_(this) {
}
BootstrappingDeviceLister::~BootstrappingDeviceLister() {
if (started_)
wifi_manager_->RemoveNetworkListObserver(this);
}
void BootstrappingDeviceLister::Start() {
DCHECK(!started_);
started_ = true;
wifi_manager_->AddNetworkListObserver(this);
wifi_manager_->GetSSIDList(
base::Bind(&BootstrappingDeviceLister::OnNetworkListChanged,
weak_factory_.GetWeakPtr()));
}
void BootstrappingDeviceLister::OnNetworkListChanged(
const std::vector<NetworkProperties>& ssids) {
ActiveDeviceList new_devices;
for (size_t i = 0; i < ssids.size(); i++) {
new_devices.push_back(make_pair(ssids[i].ssid, ssids[i].guid));
}
std::sort(new_devices.begin(), new_devices.end());
base::WeakPtr<BootstrappingDeviceLister> weak_this =
weak_factory_.GetWeakPtr();
// Find new or changed SSIDs
UpdateChangedSSIDs(true, new_devices, active_devices_);
if (!weak_this)
return;
// Find removed SSIDs
UpdateChangedSSIDs(false, active_devices_, new_devices);
if (!weak_this)
return;
active_devices_.swap(new_devices);
}
void BootstrappingDeviceLister::UpdateChangedSSIDs(
bool available,
const ActiveDeviceList& changed,
const ActiveDeviceList& original) {
base::WeakPtr<BootstrappingDeviceLister> weak_this =
weak_factory_.GetWeakPtr();
ActiveDeviceList changed_devices =
base::STLSetDifference<ActiveDeviceList>(changed, original);
for (ActiveDeviceList::reverse_iterator i = changed_devices.rbegin();
weak_this && i != changed_devices.rend();
i++) {
BootstrappingDeviceDescription description;
if (ParsePrivetSSID(i->second, i->first, &description)) {
update_callback_.Run(available, description);
}
}
}
} // namespace wifi
} // namespace local_discovery