Commit 8de460ed authored by noamsml@chromium.org's avatar noamsml@chromium.org

Bootstrapping device lister plus tests

This class lists bootstrapping WiFi devices and provides the necessary information to display/connect to them.

Depends on:
https://codereview.chromium.org/294173002/ [build variable for WiFi bootstrapping]
https://codereview.chromium.org/226883002/ [WiFi library for cloud devices]

BUG=370071

Review URL: https://codereview.chromium.org/288043004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274896 0039d316-1c4b-4281-b951-d872f2087c98
parent 63ada084
// 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/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;
std::set_difference(changed.begin(),
changed.end(),
original.begin(),
original.end(),
std::back_inserter(changed_devices));
for (ActiveDeviceList::iterator i = changed_devices.begin();
weak_this && i != changed_devices.end();
i++) {
BootstrappingDeviceDescription description;
if (ParsePrivetSSID(i->second, i->first, &description)) {
update_callback_.Run(available, description);
}
}
}
} // namespace wifi
} // namespace local_discovery
// 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.
#ifndef CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_BOOTSTRAPPING_DEVICE_LISTER_H_
#define CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_BOOTSTRAPPING_DEVICE_LISTER_H_
#include <vector>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/local_discovery/wifi/wifi_manager.h"
namespace local_discovery {
namespace wifi {
struct BootstrappingDeviceDescription {
enum ConnectionStatus {
ONLINE,
OFFLINE,
CONNECTING,
NOT_CONFIGURED,
LOCAL_ONLY
};
BootstrappingDeviceDescription();
~BootstrappingDeviceDescription();
std::string device_network_id;
std::string device_ssid;
std::string device_name;
std::string device_kind;
ConnectionStatus connection_status;
};
class BootstrappingDeviceLister : public NetworkListObserver {
public:
typedef base::Callback<
void(bool available, const BootstrappingDeviceDescription& description)>
UpdateCallback;
BootstrappingDeviceLister(WifiManager* wifi_manager,
const UpdateCallback& update_callback);
virtual ~BootstrappingDeviceLister();
void Start();
private:
typedef std::vector<
std::pair<std::string /*ssid*/, std::string /*internal_name*/> >
ActiveDeviceList;
virtual void OnNetworkListChanged(
const std::vector<NetworkProperties>& ssids) OVERRIDE;
void UpdateChangedSSIDs(bool available,
const ActiveDeviceList& changed,
const ActiveDeviceList& original);
WifiManager* wifi_manager_;
UpdateCallback update_callback_;
bool started_;
ActiveDeviceList active_devices_;
base::WeakPtrFactory<BootstrappingDeviceLister> weak_factory_;
};
} // namespace wifi
} // namespace local_discovery
#endif // CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_BOOTSTRAPPING_DEVICE_LISTER_H_
// 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 "base/bind.h"
#include "chrome/browser/local_discovery/wifi/bootstrapping_device_lister.h"
#include "chrome/browser/local_discovery/wifi/mock_wifi_manager.h"
#include "components/onc/onc_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::StrictMock;
using testing::Mock;
namespace local_discovery {
namespace wifi {
namespace {
class MockableUpdateCallback {
public:
void UpdateCallback(bool available,
const BootstrappingDeviceDescription& description) {
UpdateCallbackInternal(available,
description.device_network_id,
description.device_ssid,
description.device_name,
description.device_kind,
description.connection_status);
}
MOCK_METHOD6(UpdateCallbackInternal,
void(bool available,
const std::string& network_id,
const std::string& ssid,
const std::string& name,
const std::string& kind,
BootstrappingDeviceDescription::ConnectionStatus status));
BootstrappingDeviceLister::UpdateCallback callback() {
return base::Bind(&MockableUpdateCallback::UpdateCallback,
base::Unretained(this));
}
};
class BootstrappingDeviceListerTest : public ::testing::Test {
public:
BootstrappingDeviceListerTest()
: lister_(&mock_wifi_manager_, mockable_callback_.callback()) {}
~BootstrappingDeviceListerTest() {}
StrictMock<MockableUpdateCallback> mockable_callback_;
StrictMock<MockWifiManager> mock_wifi_manager_;
BootstrappingDeviceLister lister_;
};
TEST_F(BootstrappingDeviceListerTest, ListSingleDevice) {
EXPECT_CALL(mock_wifi_manager_, GetSSIDListInternal());
lister_.Start();
Mock::VerifyAndClearExpectations(&mock_wifi_manager_);
std::vector<NetworkProperties> network_property_list;
NetworkProperties network;
network.guid = "MyInternalID";
network.ssid = "MyDevice@camNprv";
network.connection_state = onc::connection_state::kNotConnected;
network_property_list.push_back(network);
NetworkProperties network2;
network2.guid = "MyInternalID2";
network2.ssid = "SomeRandomNetwork";
network2.connection_state = onc::connection_state::kNotConnected;
network_property_list.push_back(network2);
EXPECT_CALL(
mockable_callback_,
UpdateCallbackInternal(true,
"MyInternalID",
"MyDevice@camNprv",
"MyDevice",
"camera",
BootstrappingDeviceDescription::NOT_CONFIGURED));
mock_wifi_manager_.CallSSIDListCallback(network_property_list);
}
TEST_F(BootstrappingDeviceListerTest, AddRemoveDevice) {
EXPECT_CALL(mock_wifi_manager_, GetSSIDListInternal());
lister_.Start();
Mock::VerifyAndClearExpectations(&mock_wifi_manager_);
std::vector<NetworkProperties> network_property_list;
NetworkProperties network;
network.guid = "MyInternalID";
network.ssid = "MyDevice@camNprv";
network.connection_state = onc::connection_state::kNotConnected;
network_property_list.push_back(network);
std::vector<NetworkProperties> network_property_list2;
NetworkProperties network2;
network2.guid = "MyInternalID2";
network2.ssid = "MyDevice2@priFprv";
network2.connection_state = onc::connection_state::kNotConnected;
network_property_list2.push_back(network2);
EXPECT_CALL(
mockable_callback_,
UpdateCallbackInternal(true,
"MyInternalID",
"MyDevice@camNprv",
"MyDevice",
"camera",
BootstrappingDeviceDescription::NOT_CONFIGURED));
mock_wifi_manager_.CallSSIDListCallback(network_property_list);
Mock::VerifyAndClearExpectations(&mock_wifi_manager_);
EXPECT_CALL(
mockable_callback_,
UpdateCallbackInternal(false,
"MyInternalID",
"MyDevice@camNprv",
"MyDevice",
"camera",
BootstrappingDeviceDescription::NOT_CONFIGURED));
EXPECT_CALL(mockable_callback_,
UpdateCallbackInternal(true,
"MyInternalID2",
"MyDevice2@priFprv",
"MyDevice2",
"printer",
BootstrappingDeviceDescription::OFFLINE));
mock_wifi_manager_.CallNetworkListObservers(network_property_list2);
}
TEST_F(BootstrappingDeviceListerTest, EdgeCases) {
EXPECT_CALL(mock_wifi_manager_, GetSSIDListInternal());
lister_.Start();
Mock::VerifyAndClearExpectations(&mock_wifi_manager_);
std::vector<NetworkProperties> network_property_list;
NetworkProperties network;
network.guid = "MyInternalID";
network.ssid = "MyDevice@camprv";
network.connection_state = onc::connection_state::kNotConnected;
NetworkProperties network2;
network2.guid = "MyInternalID2";
network2.ssid = "MyDevice2@unkNprv";
network2.connection_state = onc::connection_state::kNotConnected;
NetworkProperties network3;
network3.guid = "MyInternalID3";
network3.ssid = "MyDevice3camNprv";
network3.connection_state = onc::connection_state::kNotConnected;
NetworkProperties network4;
network4.guid = "MyInternalID4";
network4.ssid = "MyDevice4@camNnpr";
network4.connection_state = onc::connection_state::kNotConnected;
NetworkProperties network5;
network5.guid = "MyInternalID5";
network5.ssid = "MyDevice5@With@At@Signs@camOprv";
network5.connection_state = onc::connection_state::kNotConnected;
network_property_list.push_back(network);
network_property_list.push_back(network2);
network_property_list.push_back(network3);
network_property_list.push_back(network4);
network_property_list.push_back(network5);
EXPECT_CALL(
mockable_callback_,
UpdateCallbackInternal(true,
"MyInternalID2",
"MyDevice2@unkNprv",
"MyDevice2",
"device",
BootstrappingDeviceDescription::NOT_CONFIGURED));
EXPECT_CALL(mockable_callback_,
UpdateCallbackInternal(true,
"MyInternalID5",
"MyDevice5@With@At@Signs@camOprv",
"MyDevice5@With@At@Signs",
"camera",
BootstrappingDeviceDescription::ONLINE));
mock_wifi_manager_.CallSSIDListCallback(network_property_list);
}
} // namespace
} // namespace wifi
} // namespace local_discovery
// 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/mock_wifi_manager.h"
namespace local_discovery {
namespace wifi {
MockWifiManager::MockWifiManager() : weak_factory_(this) {
}
MockWifiManager::~MockWifiManager() {
}
void MockWifiManager::GetSSIDList(const SSIDListCallback& callback) {
ssid_list_callback_ = callback;
GetSSIDListInternal();
}
void MockWifiManager::CallSSIDListCallback(
const std::vector<NetworkProperties>& networks) {
ssid_list_callback_.Run(networks);
}
void MockWifiManager::ConfigureAndConnectNetwork(
const std::string& ssid,
const WifiCredentials& credentials,
const SuccessCallback& callback) {
configure_and_connect_network_callback_ = callback;
ConfigureAndConnectNetworkInternal(ssid, credentials.psk);
}
void MockWifiManager::CallConfigureAndConnectNetworkCallback(bool success) {
configure_and_connect_network_callback_.Run(success);
}
void MockWifiManager::ConnectToNetworkByID(const std::string& internal_id,
const SuccessCallback& callback) {
connect_by_id_callback_ = callback;
ConnectToNetworkByIDInternal(internal_id);
}
void MockWifiManager::CallConnectToNetworkByIDCallback(bool success) {
connect_by_id_callback_.Run(success);
}
void MockWifiManager::RequestNetworkCredentials(
const std::string& internal_id,
const CredentialsCallback& callback) {
credentials_callback_ = callback;
RequestNetworkCredentialsInternal(internal_id);
}
void MockWifiManager::CallRequestNetworkCredentialsCallback(
bool success,
const std::string& ssid,
const std::string& password) {
credentials_callback_.Run(success, ssid, password);
}
void MockWifiManager::CallNetworkListObservers(
const std::vector<NetworkProperties>& ssids) {
FOR_EACH_OBSERVER(
NetworkListObserver, network_observers_, OnNetworkListChanged(ssids));
}
void MockWifiManager::AddNetworkListObserver(NetworkListObserver* observer) {
network_observers_.AddObserver(observer);
}
void MockWifiManager::RemoveNetworkListObserver(NetworkListObserver* observer) {
network_observers_.RemoveObserver(observer);
}
MockWifiManagerFactory::MockWifiManagerFactory() {
WifiManager::SetFactory(this);
}
MockWifiManagerFactory::~MockWifiManagerFactory() {
WifiManager::SetFactory(NULL);
}
scoped_ptr<WifiManager> MockWifiManagerFactory::CreateWifiManager() {
last_created_manager_ = new MockWifiManager();
return scoped_ptr<WifiManager>(last_created_manager_);
}
} // namespace wifi
} // namespace local_discovery
// 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.
#ifndef CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_MOCK_WIFI_MANAGER_H_
#define CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_MOCK_WIFI_MANAGER_H_
#include "base/observer_list.h"
#include "chrome/browser/local_discovery/wifi/wifi_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace local_discovery {
namespace wifi {
class MockWifiManager : public WifiManager {
public:
MockWifiManager();
~MockWifiManager();
MOCK_METHOD0(Start, void());
virtual void GetSSIDList(const SSIDListCallback& callback) OVERRIDE;
MOCK_METHOD0(GetSSIDListInternal, void());
void CallSSIDListCallback(const std::vector<NetworkProperties>& networks);
MOCK_METHOD0(RequestScan, void());
virtual void ConfigureAndConnectNetwork(
const std::string& ssid,
const WifiCredentials& credentials,
const SuccessCallback& callback) OVERRIDE;
MOCK_METHOD2(ConfigureAndConnectNetworkInternal,
void(const std::string& ssid, const std::string& password));
void CallConfigureAndConnectNetworkCallback(bool success);
virtual void ConnectToNetworkByID(const std::string& internal_id,
const SuccessCallback& callback) OVERRIDE;
MOCK_METHOD1(ConnectToNetworkByIDInternal,
void(const std::string& internal_id));
void CallConnectToNetworkByIDCallback(bool success);
virtual void RequestNetworkCredentials(
const std::string& internal_id,
const CredentialsCallback& callback) OVERRIDE;
MOCK_METHOD1(RequestNetworkCredentialsInternal,
void(const std::string& internal_id));
void CallRequestNetworkCredentialsCallback(bool success,
const std::string& ssid,
const std::string& password);
void AddNetworkListObserver(NetworkListObserver* observer);
void RemoveNetworkListObserver(NetworkListObserver* observer);
void CallNetworkListObservers(const std::vector<NetworkProperties>& ssids);
private:
SSIDListCallback ssid_list_callback_;
SuccessCallback configure_and_connect_network_callback_;
SuccessCallback connect_by_id_callback_;
CredentialsCallback credentials_callback_;
ObserverList<NetworkListObserver> network_observers_;
base::WeakPtrFactory<MockWifiManager> weak_factory_;
};
class MockWifiManagerFactory : public WifiManagerFactory {
public:
MockWifiManagerFactory();
virtual ~MockWifiManagerFactory();
virtual scoped_ptr<WifiManager> CreateWifiManager() OVERRIDE;
MockWifiManager* GetLastCreatedWifiManager();
private:
MockWifiManager* last_created_manager_;
};
} // namespace wifi
} // namespace local_discovery
#endif // CHROME_BROWSER_LOCAL_DISCOVERY_WIFI_MOCK_WIFI_MANAGER_H_
......@@ -8,6 +8,24 @@ namespace local_discovery {
namespace wifi {
namespace {
WifiManagerFactory* g_factory = NULL;
} // namespace
scoped_ptr<WifiManager> WifiManager::Create() {
if (g_factory) {
return g_factory->CreateWifiManager();
}
return CreateDefault();
}
void WifiManager::SetFactory(WifiManagerFactory* factory) {
g_factory = factory;
}
WifiCredentials WifiCredentials::FromPSK(const std::string& psk) {
WifiCredentials return_value;
return_value.psk = psk;
......
......@@ -30,6 +30,8 @@ struct WifiCredentials {
std::string psk;
};
class WifiManagerFactory;
// Observer for the network list. Classes may implement this interface and call
// |AddNetworkListObserver| to be notified of changes to the visible network
// list.
......@@ -55,6 +57,8 @@ class WifiManager {
static scoped_ptr<WifiManager> Create();
static void SetFactory(WifiManagerFactory* factory);
// Start the wifi manager. This must be called before any other method calls.
virtual void Start() = 0;
......@@ -91,6 +95,16 @@ class WifiManager {
// Remove a network list observer.
virtual void RemoveNetworkListObserver(NetworkListObserver* observer) = 0;
private:
static scoped_ptr<WifiManager> CreateDefault();
};
class WifiManagerFactory {
public:
virtual ~WifiManagerFactory() {}
virtual scoped_ptr<WifiManager> CreateWifiManager() = 0;
};
} // namespace wifi
......
......@@ -413,7 +413,7 @@ void WifiManagerNonChromeos::WifiServiceWrapper::PostClosure(
base::Bind(&WifiManagerNonChromeos::PostClosure, wifi_manager_, closure));
}
scoped_ptr<WifiManager> WifiManager::Create() {
scoped_ptr<WifiManager> WifiManager::CreateDefault() {
return scoped_ptr<WifiManager>(new WifiManagerNonChromeos());
}
......
......@@ -3526,6 +3526,8 @@
}],
[ 'enable_wifi_bootstrapping==1', {
'sources': [
'browser/local_discovery/wifi/bootstrapping_device_lister.cc',
'browser/local_discovery/wifi/bootstrapping_device_lister.h',
'browser/local_discovery/wifi/wifi_manager.cc',
'browser/local_discovery/wifi/wifi_manager.h',
],
......
......@@ -432,6 +432,12 @@
'renderer/media/mock_webrtc_logging_message_filter.h',
],
}],
['enable_wifi_bootstrapping', {
"sources" : [
'browser/local_discovery/wifi/mock_wifi_manager.cc',
'browser/local_discovery/wifi/mock_wifi_manager.h',
]
}]
],
},
{
......@@ -2739,6 +2745,11 @@
'browser/plugins/plugin_installer_unittest.cc',
],
}],
['enable_wifi_bootstrapping', {
'sources' : [
'browser/local_discovery/wifi/bootstrapping_device_lister_unittest.cc',
]
}],
],
},
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment