From 9841d5dd437471ec8ca05be9809fadad00ea3a3d Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 24 May 2020 19:13:14 +0200 Subject: [PATCH 1/8] Create a Client::Parameters struct to avoid passing IEs IWD doesn't accept or export raw IEs on the DBus interface, instead it takes individual parameter values and only a subset of all the ones that the IEs contain. Make the connman-specific Client class build the IE locally from an abstract parameters struct. For now the struct contains the values that source-app.cpp and sink-app.cpp use, if they need to indicate support for any of the extended WFD capabilities or other parameters, this struct will have to be extended too. This might also be a step in supporting Miracast over Ethernet (MICE?) if ever needed -- I think this is the name used for the R2-specific connection method where parameters are exposed as mDNS records. --- desktop_source/source-app.cpp | 22 ++++++--------------- p2p/connman-client.cpp | 37 ++++++++++++++++++++++++++++++----- p2p/connman-client.h | 12 ++++++++++-- p2p/main.cpp | 19 +++++------------- sink/sink-app.cpp | 23 +++++----------------- 5 files changed, 58 insertions(+), 55 deletions(-) diff --git a/desktop_source/source-app.cpp b/desktop_source/source-app.cpp index 95349c7..98e728d 100644 --- a/desktop_source/source-app.cpp +++ b/desktop_source/source-app.cpp @@ -20,7 +20,6 @@ */ #include -#include // htons() #include "source-app.h" @@ -85,23 +84,14 @@ void SourceApp::on_availability_changed(P2P::Peer *peer) SourceApp::SourceApp(int port) : peer_index_(0) { - // Create a information element for a simple WFD Source - P2P::InformationElement ie; - auto sub_element = P2P::new_subelement(P2P::DEVICE_INFORMATION); - auto dev_info = (P2P::DeviceInformationSubelement*)sub_element; - - // TODO InformationElement could have constructors for this stuff... - dev_info->session_management_control_port = htons(port); - dev_info->maximum_throughput = htons(50); - dev_info->field1.device_type = P2P::SOURCE; - dev_info->field1.session_availability = true; - ie.add_subelement(sub_element); - - std::cout << "* Registering Wi-Fi Display Source with IE " << ie.to_string() << std::endl; + struct P2P::Client::Parameters params = { + .source = true, + .session_management_control_port = (uint16_t) port, + }; // register the P2P service with connman - auto array = ie.serialize (); - p2p_client_.reset(new P2P::Client(array, this)); + std::cout << "* Registering Wi-Fi Display Source" << std::endl; + p2p_client_.reset(new P2P::Client(params, this)); source_.reset(new MiracBrokerSource(port)); } diff --git a/p2p/connman-client.cpp b/p2p/connman-client.cpp index 3db7774..362229d 100644 --- a/p2p/connman-client.cpp +++ b/p2p/connman-client.cpp @@ -323,12 +323,12 @@ void Client::technology_proxy_cb (GAsyncResult *result) observer_->on_availability_changed(this); } -Client::Client(std::unique_ptr &take_array, Observer *observer): +Client::Client(const Parameters ¶ms, Observer *observer): proxy_(NULL), technology_proxy_(NULL), - observer_(observer), - array_(std::move(take_array)) + observer_(observer) { + set_ie_array_from_parameters(params); connman_watcher_ = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "net.connman", G_BUS_NAME_WATCHER_FLAGS_NONE, @@ -349,12 +349,12 @@ Client::~Client() g_clear_object (&technology_proxy_); } -void Client::set_information_element(std::unique_ptr &take_array) +void Client::set_parameters(const Parameters ¶ms) { g_return_if_fail (is_available()); unregister_peer_service(); - array_ = std::move (take_array); + set_ie_array_from_parameters(params); register_peer_service(); } @@ -377,4 +377,31 @@ void Client::scan() this); } +void Client::set_ie_array_from_parameters(const Parameters ¶ms) +{ + P2P::InformationElement ie; + auto sub_element = P2P::new_subelement(P2P::DEVICE_INFORMATION); + auto dev_info = (P2P::DeviceInformationSubelement *) sub_element; + + if (params.source && params.session_management_control_port != 0) + dev_info->session_management_control_port = + g_htons(params.session_management_control_port); + else + dev_info->session_management_control_port = g_htons(7236); + + dev_info->maximum_throughput = g_htons(50); + + if (params.source) { + if (params.sink) + dev_info->field1.device_type = P2P::DUAL_ROLE; + else + dev_info->field1.device_type = P2P::SOURCE; + } else + dev_info->field1.device_type = P2P::PRIMARY_SINK; + + dev_info->field1.session_availability = true; + ie.add_subelement(sub_element); + array_ = ie.serialize(); +} + } diff --git a/p2p/connman-client.h b/p2p/connman-client.h index 6f7dc00..6bf5562 100644 --- a/p2p/connman-client.h +++ b/p2p/connman-client.h @@ -42,10 +42,16 @@ class Client { virtual ~Observer() {} }; - Client(std::unique_ptr &take_array, Observer *observer = NULL); + struct Parameters { + bool source; + bool sink; + uint16_t session_management_control_port; + }; + + Client(const Parameters ¶ms, Observer *observer = NULL); virtual ~Client(); - void set_information_element(std::unique_ptr &take_array); + void set_parameters(const Parameters ¶ms); void set_observer(Observer* observer) { observer_ = observer; } @@ -75,6 +81,8 @@ class Client { void register_peer_service(); void unregister_peer_service(); + void set_ie_array_from_parameters(const Parameters ¶ms); + uint connman_watcher_; GDBusProxy *proxy_; GDBusProxy *technology_proxy_; diff --git a/p2p/main.cpp b/p2p/main.cpp index 02fcab5..328cc7c 100644 --- a/p2p/main.cpp +++ b/p2p/main.cpp @@ -22,7 +22,6 @@ #include #include #include -#include // htons() #include "connman-client.h" #include "information-element.h" @@ -39,21 +38,13 @@ int main (int argc, const char **argv) assert (sizeof(P2P::CoupledSinkInformationSubelement) == P2P::SubelementSize[P2P::COUPLED_SINK_INFORMATION]); - // Create a information element for a simple WFD Sink - P2P::InformationElement ie; - auto sub_element = P2P::new_subelement(P2P::DEVICE_INFORMATION); - auto dev_info = (P2P::DeviceInformationSubelement*)sub_element; - dev_info->session_management_control_port = htons(8080); - dev_info->maximum_throughput = htons(50); - dev_info->field1.device_type = P2P::PRIMARY_SINK; - dev_info->field1.session_availability = true; - ie.add_subelement (sub_element); - - std::cout << "Registering " << ie.to_string() << std::endl; + struct P2P::Client::Parameters params = { + .sink = true, + }; // register the P2P service with connman - auto array = ie.serialize (); - P2P::Client p2p_client (array); + std::cout << "Registering" << std::endl; + P2P::Client p2p_client (params); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); diff --git a/sink/sink-app.cpp b/sink/sink-app.cpp index 0815b2e..45c07a5 100644 --- a/sink/sink-app.cpp +++ b/sink/sink-app.cpp @@ -57,26 +57,13 @@ void SinkApp::on_availability_changed(P2P::Peer *peer) } SinkApp::SinkApp(){ - // Create a information element for a simple WFD Sink - P2P::InformationElement ie; - auto sub_element = P2P::new_subelement(P2P::DEVICE_INFORMATION); - auto dev_info = (P2P::DeviceInformationSubelement*)sub_element; - - // TODO port number is a lie -- we should start the sink first, then - // use the port from there (and sink should probably default to 7236) - - // TODO InformationElement could have constructors for this stuff... - dev_info->session_management_control_port = htons(7236); - dev_info->maximum_throughput = htons(50); - dev_info->field1.device_type = P2P::PRIMARY_SINK; - dev_info->field1.session_availability = true; - ie.add_subelement (sub_element); - - std::cout << "* Registering Wifi Display with IE " << ie.to_string() << std::endl; + struct P2P::Client::Parameters params = { + .sink = true, + }; // register the P2P service with connman - auto array = ie.serialize (); - p2p_client_.reset(new P2P::Client(array, this)); + std::cout << "* Registering Wifi Display" << std::endl; + p2p_client_.reset(new P2P::Client(params, this)); } SinkApp::SinkApp(const std::string& hostname, int port) From ab83c8e1a63aba41085c5e6820eb9d1cb691bb64 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 24 May 2020 23:58:36 +0200 Subject: [PATCH 2/8] Add P2P::Client and Peer base clases In order to support other clients than Connman, rename P2P::Client to P2P::ConnmanClient and P2P::Peer to P2P::ConnmanPeer while making them subclasses of new base clases P2P::Client and P2P::Peer. Those base classes are abstract and only have what is expected to be common between Connman and IWD. --- desktop_source/source-app.cpp | 3 +- desktop_source/source-app.h | 2 +- p2p/client.h | 66 +++++++++++++++++++++++ p2p/connman-client.cpp | 82 ++++++++++++++--------------- p2p/connman-client.h | 36 +++---------- p2p/connman-peer.cpp | 72 +++++++------------------ p2p/connman-peer.h | 39 +++----------- p2p/main.cpp | 2 +- p2p/peer.h | 99 +++++++++++++++++++++++++++++++++++ sink/sink-app.cpp | 2 +- 10 files changed, 246 insertions(+), 157 deletions(-) create mode 100644 p2p/client.h create mode 100644 p2p/peer.h diff --git a/desktop_source/source-app.cpp b/desktop_source/source-app.cpp index 98e728d..465abde 100644 --- a/desktop_source/source-app.cpp +++ b/desktop_source/source-app.cpp @@ -22,6 +22,7 @@ #include #include "source-app.h" +#include "connman-client.h" void SourceApp::on_availability_changed(P2P::Client *client) { @@ -91,7 +92,7 @@ SourceApp::SourceApp(int port) : // register the P2P service with connman std::cout << "* Registering Wi-Fi Display Source" << std::endl; - p2p_client_.reset(new P2P::Client(params, this)); + p2p_client_.reset(new P2P::ConnmanClient(params, this)); source_.reset(new MiracBrokerSource(port)); } diff --git a/desktop_source/source-app.h b/desktop_source/source-app.h index afc92d1..cbd9c49 100644 --- a/desktop_source/source-app.h +++ b/desktop_source/source-app.h @@ -25,7 +25,7 @@ #include #include "source.h" -#include "connman-client.h" +#include "client.h" #include "mirac_broker_source.h" class SourceApp: public P2P::Client::Observer, public P2P::Peer::Observer { diff --git a/p2p/client.h b/p2p/client.h new file mode 100644 index 0000000..b4855de --- /dev/null +++ b/p2p/client.h @@ -0,0 +1,66 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef CLIENT_H_ +#define CLIENT_H_ + +#include + +#include "peer.h" + +namespace P2P { + +class Client { + public: + class Observer { + public: + virtual void on_peer_added(Client *client, std::shared_ptr peer) {} + virtual void on_peer_removed(Client *client, std::shared_ptr peer) {} + virtual void on_availability_changed(Client *client) {} + + protected: + virtual ~Observer() {} + }; + + struct Parameters { + bool source; + bool sink; + uint16_t session_management_control_port; + }; + + virtual void set_parameters(const Parameters ¶ms) = 0; + virtual void set_observer(Observer* observer) { + observer_ = observer; + } + + virtual bool is_available() const = 0; + /* TODO error / finished handling */ + virtual void scan() = 0; + + protected: + Client(Observer *observer = NULL) : observer_(observer) {} + + Observer* observer_; + std::map> peers_; +}; + +} +#endif // CLIENT_H_ diff --git a/p2p/connman-client.cpp b/p2p/connman-client.cpp index 362229d..30cc1f1 100644 --- a/p2p/connman-client.cpp +++ b/p2p/connman-client.cpp @@ -27,7 +27,7 @@ namespace P2P { -void Client::connman_appeared_cb(GDBusConnection *connection, const char *name, const char *owner, gpointer data_ptr) +void ConnmanClient::connman_appeared_cb(GDBusConnection *connection, const char *name, const char *owner, gpointer data_ptr) { g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, @@ -36,7 +36,7 @@ void Client::connman_appeared_cb(GDBusConnection *connection, const char *name, "/", "net.connman.Manager", NULL, - Client::proxy_cb, + ConnmanClient::proxy_cb, data_ptr); /* TODO should get the p2p object path @@ -48,27 +48,27 @@ void Client::connman_appeared_cb(GDBusConnection *connection, const char *name, "/net/connman/technology/p2p", "net.connman.Technology", NULL, - Client::technology_proxy_cb, + ConnmanClient::technology_proxy_cb, data_ptr); } -void Client::connman_disappeared_cb(GDBusConnection *connection, const char *name, gpointer data_ptr) +void ConnmanClient::connman_disappeared_cb(GDBusConnection *connection, const char *name, gpointer data_ptr) { - auto client = static_cast (data_ptr); + auto client = static_cast (data_ptr); client->connman_disappeared (); } /* static C callback */ -void Client::proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr) +void ConnmanClient::proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr) { if (g_strcmp0(signal, "PeersChanged") != 0) return; - auto client = static_cast (data_ptr); + auto client = static_cast (data_ptr); client->peers_changed (params); } -void Client::get_technologies_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::get_technologies_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY (object); @@ -99,7 +99,7 @@ void Client::get_technologies_cb (GObject *object, GAsyncResult *res, gpointer d std::cout << "Warning: P2P not found in Connman technologies." << std::endl; } -void Client::register_peer_service_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::register_peer_service_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY (object); @@ -112,7 +112,7 @@ void Client::register_peer_service_cb (GObject *object, GAsyncResult *res, gpoin } } -void Client::get_peers_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::get_peers_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY(object); @@ -127,7 +127,7 @@ void Client::get_peers_cb (GObject *object, GAsyncResult *res, gpointer data_ptr } g_variant_get(params, "(a(oa{sv}))", &peer_iter); - auto client = static_cast(data_ptr); + auto client = static_cast(data_ptr); client->handle_new_peers(peer_iter); g_variant_unref(params); @@ -135,7 +135,7 @@ void Client::get_peers_cb (GObject *object, GAsyncResult *res, gpointer data_ptr } /* static C callback */ -void Client::scan_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::scan_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY (object); @@ -151,21 +151,21 @@ void Client::scan_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) } /* static C callback */ -void Client::proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { - auto client = static_cast (data_ptr); + auto client = static_cast (data_ptr); client->proxy_cb (res); } /* static C callback */ -void Client::technology_proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanClient::technology_proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { - auto client = static_cast (data_ptr); + auto client = static_cast (data_ptr); client->technology_proxy_cb (res); } -void Client::connman_disappeared() +void ConnmanClient::connman_disappeared() { bool was_available = is_available(); @@ -178,14 +178,14 @@ void Client::connman_disappeared() observer_->on_availability_changed(this); } -void Client::handle_new_peers (GVariantIter *added) +void ConnmanClient::handle_new_peers (GVariantIter *added) { GVariantIter *props; const char *path; while (g_variant_iter_loop(added, "(oa{sv})", &path, &props)) { try { - peers_[path] = std::make_shared(path, props); + peers_[path] = std::make_shared(path, props); if (observer_) observer_->on_peer_added(this, peers_[path]); } catch (std::invalid_argument &x) { @@ -194,7 +194,7 @@ void Client::handle_new_peers (GVariantIter *added) } } -void Client::peers_changed (GVariant *params) +void ConnmanClient::peers_changed (GVariant *params) { GVariantIter *added, *removed; const char *path; @@ -218,7 +218,7 @@ void Client::peers_changed (GVariant *params) g_variant_iter_free (removed); } -void Client::register_peer_service () +void ConnmanClient::register_peer_service () { GVariantBuilder builder; @@ -241,11 +241,11 @@ void Client::register_peer_service () G_DBUS_CALL_FLAGS_NONE, -1, NULL, - Client::register_peer_service_cb, + ConnmanClient::register_peer_service_cb, this); } -void Client::unregister_peer_service () +void ConnmanClient::unregister_peer_service () { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}")); @@ -262,11 +262,11 @@ void Client::unregister_peer_service () G_DBUS_CALL_FLAGS_NONE, -1, NULL, - Client::register_peer_service_cb, + ConnmanClient::register_peer_service_cb, this); } -void Client::initialize_peers () +void ConnmanClient::initialize_peers () { g_dbus_proxy_call(proxy_, "GetPeers", @@ -274,12 +274,12 @@ void Client::initialize_peers () G_DBUS_CALL_FLAGS_NONE, -1, NULL, - Client::get_peers_cb, + ConnmanClient::get_peers_cb, this); } -void Client::proxy_cb (GAsyncResult *result) +void ConnmanClient::proxy_cb (GAsyncResult *result) { GError *error = NULL; @@ -291,7 +291,7 @@ void Client::proxy_cb (GAsyncResult *result) } g_signal_connect(proxy_, "g-signal", - G_CALLBACK (Client::proxy_signal_cb), this); + G_CALLBACK (ConnmanClient::proxy_signal_cb), this); g_dbus_proxy_call(proxy_, "GetTechnologies", @@ -299,7 +299,7 @@ void Client::proxy_cb (GAsyncResult *result) G_DBUS_CALL_FLAGS_NONE, -1, NULL, - Client::get_technologies_cb, + ConnmanClient::get_technologies_cb, this); initialize_peers(); @@ -309,7 +309,7 @@ void Client::proxy_cb (GAsyncResult *result) observer_->on_availability_changed(this); } -void Client::technology_proxy_cb (GAsyncResult *result) +void ConnmanClient::technology_proxy_cb (GAsyncResult *result) { GError *error = NULL; @@ -323,21 +323,21 @@ void Client::technology_proxy_cb (GAsyncResult *result) observer_->on_availability_changed(this); } -Client::Client(const Parameters ¶ms, Observer *observer): +ConnmanClient::ConnmanClient(const Parameters ¶ms, Observer *observer): + Client(observer), proxy_(NULL), - technology_proxy_(NULL), - observer_(observer) + technology_proxy_(NULL) { set_ie_array_from_parameters(params); connman_watcher_ = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "net.connman", G_BUS_NAME_WATCHER_FLAGS_NONE, - Client::connman_appeared_cb, - Client::connman_disappeared_cb, + ConnmanClient::connman_appeared_cb, + ConnmanClient::connman_disappeared_cb, this, NULL); } -Client::~Client() +ConnmanClient::~ConnmanClient() { if (connman_watcher_ != 0) { g_bus_unwatch_name (connman_watcher_); @@ -349,7 +349,7 @@ Client::~Client() g_clear_object (&technology_proxy_); } -void Client::set_parameters(const Parameters ¶ms) +void ConnmanClient::set_parameters(const Parameters ¶ms) { g_return_if_fail (is_available()); @@ -358,12 +358,12 @@ void Client::set_parameters(const Parameters ¶ms) register_peer_service(); } -bool Client::is_available() const +bool ConnmanClient::is_available() const { return proxy_ && technology_proxy_; } -void Client::scan() +void ConnmanClient::scan() { g_return_if_fail (is_available()); @@ -373,11 +373,11 @@ void Client::scan() G_DBUS_CALL_FLAGS_NONE, 60 * 1000, NULL, - Client::scan_cb, + ConnmanClient::scan_cb, this); } -void Client::set_ie_array_from_parameters(const Parameters ¶ms) +void ConnmanClient::set_ie_array_from_parameters(const Parameters ¶ms) { P2P::InformationElement ie; auto sub_element = P2P::new_subelement(P2P::DEVICE_INFORMATION); diff --git a/p2p/connman-client.h b/p2p/connman-client.h index 6bf5562..2d82acd 100644 --- a/p2p/connman-client.h +++ b/p2p/connman-client.h @@ -25,42 +25,24 @@ #include #include +#include "client.h" #include "information-element.h" #include "connman-peer.h" namespace P2P { -class Client { +class ConnmanClient : public Client { public: - class Observer { - public: - virtual void on_peer_added(Client *client, std::shared_ptr peer) {} - virtual void on_peer_removed(Client *client, std::shared_ptr peer) {} - virtual void on_availability_changed(Client *client) {} + ConnmanClient(const Parameters ¶ms, Observer *observer = NULL); + virtual ~ConnmanClient(); - protected: - virtual ~Observer() {} - }; + void set_parameters(const Parameters ¶ms) override; - struct Parameters { - bool source; - bool sink; - uint16_t session_management_control_port; - }; - - Client(const Parameters ¶ms, Observer *observer = NULL); - virtual ~Client(); - - void set_parameters(const Parameters ¶ms); - void set_observer(Observer* observer) { - observer_ = observer; - } - - bool is_available() const; + bool is_available() const override; /* TODO error / finished handling */ - void scan(); + void scan() override; - private: + protected: static void connman_appeared_cb(GDBusConnection *connection, const char *name, const char *owner, gpointer data_ptr); static void connman_disappeared_cb(GDBusConnection *connection, const char *name, gpointer data_ptr); static void proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr); @@ -87,9 +69,7 @@ class Client { GDBusProxy *proxy_; GDBusProxy *technology_proxy_; - Observer* observer_; std::unique_ptrarray_; - std::map> peers_; }; } diff --git a/p2p/connman-peer.cpp b/p2p/connman-peer.cpp index 292045f..721192b 100644 --- a/p2p/connman-peer.cpp +++ b/p2p/connman-peer.cpp @@ -28,18 +28,18 @@ namespace P2P { /* static C callback */ -void Peer::proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanPeer::proxy_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { - auto client = static_cast (data_ptr); + auto client = static_cast (data_ptr); client->proxy_cb (res); } /* static C callback */ -void Peer::proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr) +void ConnmanPeer::proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr) { GVariant *property; char *name; - auto peer = static_cast (data_ptr); + auto peer = static_cast (data_ptr); if (g_strcmp0(signal, "PropertyChanged") != 0) return; @@ -49,7 +49,7 @@ void Peer::proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *s peer->handle_property_change (name, property); } -void Peer::handle_property_change (const char *name, GVariant *property) +void ConnmanPeer::handle_property_change (const char *name, GVariant *property) { if (g_strcmp0(name, "State") == 0) { state_changed (g_variant_get_string (property, NULL)); @@ -97,7 +97,7 @@ void Peer::handle_property_change (const char *name, GVariant *property) } /* static C callback */ -void Peer::connect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanPeer::connect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY (object); @@ -113,7 +113,7 @@ void Peer::connect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) } /* static C callback */ -void Peer::disconnect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) +void ConnmanPeer::disconnect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) { GError *error = NULL; GDBusProxy *proxy = G_DBUS_PROXY (object); @@ -128,7 +128,7 @@ void Peer::disconnect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr) std::cout << "* disconnected "<< std::endl; } -void Peer::proxy_cb (GAsyncResult *result) +void ConnmanPeer::proxy_cb (GAsyncResult *result) { GError *error = NULL; @@ -140,7 +140,7 @@ void Peer::proxy_cb (GAsyncResult *result) } g_signal_connect (proxy_, "g-signal", - G_CALLBACK (Peer::proxy_signal_cb), this); + G_CALLBACK (ConnmanPeer::proxy_signal_cb), this); /* TODO check the ip address in case it's up to date already */ @@ -148,32 +148,7 @@ void Peer::proxy_cb (GAsyncResult *result) observer_->on_initialized(this); } -void Peer::ips_changed (const char *remote, const char *local) -{ - if (g_strcmp0 (remote, remote_host_.c_str()) == 0 && - g_strcmp0 (local, local_host_.c_str()) == 0) - return; - - auto was_available = is_available(); - - if (g_strcmp0 (remote, "0.0.0.0") == 0) - remote_host_.clear(); - else - remote_host_ = std::string(remote); - - if (g_strcmp0 (local, "0.0.0.0") == 0) - local_host_.clear(); - else - local_host_ = std::string(local); - - if (!observer_) - return; - - if (was_available != is_available()) - observer_->on_availability_changed(this); -} - -void Peer::state_changed (const char *state) +void ConnmanPeer::state_changed (const char *state) { bool ready = (g_strcmp0 (state, "ready") == 0); @@ -190,21 +165,13 @@ void Peer::state_changed (const char *state) observer_->on_availability_changed(this); } -void Peer::name_changed (const char *name) -{ - if (g_strcmp0 (name, name_.c_str()) == 0) - return; - - name_ = std::string (name); -} - - -Peer::Peer(const char *object_path, GVariantIter *props): - observer_(NULL) +ConnmanPeer::ConnmanPeer(const char *object_path, GVariantIter *props) { GVariant *val; const char *prop_name; + observer_ = NULL; + while (g_variant_iter_loop (props, "{&sv}", &prop_name, &val)) { handle_property_change (prop_name, val); } @@ -219,11 +186,11 @@ Peer::Peer(const char *object_path, GVariantIter *props): object_path, "net.connman.Peer", NULL, - Peer::proxy_cb, + ConnmanPeer::proxy_cb, this); } -void Peer::connect() +void ConnmanPeer::connect() { g_dbus_proxy_call (proxy_, "Connect", @@ -231,11 +198,11 @@ void Peer::connect() G_DBUS_CALL_FLAGS_NONE, 60 * 1000, // is 1 minute too long? NULL, - Peer::connect_cb, + ConnmanPeer::connect_cb, this); } -void Peer::disconnect() +void ConnmanPeer::disconnect() { g_dbus_proxy_call (proxy_, "Disconnect", @@ -243,12 +210,11 @@ void Peer::disconnect() G_DBUS_CALL_FLAGS_NONE, -1, NULL, - Peer::disconnect_cb, + ConnmanPeer::disconnect_cb, this); } - -Peer::~Peer() +ConnmanPeer::~ConnmanPeer() { if (proxy_) g_clear_object (&proxy_); diff --git a/p2p/connman-peer.h b/p2p/connman-peer.h index 0b46076..3b03cdf 100644 --- a/p2p/connman-peer.h +++ b/p2p/connman-peer.h @@ -20,6 +20,7 @@ */ #include +#include "peer.h" #include "information-element.h" #ifndef CONNMAN_PEER_H_ @@ -27,51 +28,27 @@ namespace P2P { -class Peer { +class ConnmanPeer : public Peer { public: - class Observer { - public: - virtual void on_availability_changed(Peer *peer) {} - virtual void on_initialized(Peer *peer) {} - - protected: - virtual ~Observer() {} - }; - - Peer(const char *object_path, GVariantIter *property_iterator); - virtual ~Peer(); - - void set_observer(Observer* observer) { - observer_ = observer; - } + ConnmanPeer(const char *object_path, GVariantIter *property_iterator); + virtual ~ConnmanPeer(); /* TODO add error handling for these -- maybe through observer.on_error? */ - void connect(); - void disconnect(); + void connect() override; + void disconnect() override; - const P2P::DeviceType device_type() const { return ie_->get_device_type(); } - const std::string& name() const { return name_; } - const std::string& remote_host() const {return remote_host_; } - const int remote_port() const { return ie_->get_rtsp_port(); } - const std::string& local_host() const {return local_host_; } - bool is_available() const { return ready_ && !remote_host_.empty() && !local_host_.empty(); } + bool is_available() const override { return ready_ && !remote_host_.empty() && !local_host_.empty(); } - private: + protected: static void proxy_signal_cb (GDBusProxy *proxy, const char *sender, const char *signal, GVariant *params, gpointer data_ptr); static void proxy_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); static void connect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr); static void disconnect_cb (GObject *object, GAsyncResult *res, gpointer data_ptr); - void ips_changed (const char *remote, const char *local); void state_changed (const char *state); - void name_changed (const char *name); void proxy_cb (GAsyncResult *res); void handle_property_change (const char *name, GVariant *property); - Observer *observer_; - std::string name_; - std::string remote_host_; - std::string local_host_; bool ready_; GDBusProxy *proxy_; std::shared_ptr ie_; diff --git a/p2p/main.cpp b/p2p/main.cpp index 328cc7c..47d0aaa 100644 --- a/p2p/main.cpp +++ b/p2p/main.cpp @@ -44,7 +44,7 @@ int main (int argc, const char **argv) // register the P2P service with connman std::cout << "Registering" << std::endl; - P2P::Client p2p_client (params); + P2P::ConnmanClient p2p_client (params); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); diff --git a/p2p/peer.h b/p2p/peer.h new file mode 100644 index 0000000..619b958 --- /dev/null +++ b/p2p/peer.h @@ -0,0 +1,99 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include "information-element.h" + +#ifndef PEER_H_ +#define PEER_H_ + +namespace P2P { + +class Peer { + public: + class Observer { + public: + virtual void on_availability_changed(Peer *peer) {} + virtual void on_initialized(Peer *peer) {} + + protected: + virtual ~Observer() {} + }; + + virtual void set_observer(Observer* observer) { + observer_ = observer; + } + + /* TODO add error handling for these -- maybe through observer.on_error? */ + virtual void connect() = 0; + virtual void disconnect() = 0; + + const P2P::DeviceType device_type() const { return device_type_; } + const std::string& name() const { return name_; } + const std::string& remote_host() const { return remote_host_; } + const int remote_port() const { return rtsp_port_; } + const std::string& local_host() const { return local_host_; } + virtual bool is_available() const = 0; + + protected: + virtual void ips_changed (const char *remote, const char *local) + { + if (g_strcmp0 (remote, remote_host_.c_str()) == 0 && + g_strcmp0 (local, local_host_.c_str()) == 0) + return; + + auto was_available = is_available(); + + if (g_strcmp0 (remote, "0.0.0.0") == 0) + remote_host_.clear(); + else + remote_host_ = std::string(remote); + + if (g_strcmp0 (local, "0.0.0.0") == 0) + local_host_.clear(); + else + local_host_ = std::string(local); + + if (!observer_) + return; + + if (was_available != is_available()) + observer_->on_availability_changed(this); + } + + virtual void name_changed (const char *name) + { + if (g_strcmp0 (name, name_.c_str()) == 0) + return; + + name_ = std::string (name); + } + + Observer *observer_; + P2P::DeviceType device_type_; + std::string name_; + std::string remote_host_; + std::string local_host_; + int rtsp_port_; +}; + +} +#endif // PEER_H_ diff --git a/sink/sink-app.cpp b/sink/sink-app.cpp index 45c07a5..6d27f52 100644 --- a/sink/sink-app.cpp +++ b/sink/sink-app.cpp @@ -63,7 +63,7 @@ SinkApp::SinkApp(){ // register the P2P service with connman std::cout << "* Registering Wifi Display" << std::endl; - p2p_client_.reset(new P2P::Client(params, this)); + p2p_client_.reset(new P2P::ConnmanClient(params, this)); } SinkApp::SinkApp(const std::string& hostname, int port) From 7374b9809fc523cd0a81daec7be29eb3cbf2831e Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 19 Jul 2020 03:27:06 +0200 Subject: [PATCH 3/8] p2p: Move struct Parameters to peer.h and out of the Client class Make the struct global within the P2P namespace so it can be used in the Peer class as a utility struct. --- desktop_source/source-app.cpp | 2 +- p2p/client.h | 6 ------ p2p/main.cpp | 2 +- p2p/peer.h | 6 ++++++ sink/sink-app.cpp | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/desktop_source/source-app.cpp b/desktop_source/source-app.cpp index 465abde..a33bcd4 100644 --- a/desktop_source/source-app.cpp +++ b/desktop_source/source-app.cpp @@ -85,7 +85,7 @@ void SourceApp::on_availability_changed(P2P::Peer *peer) SourceApp::SourceApp(int port) : peer_index_(0) { - struct P2P::Client::Parameters params = { + static struct P2P::Parameters params = { .source = true, .session_management_control_port = (uint16_t) port, }; diff --git a/p2p/client.h b/p2p/client.h index b4855de..7c52697 100644 --- a/p2p/client.h +++ b/p2p/client.h @@ -40,12 +40,6 @@ class Client { virtual ~Observer() {} }; - struct Parameters { - bool source; - bool sink; - uint16_t session_management_control_port; - }; - virtual void set_parameters(const Parameters ¶ms) = 0; virtual void set_observer(Observer* observer) { observer_ = observer; diff --git a/p2p/main.cpp b/p2p/main.cpp index 47d0aaa..9767b29 100644 --- a/p2p/main.cpp +++ b/p2p/main.cpp @@ -38,7 +38,7 @@ int main (int argc, const char **argv) assert (sizeof(P2P::CoupledSinkInformationSubelement) == P2P::SubelementSize[P2P::COUPLED_SINK_INFORMATION]); - struct P2P::Client::Parameters params = { + static struct P2P::Parameters params = { .sink = true, }; diff --git a/p2p/peer.h b/p2p/peer.h index 619b958..78e6c2c 100644 --- a/p2p/peer.h +++ b/p2p/peer.h @@ -27,6 +27,12 @@ namespace P2P { +struct Parameters { + bool source; + bool sink; + uint16_t session_management_control_port; +}; + class Peer { public: class Observer { diff --git a/sink/sink-app.cpp b/sink/sink-app.cpp index 6d27f52..7895cb6 100644 --- a/sink/sink-app.cpp +++ b/sink/sink-app.cpp @@ -57,7 +57,7 @@ void SinkApp::on_availability_changed(P2P::Peer *peer) } SinkApp::SinkApp(){ - struct P2P::Client::Parameters params = { + static struct P2P::Parameters params = { .sink = true, }; From e08e1a29fe6c4bbd5328f436c778a947e23cf4cb Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 19 Jul 2020 03:29:55 +0200 Subject: [PATCH 4/8] p2p: Add IWD client code Similar to the DBus-based Connman client add a DBus-based IWD client for managing peer discovery, connection/disconnection and P2P service registation using the IWD DBus-service. --- p2p/CMakeLists.txt | 2 +- p2p/iwd-client.cpp | 514 +++++++++++++++++++++++++++++++++++++++++++++ p2p/iwd-client.h | 77 +++++++ p2p/iwd-peer.cpp | 359 +++++++++++++++++++++++++++++++ p2p/iwd-peer.h | 77 +++++++ 5 files changed, 1028 insertions(+), 1 deletion(-) create mode 100644 p2p/iwd-client.cpp create mode 100644 p2p/iwd-client.h create mode 100644 p2p/iwd-peer.cpp create mode 100644 p2p/iwd-peer.h diff --git a/p2p/CMakeLists.txt b/p2p/CMakeLists.txt index 161f733..24aca02 100644 --- a/p2p/CMakeLists.txt +++ b/p2p/CMakeLists.txt @@ -8,7 +8,7 @@ pkg_check_modules (GIO REQUIRED gio-2.0) include_directories(${GIO_INCLUDE_DIRS}) add_library(p2p STATIC - connman-peer.cpp connman-client.cpp information-element.cpp + connman-peer.cpp connman-client.cpp information-element.cpp iwd-peer.cpp iwd-client.cpp ) add_executable(register-peer-service main.cpp) diff --git a/p2p/iwd-client.cpp b/p2p/iwd-client.cpp new file mode 100644 index 0000000..e9ce5a4 --- /dev/null +++ b/p2p/iwd-client.cpp @@ -0,0 +1,514 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include + +#include "iwd-client.h" + +#define IWD_BUS_TYPE G_BUS_TYPE_SYSTEM +#define IWD_SERVICE "net.connman.iwd" +#define IWD_P2P_INTERFACE "net.connman.iwd.p2p.Device" +#define IWD_P2P_PEER_INTERFACE "net.connman.iwd.p2p.Peer" +#define IWD_P2P_SERVICE_MANAGER_INTERFACE "net.connman.iwd.p2p.ServiceManager" +#define IWD_P2P_WFD_INTERFACE "net.connman.iwd.p2p.Display" +#define IWD_WSC_INTERFACE "net.connman.iwd.SimpleConfiguration" + +namespace P2P { + +void IWDClient::check_peer_ready(std::shared_ptr peer) +{ + if (!p2p_proxy_ || g_strcmp0(g_dbus_proxy_get_object_path(p2p_proxy_), peer->get_dev_path())) + return; + if (!peer->is_ready()) + return; + + peers_[peer->get_path()] = peer; + non_wfd_peers_.erase(peer->get_path()); + + if (observer_) + observer_->on_peer_added(this, peer); + + peer->added(); +} + +void IWDClient::check_all_peers(void) +{ + const char *path = p2p_proxy_ ? g_dbus_proxy_get_object_path(p2p_proxy_) : NULL; + std::map> removed; + + for (auto it = peers_.begin(); it != peers_.end();) { + auto peer = std::static_pointer_cast(it->second); + + if (!g_strcmp0(path, peer->get_dev_path()) && peer->is_ready()) { + ++it; + continue; + } + + removed[peer->get_path()] = peer; + peers_.erase(it++); + + if (observer_) + observer_->on_peer_removed(this, peer); + + peer->removed(); + } + + for (auto it = non_wfd_peers_.begin(); it != non_wfd_peers_.end();) { + auto peer = std::static_pointer_cast(it->second); + + if (g_strcmp0(path, peer->get_dev_path()) || !peer->is_ready()) { + ++it; + continue; + } + + peers_[peer->get_path()] = peer; + non_wfd_peers_.erase(it++); + + if (observer_) + observer_->on_peer_added(this, peer); + + peer->added(); + } + + non_wfd_peers_.insert(removed.begin(), removed.end()); +} + +void IWDClient::check_available() +{ + if (!enabled_ || !registered_) + return; + + if (observer_) + observer_->on_availability_changed(this); +} + +void IWDClient::check_unavailable() +{ + if (enabled_ && registered_) + return; + + if (observer_) + observer_->on_availability_changed(this); +} + +void IWDClient::iwd_properties_changed(GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + auto client = static_cast(user_data); + gboolean new_bool; + + if (g_variant_lookup(changed_properties, "Enabled", "b", &new_bool)) { + if (new_bool == client->enabled_) + return; + + client->enabled_ = new_bool; + + if (new_bool) { + std::cout << "P2P device is now enabled" << std::endl; + client->check_available(); + } else { + std::cout << "P2P device is now disabled" << std::endl; + client->check_unavailable(); + } + + return; + } +} + +void IWDClient::set_enabled_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_variant_unref(g_dbus_proxy_call_finish(proxy, res, &error)); + if (error) { + std::cout << "set enabled error " << error->message << std::endl; + g_clear_error(&error); + } +} + +void IWDClient::interface_added(GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + GDBusProxy *proxy; + const char *iface_name, *path; + + proxy = G_DBUS_PROXY(interface); + iface_name = g_dbus_proxy_get_interface_name(proxy); + path = g_dbus_proxy_get_object_path(proxy); + + if (!g_strcmp0(iface_name, IWD_P2P_INTERFACE)) { + if (client->p2p_proxy_) + return; + + client->p2p_proxy_ = G_DBUS_PROXY(g_object_ref(proxy)); + g_signal_connect(client->p2p_proxy_, "g-properties-changed", + G_CALLBACK(iwd_properties_changed), data_ptr); + client->check_all_peers(); + + GVariant *value = g_dbus_proxy_get_cached_property(client->p2p_proxy_, "Enabled"); + if (!value) { + std::cout << "can't get the Enabled property" << std::endl; + return; + } + + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) { + std::cout << "Enabled property type wrong" << std::endl; + g_variant_unref(value); + return; + } + + if (g_variant_get_boolean(value)) { + client->enabled_ = true; + client->check_available(); + g_variant_unref(value); + return; + } + + g_variant_unref(value); + g_dbus_proxy_call(client->p2p_proxy_, "org.freedesktop.DBus.Properties.Set", + g_variant_new("(ssv)", IWD_P2P_INTERFACE, + "Enabled", g_variant_new("b", true)), + G_DBUS_CALL_FLAGS_NONE, 2000, client->cancellable_, + set_enabled_cb, data_ptr); + return; + } + + if ( !g_strcmp0(iface_name, IWD_P2P_PEER_INTERFACE) || + !g_strcmp0(iface_name, IWD_P2P_WFD_INTERFACE) || + !g_strcmp0(iface_name, IWD_WSC_INTERFACE)) { + if (client->peers_.find(path) != client->peers_.end()) + return; /* Already have this peer */ + + if (client->non_wfd_peers_.find(path) == client->non_wfd_peers_.end()) + client->non_wfd_peers_[path] = std::make_shared(path); + + auto peer = std::static_pointer_cast(client->non_wfd_peers_[path]); + if (!g_strcmp0(iface_name, IWD_P2P_PEER_INTERFACE)) + peer->set_peer_proxy(proxy); + else if (!g_strcmp0(iface_name, IWD_P2P_WFD_INTERFACE)) + peer->set_wfd_proxy(proxy); + else + peer->set_wsc_proxy(proxy); + + client->check_peer_ready(peer); + return; + } +} + +void IWDClient::interface_removed(GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data) +{ + auto client = static_cast(user_data); + GDBusProxy *proxy; + const char *iface_name, *path; + + proxy = G_DBUS_PROXY(interface); + iface_name = g_dbus_proxy_get_interface_name(proxy); + path = g_dbus_proxy_get_object_path(proxy); + + if (!g_strcmp0(iface_name, IWD_P2P_INTERFACE)) { + if (proxy != client->p2p_proxy_) + return; + + g_signal_handlers_disconnect_by_data(client->p2p_proxy_, user_data); + g_clear_object(&client->p2p_proxy_); + client->enabled_ = false; + client->check_unavailable(); + client->check_all_peers(); + return; + } + + if ( !g_strcmp0(iface_name, IWD_P2P_PEER_INTERFACE) || + !g_strcmp0(iface_name, IWD_P2P_WFD_INTERFACE) || + !g_strcmp0(iface_name, IWD_WSC_INTERFACE)) { + std::shared_ptr peer; + + if (client->peers_.find(path) != client->peers_.end()) { + peer = std::static_pointer_cast(client->peers_[path]); + client->peers_.erase(path); + client->non_wfd_peers_[path] = peer; + + if (client->observer_) + client->observer_->on_peer_removed(client, peer); + + peer->removed(); + } else if (client->non_wfd_peers_.find(path) == client->non_wfd_peers_.end()) + peer = std::static_pointer_cast(client->non_wfd_peers_[path]); + else + return; + + if (!g_strcmp0(iface_name, IWD_P2P_PEER_INTERFACE)) + peer->set_peer_proxy(NULL); + else if (!g_strcmp0(iface_name, IWD_P2P_WFD_INTERFACE)) + peer->set_wfd_proxy(NULL); + else + peer->set_wsc_proxy(NULL); + + /* TODO: erase once no interfaces left */ + return; + } +} + +void IWDClient::object_added(GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data) +{ + auto client = static_cast(user_data); + GList *interfaces, *iter; + + interfaces = g_dbus_object_get_interfaces(object); + + for (iter = interfaces; iter; iter = iter->next) + interface_added(NULL, object, G_DBUS_INTERFACE (iter->data), client); + + g_list_free_full(interfaces, g_object_unref); +} + +void IWDClient::object_removed(GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data) +{ + auto client = static_cast(user_data); + const char *path = g_dbus_object_get_object_path(object); + + if (!g_strcmp0(path, g_dbus_proxy_get_object_path(client->p2p_proxy_))) { + g_signal_handlers_disconnect_by_data(client->p2p_proxy_, user_data); + g_clear_object(&client->p2p_proxy_); + client->enabled_ = false; + client->check_unavailable(); + client->check_all_peers(); + return; + } + + if (client->peers_.find(path) != client->peers_.end()) { + auto peer = std::static_pointer_cast(client->peers_[path]); + client->peers_.erase(path); + + if (client->observer_) + client->observer_->on_peer_removed(client, peer); + + peer->removed(); + return; + } + + if (client->non_wfd_peers_.find(path) != client->non_wfd_peers_.end()) + client->non_wfd_peers_.erase(path); +} + +void IWDClient::register_display_svc_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_variant_unref(g_dbus_proxy_call_finish(proxy, res, &error)); + if (error) { + std::cout << "register error " << error->message << std::endl; + g_clear_error(&error); + return; + } + + client->registered_ = true; + client->check_available(); +} + +void IWDClient::new_object_manager_cb(GObject *object, GAsyncResult *result, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + GError *error = NULL; + GList *objects, *iter; + GVariantBuilder params_builder; + + client->object_manager_ = g_dbus_object_manager_client_new_for_bus_finish(result, &error); + if (!client->object_manager_) { + std::cout << "g_dbus_object_manager_client_new_for_bus error: " << error->message << std::endl; + g_clear_error(&error); + return; + } + + g_signal_connect(client->object_manager_, "object-added", + G_CALLBACK(object_added), data_ptr); + g_signal_connect(client->object_manager_, "object-removed", + G_CALLBACK(object_removed), data_ptr); + g_signal_connect(client->object_manager_, "interface-added", + G_CALLBACK(interface_added), data_ptr); + g_signal_connect(client->object_manager_, "interface-removed", + G_CALLBACK(interface_removed), data_ptr); + + client->service_manager_proxy_ = G_DBUS_PROXY(g_dbus_object_manager_get_interface(client->object_manager_, "/net/connman/iwd", IWD_P2P_SERVICE_MANAGER_INTERFACE)); + + g_variant_builder_init(¶ms_builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(¶ms_builder, "{sv}", "Source", g_variant_new("b", client->params_.source)); + g_variant_builder_add(¶ms_builder, "{sv}", "Sink", g_variant_new("b", client->params_.sink)); + g_variant_builder_add(¶ms_builder, "{sv}", "Port", g_variant_new("q", client->params_.session_management_control_port)); + + g_dbus_proxy_call(client->service_manager_proxy_, "RegisterDisplayService", + g_variant_new("(a{sv})", ¶ms_builder), G_DBUS_CALL_FLAGS_NONE, 2000, + client->cancellable_, register_display_svc_cb, data_ptr); + + objects = g_dbus_object_manager_get_objects(client->object_manager_); + + for (iter = objects; iter; iter = iter->next) + client->object_added(client->object_manager_, G_DBUS_OBJECT(iter->data), data_ptr); + + g_list_free_full(objects, g_object_unref); +} + +void IWDClient::iwd_appeared_cb(GDBusConnection *connection, const char *name, const char *owner, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + + std::cout << "IWD appeared" << std::endl; + + if (client->object_manager_) + return; + + g_dbus_object_manager_client_new_for_bus(IWD_BUS_TYPE, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + IWD_SERVICE, "/", NULL, NULL, NULL, + client->cancellable_, + new_object_manager_cb, data_ptr); +} + +/* Cleanup actions common to IWD disconnecting and us disconnecting */ +void IWDClient::iwd_cleanup(void) +{ + for (auto it = peers_.begin(); it != peers_.end(); ++it) + static_cast(it->second.get())->removed(); + + peers_.clear(); + non_wfd_peers_.clear(); + + g_cancellable_cancel(cancellable_); + g_clear_object(&cancellable_); + + if (object_manager_) { + g_signal_handlers_disconnect_by_data(object_manager_, this); + g_clear_object(&object_manager_); + } + + if (p2p_proxy_) + g_clear_object(&p2p_proxy_); + + if (service_manager_proxy_) + g_clear_object(&service_manager_proxy_); + + if (disc_timeout_) + g_clear_handle_id(&disc_timeout_, g_source_remove); + + enabled_ = false; + registered_ = false; +} + +void IWDClient::iwd_disappeared_cb(GDBusConnection *connection, const char *name, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + bool was_available = client->is_available(); + + std::cout << "IWD disappeared" << std::endl; + + if (client->observer_) + for (auto it = client->peers_.begin(); it != client->peers_.end(); ++it) + client->observer_->on_peer_removed(client, it->second); + + client->iwd_cleanup(); + client->cancellable_ = g_cancellable_new(); + + if (was_available && client->observer_) + client->observer_->on_availability_changed(client); +} + +IWDClient::IWDClient(const Parameters ¶ms, Observer *observer): + Client(observer), + params_(params), + p2p_proxy_(NULL), + service_manager_proxy_(NULL), + object_manager_(NULL), + enabled_(false), + registered_(false), + iwd_watcher_(0), + disc_timeout_(0) +{ + cancellable_ = g_cancellable_new(); + iwd_watcher_ = g_bus_watch_name(G_BUS_TYPE_SYSTEM, IWD_SERVICE, + G_BUS_NAME_WATCHER_FLAGS_NONE, + iwd_appeared_cb, iwd_disappeared_cb, + this, NULL); +} + +IWDClient::~IWDClient() +{ + if (registered_) { + g_dbus_proxy_call(service_manager_proxy_, "UnregisterDisplayService", + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, NULL, NULL, NULL); + registered_ = false; + } + + iwd_cleanup(); + + if (iwd_watcher_ != 0) { + g_bus_unwatch_name(iwd_watcher_); + iwd_watcher_ = 0; + } +} + +void IWDClient::set_parameters(const Parameters ¶ms) +{ + params_ = params; + /* TODO: re-register? */ +} + +gboolean IWDClient::disc_timeout_cb(gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + + std::cout << "discovery finished" << std::endl; + g_dbus_proxy_call(client->p2p_proxy_, "ReleaseDiscovery", + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, + client->cancellable_, NULL, NULL); + client->disc_timeout_ = 0; + return FALSE; +} + +void IWDClient::disc_request_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + auto client = static_cast(data_ptr); + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_variant_unref(g_dbus_proxy_call_finish(proxy, res, &error)); + if (error) { + std::cout << "RequestDiscovery error " << error->message << std::endl; + g_clear_error(&error); + return; + } + + client->disc_timeout_ = g_timeout_add_seconds(20, disc_timeout_cb, data_ptr); +} + +void IWDClient::scan() +{ + g_return_if_fail(is_available()); + + g_dbus_proxy_call(p2p_proxy_, "RequestDiscovery", + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, + cancellable_, disc_request_cb, this); +} + +} diff --git a/p2p/iwd-client.h b/p2p/iwd-client.h new file mode 100644 index 0000000..44be389 --- /dev/null +++ b/p2p/iwd-client.h @@ -0,0 +1,77 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef IWD_CLIENT_H_ +#define IWD_CLIENT_H_ + +#include +#include + +#include "client.h" +#include "iwd-peer.h" + +namespace P2P { + +class IWDClient : public Client { + public: + IWDClient(const Parameters ¶ms, Observer *observer = NULL); + virtual ~IWDClient(); + + void set_parameters(const Parameters ¶ms) override; + + bool is_available() const override { return enabled_ && registered_; } + + void scan() override; + + protected: + void check_peer_ready(std::shared_ptr peer); + void check_all_peers(void); + void check_available(void); + void check_unavailable(void); + void iwd_cleanup(void); + + static void set_enabled_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + static void register_display_svc_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + static void interface_added(GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer data_ptr); + static void interface_removed(GDBusObjectManager *object_manager, GDBusObject *object, GDBusInterface *interface, gpointer user_data); + static void object_added(GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data); + static void object_removed(GDBusObjectManager *object_manager, GDBusObject *object, gpointer user_data); + static void new_object_manager_cb(GObject *object, GAsyncResult *result, gpointer data_ptr); + static void iwd_properties_changed(GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data); + static void iwd_appeared_cb(GDBusConnection *connection, const char *name, const char *owner, gpointer data_ptr); + static void iwd_disappeared_cb(GDBusConnection *connection, const char *name, gpointer data_ptr); + static gboolean disc_timeout_cb(gpointer data_ptr); + static void disc_request_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + + Parameters params_; + std::map> non_wfd_peers_; + GDBusProxy *p2p_proxy_; + GDBusProxy *service_manager_proxy_; + GDBusObjectManager *object_manager_; + bool enabled_; + bool registered_; + uint iwd_watcher_; + GCancellable *cancellable_; + guint disc_timeout_; +}; + +} +#endif // IWD_CLIENT_H_ diff --git a/p2p/iwd-peer.cpp b/p2p/iwd-peer.cpp new file mode 100644 index 0000000..713623b --- /dev/null +++ b/p2p/iwd-peer.cpp @@ -0,0 +1,359 @@ +/* vim: ts=4 et + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iwd-peer.h" + +namespace P2P { + +IWDPeer::IWDPeer(const char *path): + params_({}), + peer_proxy_(NULL), + wfd_proxy_(NULL), + wsc_proxy_(NULL), + dev_path_(NULL), + connected_(false) +{ + observer_ = NULL; + path_ = g_strdup(path); + cancellable_ = g_cancellable_new(); +} + +IWDPeer::~IWDPeer() +{ + g_cancellable_cancel(cancellable_); + g_clear_object(&cancellable_); + observer_ = NULL; + set_peer_proxy(NULL); + set_wfd_proxy(NULL); + set_wsc_proxy(NULL); + g_free(path_); + g_free(dev_path_); +} + +void IWDPeer::connect_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_dbus_proxy_call_finish(proxy, res, &error); + if (error) { + std::cout << "Connect() error " << error->message << std::endl; + g_clear_error(&error); + } else + std::cout << "Connect() success" << std::endl; +} + +void IWDPeer::cancel_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_dbus_proxy_call_finish(proxy, res, &error); + if (error) { + std::cout << "Cancel() error " << error->message << std::endl; + g_clear_error(&error); + } else + std::cout << "Cancel() success" << std::endl; +} + +void IWDPeer::disconnect_cb(GObject *object, GAsyncResult *res, gpointer data_ptr) +{ + GDBusProxy *proxy = G_DBUS_PROXY(object); + GError *error = NULL; + + g_dbus_proxy_call_finish(proxy, res, &error); + if (error) { + std::cout << "Disconnect() error " << error->message << std::endl; + g_clear_error(&error); + } else + std::cout << "Disconnect() success" << std::endl; +} + +void IWDPeer::connect() +{ + g_dbus_proxy_call(wsc_proxy_, "PushButton", NULL, G_DBUS_CALL_FLAGS_NONE, + 120000, cancellable_, connect_cb, this); +} + +void IWDPeer::disconnect() +{ + if (!connected_) + g_dbus_proxy_call(wsc_proxy_, "Cancel", NULL, G_DBUS_CALL_FLAGS_NONE, + 5000, cancellable_, cancel_cb, this); + + g_dbus_proxy_call(peer_proxy_, "Disconnect", NULL, G_DBUS_CALL_FLAGS_NONE, + 5000, cancellable_, disconnect_cb, this); +} + +const char *IWDPeer::get_str_property(GVariant *value, const GVariantType *type, const char *name) +{ + if (!value || !g_variant_is_of_type(value, type)) { + std::cout << "property " << name << " type wrong" << std::endl; + return NULL; + } + + return g_variant_get_string(value, NULL); +} + +bool IWDPeer::get_bool_property(GVariant *value, const char *name, bool *out) +{ + if (!value || !g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) { + std::cout << "property " << name << " type wrong" << std::endl; + return false; + } + + *out = g_variant_get_boolean(value); + return true; +} + +bool IWDPeer::get_uint16_property(GVariant *value, const char *name, int *out) +{ + if (!value || !g_variant_is_of_type(value, G_VARIANT_TYPE_UINT16)) { + std::cout << "property " << name << " type wrong" << std::endl; + return false; + } + + *out = g_variant_get_uint16(value); + return true; +} + +void IWDPeer::local_host_update(const char *ifname) +{ + struct ifaddrs *ifaddr, *ifa; + + if (getifaddrs(&ifaddr) == -1) { + std::cout << "can't get local IPs: " << strerror(errno) << std::endl; + return; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + void *addr; + char str[INET_ADDRSTRLEN]; + + if (strcmp(ifa->ifa_name, ifname)) + continue; + if (ifa->ifa_addr == NULL) + continue; + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + + addr = &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; + inet_ntop(AF_INET, addr, str, INET_ADDRSTRLEN); + local_host_ = std::string(str); + return; + } + + std::cout << "can't find local IP" << std::endl; +} + +void IWDPeer::dev_type_update(void) +{ + if (params_.source) + device_type_ = params_.sink ? DUAL_ROLE : SOURCE; + else if (params_.sink) + device_type_ = PRIMARY_SINK; + else + std::cout << "Device is neither a source nor a sink" << std::endl; +} + +void IWDPeer::check_available(void) +{ + if (observer_ && connected_ && !local_host_.empty() && !remote_host_.empty()) + observer_->on_availability_changed(this); +} + +void IWDPeer::peer_property_update(const char *name, GVariant *value) +{ + if (!strcmp(name, "Name")) { + const char *str_val = get_str_property(value, G_VARIANT_TYPE_STRING, "Name"); + if (!str_val) + return; + + name_changed(str_val); + } else if (!strcmp(name, "Device")) { + const char *str_val = get_str_property(value, G_VARIANT_TYPE_OBJECT_PATH, "Device"); + if (!str_val) + return; + + g_free(dev_path_); + dev_path_ = g_strdup(str_val); + } else if (!strcmp(name, "Connected")) { + bool was_available = is_available(); + bool bool_val; + + if (!get_bool_property(value, "Connected", &bool_val)) + return; + + connected_ = bool_val; + + if (was_available && !connected_) { + local_host_.clear(); + remote_host_.clear(); + + if (observer_) + observer_->on_availability_changed(this); + } else if (!was_available && connected_) + check_available(); + } else if (!strcmp(name, "ConnectedInterface")) { + bool was_available = is_available(); + const char *str_val = get_str_property(value, G_VARIANT_TYPE_STRING, "ConnectedInterface"); + if (!str_val) + return; + + local_host_update(str_val); + + if (!was_available) + check_available(); + } else if (!strcmp(name, "ConnectedIP")) { + bool was_available = is_available(); + const char *str_val = get_str_property(value, G_VARIANT_TYPE_STRING, "ConnectedIp"); + if (!str_val) + return; + + remote_host_ = std::string(str_val); + + if (!was_available) + check_available(); + } +} + +void IWDPeer::wfd_property_update(const char *name, GVariant *value) +{ + if (!strcmp(name, "Source")) { + if (!get_bool_property(value, "Source", ¶ms_.source)) + return; + + dev_type_update(); + } else if (!strcmp(name, "Sink")) { + if (!get_bool_property(value, "Sink", ¶ms_.sink)) + return; + + dev_type_update(); + } else if (!strcmp(name, "Port")) + get_uint16_property(value, "Port", &rtsp_port_); +} + +void IWDPeer::peer_properties_changed_cb(GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + auto peer = static_cast(user_data); + GVariantIter *iter; + const char *key; + GVariant *value; + + g_variant_get(changed_properties, "a{sv}", &iter); + while (g_variant_iter_loop(iter, "{&sv}", &key, &value)) + peer->peer_property_update(key, value); + g_variant_iter_free(iter); +} + +void IWDPeer::set_peer_proxy(GDBusProxy *proxy) +{ + if (peer_proxy_) + g_signal_handlers_disconnect_by_data(peer_proxy_, this); + + g_set_object(&peer_proxy_, proxy); + + if (peer_proxy_) { + char **props = g_dbus_proxy_get_cached_property_names(peer_proxy_); + + g_signal_connect(peer_proxy_, "g-properties-changed", + G_CALLBACK(peer_properties_changed_cb), this); + + for (char **name = props; *name; name++) { + GVariant *value = g_dbus_proxy_get_cached_property(peer_proxy_, *name); + peer_property_update(*name, value); + g_variant_unref(value); + } + g_strfreev(props); + + if (name_.empty()) { + std::cout << "peer name not found" << std::endl; + g_clear_object(&peer_proxy_); + } else if (!dev_path_) { + std::cout << "peer device path not found" << std::endl; + g_clear_object(&peer_proxy_); + } + } +} + +void IWDPeer::wfd_properties_changed_cb(GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + auto peer = static_cast(user_data); + GVariantIter *iter; + const char *key; + GVariant *value; + + g_variant_get(changed_properties, "a{sv}", &iter); + while (g_variant_iter_loop(iter, "{&sv}", &key, &value)) + peer->wfd_property_update(key, value); + g_variant_iter_free(iter); +} + +void IWDPeer::set_wfd_proxy(GDBusProxy *proxy) +{ + if (wfd_proxy_) + g_signal_handlers_disconnect_by_data(wfd_proxy_, this); + + g_set_object(&wfd_proxy_, proxy); + + if (wfd_proxy_) { + char **props = g_dbus_proxy_get_cached_property_names(wfd_proxy_); + + g_signal_connect(wfd_proxy_, "g-properties-changed", + G_CALLBACK(wfd_properties_changed_cb), this); + + for (char **name = props; *name; name++) { + GVariant *value = g_dbus_proxy_get_cached_property(wfd_proxy_, *name); + wfd_property_update(*name, value); + g_variant_unref(value); + } + g_strfreev(props); + + if (!params_.source && !params_.sink) { + std::cout << "couldn't determine device type" << std::endl; + g_clear_object(&wfd_proxy_); + } + } +} + +void IWDPeer::added(void) +{ + if (observer_) + observer_->on_initialized(this); +} + +void IWDPeer::removed(void) +{ + observer_ = NULL; +} + +} diff --git a/p2p/iwd-peer.h b/p2p/iwd-peer.h new file mode 100644 index 0000000..2762214 --- /dev/null +++ b/p2p/iwd-peer.h @@ -0,0 +1,77 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include +#include "peer.h" + +#ifndef IWD_PEER_H_ +#define IWD_PEER_H_ + +namespace P2P { + +class IWDPeer : public Peer { + public: + IWDPeer(const char *path); + virtual ~IWDPeer(); + + void connect() override; + void disconnect() override; + + bool is_available() const override { return connected_; } + void set_peer_proxy(GDBusProxy *proxy); + void set_wfd_proxy(GDBusProxy *proxy); + void set_wsc_proxy(GDBusProxy *proxy) { g_set_object(&wsc_proxy_, proxy); } + bool is_ready() const { return peer_proxy_ && wfd_proxy_ && wsc_proxy_; } + const char *get_path() const { return path_; } + const char *get_dev_path() const { return dev_path_; } + void added(void); + void removed(void); + + protected: + static void connect_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + static void cancel_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + static void disconnect_cb(GObject *object, GAsyncResult *res, gpointer data_ptr); + static const char *get_str_property(GVariant *value, const GVariantType *type, const char *name); + static bool get_bool_property(GVariant *value, const char *name, bool *out); + static bool get_uint16_property(GVariant *value, const char *name, int *out); + static void peer_properties_changed_cb(GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data); + static void wfd_properties_changed_cb(GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data); + + void local_host_update(const char *ifname); + void dev_type_update(void); + void check_available(void); + void peer_property_update(const char *name, GVariant *value); + void wfd_property_update(const char *name, GVariant *value); + + Parameters params_; + GDBusProxy *peer_proxy_; + GDBusProxy *wfd_proxy_; + GDBusProxy *wsc_proxy_; + char *path_; + char *dev_path_; + bool connected_; + GCancellable *cancellable_; +}; + +} +#endif // IWD_PEER_H_ From 73d6b5a753140cfdd919c53126c9658fdca74a26 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 19 Jul 2020 03:35:21 +0200 Subject: [PATCH 5/8] p2p: Add MultiClient To avoid forcing the source/sink apps to select between one client or another, add the MultiClient class, implementing P2P::Client, so that it can be a drop-in replacement of ConnmanClient that does the right thing depending on which of the two services are available on DBus. Under the hood it hold objects of both ConnmandClient and IWDClient and whichever detects its corresponding daemon is available for use, forwards any callbacks through MultiClient up to the app. --- p2p/multi-client.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 p2p/multi-client.h diff --git a/p2p/multi-client.h b/p2p/multi-client.h new file mode 100644 index 0000000..20f0ed5 --- /dev/null +++ b/p2p/multi-client.h @@ -0,0 +1,71 @@ +/* + * This file is part of Wireless Display Software for Linux OS + * + * Copyright (C) 2020 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef MULTI_CLIENT_H_ +#define MULTI_CLIENT_H_ + +#include "iwd-client.h" +#include "connman-client.h" + +namespace P2P { + +class MultiClient : public Client, public Client::Observer { + public: + MultiClient(const Parameters ¶ms, Client::Observer *observer = NULL) : Client(observer), iwd_(params, this), connman_(params, this) {} + ~MultiClient() {} + + void set_parameters(const Parameters ¶ms) override { iwd_.set_parameters(params); connman_.set_parameters(params); } + + bool is_available() const override { return iwd_.is_available() || connman_.is_available(); } + + void scan() override + { + if (iwd_.is_available()) + iwd_.scan(); + else + connman_.scan(); + } + + void on_peer_added(Client *client, std::shared_ptr peer) override + { + if (observer_) + observer_->on_peer_added(this, peer); + } + + void on_peer_removed(Client *client, std::shared_ptr peer) override + { + if (observer_) + observer_->on_peer_removed(this, peer); + } + + void on_availability_changed(Client *client) override + { + /* No extra check needed if we assume only one service is available at any time */ + if (observer_) + observer_->on_availability_changed(this); + } + protected: + IWDClient iwd_; + ConnmanClient connman_; +}; + +} +#endif // MULTI_CLIENT_H_ From bd92ddc378af8b367c91ee2e1f1f98b4a6cc73ee Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 19 Jul 2020 03:40:37 +0200 Subject: [PATCH 6/8] apps: Switch to MultiClient --- desktop_source/source-app.cpp | 6 +++--- p2p/main.cpp | 6 +++--- sink/sink-app.cpp | 6 +++--- sink/sink-app.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/desktop_source/source-app.cpp b/desktop_source/source-app.cpp index a33bcd4..28c5307 100644 --- a/desktop_source/source-app.cpp +++ b/desktop_source/source-app.cpp @@ -22,7 +22,7 @@ #include #include "source-app.h" -#include "connman-client.h" +#include "multi-client.h" void SourceApp::on_availability_changed(P2P::Client *client) { @@ -90,9 +90,9 @@ SourceApp::SourceApp(int port) : .session_management_control_port = (uint16_t) port, }; - // register the P2P service with connman + // register the P2P service with the DBus service in use std::cout << "* Registering Wi-Fi Display Source" << std::endl; - p2p_client_.reset(new P2P::ConnmanClient(params, this)); + p2p_client_.reset(new P2P::MultiClient(params, this)); source_.reset(new MiracBrokerSource(port)); } diff --git a/p2p/main.cpp b/p2p/main.cpp index 9767b29..dfb5865 100644 --- a/p2p/main.cpp +++ b/p2p/main.cpp @@ -23,7 +23,7 @@ #include #include -#include "connman-client.h" +#include "multi-client.h" #include "information-element.h" int main (int argc, const char **argv) @@ -42,9 +42,9 @@ int main (int argc, const char **argv) .sink = true, }; - // register the P2P service with connman + // register the P2P service with the DBus service in use std::cout << "Registering" << std::endl; - P2P::ConnmanClient p2p_client (params); + P2P::MultiClient p2p_client (params); g_main_loop_run (main_loop); g_main_loop_unref (main_loop); diff --git a/sink/sink-app.cpp b/sink/sink-app.cpp index 7895cb6..c5f4b11 100644 --- a/sink/sink-app.cpp +++ b/sink/sink-app.cpp @@ -24,7 +24,7 @@ #include "sink-app.h" #include "sink.h" -#include "connman-client.h" +#include "multi-client.h" void SinkApp::on_peer_added(P2P::Client *client, std::shared_ptr peer) { @@ -61,9 +61,9 @@ SinkApp::SinkApp(){ .sink = true, }; - // register the P2P service with connman + // register the P2P service with the DBus service in use std::cout << "* Registering Wifi Display" << std::endl; - p2p_client_.reset(new P2P::ConnmanClient(params, this)); + p2p_client_.reset(new P2P::MultiClient(params, this)); } SinkApp::SinkApp(const std::string& hostname, int port) diff --git a/sink/sink-app.h b/sink/sink-app.h index 16a8a51..6435a6e 100644 --- a/sink/sink-app.h +++ b/sink/sink-app.h @@ -25,7 +25,7 @@ #include #include "sink.h" -#include "connman-client.h" +#include "multi-client.h" class SinkApp: public P2P::Client::Observer, public P2P::Peer::Observer { public: From 9d8643e362f388b4a30d2d29c0b6b1213d057175 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 19 Jul 2020 03:43:26 +0200 Subject: [PATCH 7/8] Fix a use of an uninitialised value in MiracBroker --- mirac_network/mirac-broker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mirac_network/mirac-broker.cpp b/mirac_network/mirac-broker.cpp index 382494e..caedbce 100644 --- a/mirac_network/mirac-broker.cpp +++ b/mirac_network/mirac-broker.cpp @@ -178,6 +178,7 @@ std::string MiracBroker::get_peer_address() const } MiracBroker::MiracBroker (const std::string& listen_port): + connect_wait_id_(0), connect_timer_(NULL) { network_source_ptr_ = this; @@ -193,6 +194,7 @@ MiracBroker::MiracBroker (const std::string& listen_port): MiracBroker::MiracBroker(const std::string& peer_address, const std::string& peer_port, uint timeout): peer_address_(peer_address), peer_port_(peer_port), + connect_wait_id_(0), connect_timeout_(timeout) { network_source_ptr_ = this; From 5f04bd9e274cf0ae0649b17dc5ad18eec029f4fc Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Mon, 20 Jul 2020 03:44:20 +0200 Subject: [PATCH 8/8] Update README.md with mentions of IWD backend --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a023675..c504833 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,24 @@ WDS is a set of libraries for developers who want to build Wi-Fi Display applica WDS consists of: * libwds: Main library implements a Wi-Fi Display dialect of RTSP that includes the parser, actual negotiation logic for sink and source, and the related data structures. It is not tied to any specific connection manager, media framework or main loop. This Library is also compatible with MSVC. * network: Supports integration with GLib main loop and GStreamer - * p2p: Supports integration with Connman Wifi P2P features + * p2p: Supports integration with Connman and IWD Wifi P2P features The source code includes example implementations: - * _sink:_ Wi-Fi Display sink that depends on Gstreamer, Connman and GLib mainloop - * _desktop_source:_ Wi-Fi Display source that depends on Gstreamer, Connman and GLib mainloop + * _sink:_ Wi-Fi Display sink that depends on Gstreamer, Connman/IWD and GLib mainloop + * _desktop_source:_ Wi-Fi Display source that depends on Gstreamer, Connman/IWD and GLib mainloop More information can be found on the [mailing list](https://lists.01.org/mailman/listinfo/wysiwidi-dev) and the [wiki](https://github.com/01org/wds/wiki). ### Requirements: -WDS test executables have runtime dependencies on just a few things (mostly GStreamer and GLib), but for successful Wi-Fi Display sessions the following are adviced: +WDS test executables have runtime dependencies on just a few things (mostly GStreamer and GLib), but for successful Wi-Fi Display sessions the following are advised: * Wifi adapter from Intel 7260-family or Atheros ath9k - * [wpa_supplicant](http://w1.fi/wpa_supplicant/): version 2.4 or later, built with `CONFIG_P2P=y`, `CONFIG_WIFI_DISPLAY=y` and `CONFIG_CTRL_IFACE_DBUS_NEW=y` - * [connman](https://01.org/connman): version 1.28 (released Feb 1st 2015) or later. * gstreamer: either master branch more recent than Feb 3rd 2015 (commit d0a50be2), or 1.4 branch more recent than Feb 3rd 2005 (commit 1ce3260a638d or release 1.4.6 or later). + * either: [wpa_supplicant](http://w1.fi/wpa_supplicant/): version 2.4 or later, built with `CONFIG_P2P=y`, `CONFIG_WIFI_DISPLAY=y` and `CONFIG_CTRL_IFACE_DBUS_NEW=y` + * [connman](https://01.org/connman): version 1.28 (released Feb 1st 2015) or later. + * or: [IWD](https://iwd.wiki.kernel.org/) version 1.9+ -- alternative to connman and wpa_supplicant. Test results with other Wifi adapters are very welcome but be warned that in many cases Wifi-P2P has not had the testing it needs on linux: you may run into problems in surprising places. @@ -34,7 +35,7 @@ make ### Testing WDS -#### Pre-requisites +#### Pre-requisites (connman backend) Make sure wpa_supplicant & connmand are running. Running both of them uninstalled is possible (but in that case make sure the system wpa_supplicant and system connection manager are _not_ running):