efl_net_session and efl_net_control for ConnMan

These are objects to allow control of networking devices
(efl_net_control) as well as an application to request for
connectivity (efl_net_session).

They are loosely based on ConnMan.org, which we already use in
Enlightenment Window Manager via DBus access with Eldbus. However they
do not map 1:1 as the goal was to expose a viable subset of controls
but in a simple and general way, thus nome strings were converted to
enums, some arrays of strings were converted to bitwise flags, some
names were made more general, such as "service" was turned into
"access point" so it doesn't generate confusion with other "network
services" (ie: http server), or "favorite" that was renamed to
"remembered". Some behavior are slightly different (yet able to be
implemented on top), such as "Service.MoveBefore" and "MoveAfter" were
converted to a numeric "priority", calculated from service's list
index, changing the priority will reoder the list and thus generate
the MoveBefore and MoveAfter DBus commands.

ConnMan was chosen not only because we already use it, but because its
DBus API is sane and simple, with the server doing almost all that we
need. This is visible in the efl_net_session, which is completely done
in the server and do not require any extra work on our side -- aside
from talking DBus and converting to Eo, which is a major work :-D

   NOTE: ConnMan doesn't use FreeDesktop.Org DBus interfaces such as
         Properties and ObjectManager, thus we cannot use
         eldbus_model_object.

There are two examples added:

 - efl_net_session_example: monitors the connection available for an
   application and try to connect. You need a connman compiled with
   session_policy_local and a configuration file explained in
   https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt
   to get a connection if nothing is connected. Otherwise it will just
   monitor the connectivity state.

 - efl_net_control_example: monitors, plays the agent and configure
   the network details. It can enable/disable technologies, connect to
   access points (services) and configure them. It's quite extensive
   as allows testing all of ConnMan's DBus API except P2P (Peers).
This commit is contained in:
Gustavo Sverzut Barbieri 2016-09-15 21:43:19 -03:00
parent 5e53ecb9e6
commit 98fe627ca4
17 changed files with 6989 additions and 4 deletions

View File

@ -3052,6 +3052,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eet])
EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eina])
EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [ecore])
EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [emile])
EFL_INTERNAL_DEPEND_PKG([ECORE_CON], [eldbus])
EFL_ADD_LIBS([ECORE_CON], [-lm])

View File

@ -24,6 +24,10 @@ ecore_con_eolian_files = \
lib/ecore_con/efl_net_ssl_context.eo \
lib/ecore_con/efl_net_dialer_ssl.eo \
lib/ecore_con/efl_net_server_ssl.eo \
lib/ecore_con/efl_net_control_access_point.eo \
lib/ecore_con/efl_net_control_technology.eo \
lib/ecore_con/efl_net_control.eo \
lib/ecore_con/efl_net_session.eo \
lib/ecore_con/ecore_con_eet_base.eo \
lib/ecore_con/ecore_con_eet_server_obj.eo \
lib/ecore_con/ecore_con_eet_client_obj.eo \
@ -104,7 +108,13 @@ lib/ecore_con/efl_net_server_udp_client.c \
lib/ecore_con/efl_net_socket_ssl.c \
lib/ecore_con/efl_net_ssl_context.c \
lib/ecore_con/efl_net_dialer_ssl.c \
lib/ecore_con/efl_net_server_ssl.c
lib/ecore_con/efl_net_server_ssl.c \
lib/ecore_con/efl_net-connman.h \
lib/ecore_con/efl_net-connman.c \
lib/ecore_con/efl_net_control_access_point-connman.c \
lib/ecore_con/efl_net_control_technology-connman.c \
lib/ecore_con/efl_net_control-connman.c \
lib/ecore_con/efl_net_session-connman.c
EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c

View File

@ -61,3 +61,5 @@
/efl_net_socket_ssl_dialer_example
/efl_net_socket_ssl_server_example
/*.pem
/efl_net_session_example
/efl_net_control_example

View File

@ -88,7 +88,9 @@ efl_net_dialer_websocket_example \
efl_net_dialer_websocket_autobahntestee \
efl_net_dialer_udp_example \
efl_net_socket_ssl_dialer_example \
efl_net_socket_ssl_server_example
efl_net_socket_ssl_server_example \
efl_net_session_example \
efl_net_control_example
ECORE_COMMON_LDADD = \
@ -334,6 +336,12 @@ efl_net_socket_ssl_dialer_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_socket_ssl_server_example_SOURCES = efl_net_socket_ssl_server_example.c
efl_net_socket_ssl_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_session_example_SOURCES = efl_net_session_example.c
efl_net_session_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_control_example_SOURCES = efl_net_control_example.c
efl_net_control_example_LDADD = $(ECORE_CON_COMMON_LDADD)
SRCS = \
ecore_animator_example.c \
ecore_buffer_example.c \
@ -391,7 +399,9 @@ efl_net_dialer_websocket_example.c \
efl_net_dialer_websocket_autobahntestee.c \
efl_net_dialer_udp_example.c \
efl_net_socket_ssl_dialer_example.c \
efl_net_socket_ssl_server_example.c
efl_net_socket_ssl_server_example.c \
efl_net_session_example.c \
efl_net_control_example.c
%.pem:
echo -e "US\nOR\nPortland\nXPTO Ltd\n\nlocalhost\nroot@localhost\n" | openssl req -new -x509 -days 30 -nodes -out $@ -keyout $@

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
#define EFL_BETA_API_SUPPORT 1
#define EFL_EO_API_SUPPORT 1
#include <Ecore.h>
#include <Ecore_Con.h>
#include <Ecore_Getopt.h>
static int retval = EXIT_SUCCESS;
static const char *
_state_str(Efl_Net_Session_State state)
{
static const char *strs[] = {
[EFL_NET_SESSION_STATE_OFFLINE] = "offline",
[EFL_NET_SESSION_STATE_LOCAL] = "local",
[EFL_NET_SESSION_STATE_ONLINE] = "online",
};
if ((unsigned)state > sizeof(strs)/sizeof(strs[0])) return "???";
if (!strs[state]) return "???";
return strs[state];
}
static const char *
_technology_str(Efl_Net_Session_Technology tech)
{
static const char *strs[] = {
[EFL_NET_SESSION_TECHNOLOGY_UNKNOWN] = "none",
[EFL_NET_SESSION_TECHNOLOGY_ETHERNET] = "ethernet",
[EFL_NET_SESSION_TECHNOLOGY_WIFI] = "wifi",
[EFL_NET_SESSION_TECHNOLOGY_BLUETOOTH] = "bluetooth",
[EFL_NET_SESSION_TECHNOLOGY_CELLULAR] = "cellular",
[EFL_NET_SESSION_TECHNOLOGY_VPN] = "vpn",
[EFL_NET_SESSION_TECHNOLOGY_GADGET] = "gadget",
};
if ((unsigned)tech > sizeof(strs)/sizeof(strs[0])) return "???";
if (!strs[tech]) return "???";
return strs[tech];
}
static void
_changed(void *data EINA_UNUSED, const Efl_Event *event)
{
Eo *session = event->object;
const char *ip, *netmask, *gateway;
uint8_t prefix;
printf("INFO: session changed:\n"
"INFO: - name: '%s'\n"
"INFO: - state: %s\n"
"INFO: - technology: %s\n"
"INFO: - interface: '%s'\n",
efl_net_session_name_get(session),
_state_str(efl_net_session_state_get(session)),
_technology_str(efl_net_session_technology_get(session)),
efl_net_session_interface_get(session));
efl_net_session_ipv4_get(session, &ip, &netmask, &gateway);
if (ip)
{
printf("INFO: - IPv4: %s, gateway=%s, netmask=%s\n",
ip, gateway, netmask);
}
efl_net_session_ipv6_get(session, &ip, &prefix, &netmask, &gateway);
if (ip)
{
printf("INFO: - IPv6: %s/%hhu, gateway=%s, netmask=%s\n",
ip, prefix, gateway, netmask);
}
}
EFL_CALLBACKS_ARRAY_DEFINE(session_events_cbs,
{ EFL_NET_SESSION_EVENT_CHANGED, _changed });
static const Ecore_Getopt options = {
"efl_net_session_example", /* program name */
NULL, /* usage line */
"1", /* version */
"(C) 2016 Enlightenment Project", /* copyright */
"BSD 2-Clause", /* license */
/* long description, may be multiline and contain \n */
"Example of Efl_Net_Session to request or monitor a network session for an application.\n",
EINA_FALSE,
{
ECORE_GETOPT_STORE_TRUE('c', "connect", "Require a connection to the internet (See -o/--require-online)."),
ECORE_GETOPT_STORE_TRUE('o', "require-online", "When connecting (-c/--connect), require connection to the internet. Otherwise a local network connection is enough"),
ECORE_GETOPT_APPEND('t', "technology", "Bearer technologies to use when connecting (ethernet, wifi, bluetooth, cellular, vpn, gadget or all)", ECORE_GETOPT_TYPE_STR),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_SENTINEL
}
};
int
main(int argc, char **argv)
{
char *str;
Eina_List *techs = NULL;
Eina_Bool do_connect = EINA_FALSE;
Eina_Bool require_online = EINA_FALSE;
Eina_Bool quit_option = EINA_FALSE;
Efl_Net_Session_Technology technologies = EFL_NET_SESSION_TECHNOLOGY_ALL;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(do_connect),
ECORE_GETOPT_VALUE_BOOL(require_online),
ECORE_GETOPT_VALUE_LIST(techs),
/* standard block to provide version, copyright, license and help */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
ECORE_GETOPT_VALUE_NONE /* sentinel */
};
int args;
Eo *session;
ecore_init();
ecore_con_init();
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
{
fputs("ERROR: Could not parse command line options.\n", stderr);
retval = EXIT_FAILURE;
goto end;
}
if (quit_option) goto end;
if (techs)
{
technologies = 0;
EINA_LIST_FREE(techs, str)
{
if (0) {}
#define MAP(X) else if (strcasecmp(str, #X) == 0) technologies |= EFL_NET_SESSION_TECHNOLOGY_ ## X
MAP(ETHERNET);
MAP(WIFI);
MAP(BLUETOOTH);
MAP(CELLULAR);
MAP(VPN);
MAP(GADGET);
#undef MAP
else fprintf(stderr, "WARNING: unknown technology '%s' ignored.\n", str);
free(str);
}
}
session = efl_add(EFL_NET_SESSION_CLASS, ecore_main_loop_get(),
efl_event_callback_array_add(efl_added, session_events_cbs(), NULL));
if (!session)
{
fputs("ERROR: Could not create Efl.Net.Session object.\n", stderr);
retval = EXIT_FAILURE;
goto end;
}
if (do_connect)
{
printf("INFO: requesting a %s connection.\n", require_online ? "online" : "local");
efl_net_session_connect(session, require_online, technologies);
}
printf("INFO: the session will active while this application runs. Use ^C (Control + C) to close it\n");
ecore_main_loop_begin();
efl_del(session);
end:
ecore_con_shutdown();
ecore_shutdown();
return retval;
}

View File

@ -32,10 +32,14 @@
#include "efl_net_dialer_http.eo.h"
#include "efl_net_dialer_websocket.eo.h"
#include "efl_net_ssl_types.eot.h"
#include "efl_net_ssl_context.eo.h"
#include "efl_net_socket_ssl.eo.h"
#include "efl_net_dialer_ssl.eo.h"
#include "efl_net_server_ssl.eo.h"
#include "efl_net_control_technology.eo.h"
#include "efl_net_control_access_point.eo.h"
#include "efl_net_control.eo.h"
#include "efl_net_session.eo.h"

View File

@ -0,0 +1,109 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "efl_net-connman.h"
static Eldbus_Proxy *_efl_net_connman_manager;
static Eldbus_Connection *_efl_net_connman_conn;
static int _efl_net_connman_init_count;
static void
_efl_net_connman_manager_free(void *data EINA_UNUSED, const void *dead_ptr)
{
DBG("ConnMan Manager was freed");
/* handle programming errors where 'too many unrefs' happen */
if (dead_ptr == _efl_net_connman_manager)
_efl_net_connman_manager = NULL;
}
Eina_Bool
efl_net_connman_init(void)
{
Eldbus_Object *obj;
_efl_net_connman_init_count++;
if (_efl_net_connman_init_count > 1) return EINA_TRUE;
if (!eldbus_init())
{
ERR("could not init eldbus");
goto error;
}
_efl_net_connman_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
if (!_efl_net_connman_conn)
{
ERR("could not get DBus system connection");
goto error;
}
DBG("DBus unique name %s", eldbus_connection_unique_name_get(_efl_net_connman_conn));
obj = eldbus_object_get(_efl_net_connman_conn, "net.connman", "/");
if (!obj)
{
ERR("could not create DBus object for name='net.connman', path='/'");
goto error_obj;
}
_efl_net_connman_manager = eldbus_proxy_get(obj, "net.connman.Manager");
if (!_efl_net_connman_manager)
{
ERR("could not create DBus proxy for interface='net.connman.Manager', name='net.connman', path='/'");
goto error_proxy;
}
eldbus_proxy_free_cb_add(_efl_net_connman_manager, _efl_net_connman_manager_free, NULL);
DBG("ConnMan support initialized");
return EINA_TRUE;
error_proxy:
eldbus_object_unref(obj);
error_obj:
eldbus_connection_unref(_efl_net_connman_conn);
_efl_net_connman_conn = NULL;
error:
_efl_net_connman_init_count = 0;
return EINA_FALSE;
}
void
efl_net_connman_shutdown(void)
{
_efl_net_connman_init_count--;
if (_efl_net_connman_init_count > 0) return;
DBG("ConnMan support shutting down");
if (_efl_net_connman_manager)
{
Eldbus_Object *obj = eldbus_proxy_object_get(_efl_net_connman_manager);
eldbus_proxy_unref(_efl_net_connman_manager);
_efl_net_connman_manager = NULL;
eldbus_object_unref(obj);
}
if (_efl_net_connman_conn)
{
eldbus_connection_unref(_efl_net_connman_conn);
_efl_net_connman_conn = NULL;
}
eldbus_shutdown();
}
Eldbus_Connection *
efl_net_connman_connection_get(void)
{
return _efl_net_connman_conn;
}
Eldbus_Proxy *
efl_net_connman_manager_get(void)
{
return _efl_net_connman_manager;
}

View File

@ -0,0 +1,122 @@
#ifndef _EFL_NET_CONNMAN_H_
#define _EFL_NET_CONNMAN_H_ 1
#include "Ecore.h"
#include "ecore_con_private.h"
#include "Eldbus.h"
#define DEFAULT_TIMEOUT 10000.0
/**
* @file efl_net-connman.h
*
* Common infrastructure to create Efl_Net_Control and Efl_Net_Session
* based on ConnMan connection manager.
*
* @note Each connection manager that needs shared infra should create
* their own file!
*
* @internal
*/
/**
* Should be called from inside Efl.Object.constructor before using
* any of connman functions.
*
* @return #EINA_FALSE on errors, all other functions will be useless
* in this case. #EINA_TRUE on success.
*
* @internal
*/
Eina_Bool efl_net_connman_init(void);
/**
* Should be called from inside Efl.Object.destructor after done using
* all of connman functions.
*
* @internal
*/
void efl_net_connman_shutdown(void);
/**
* Returns the DBus connection shared by all objects.
*
* @internal
*/
Eldbus_Connection *efl_net_connman_connection_get(void);
/**
* Returns a singleton for ConnMan's Manager object and interface
* (proxy).
*
* @note call efl_net_connman_init() before using. There is no need to
* eldbus_proxy_ref() it, but if done should have a matching
* eldbus_proxy_unref(). After done, remember to
* efl_net_connman_shutdown().
*
* @internal
*/
Eldbus_Proxy *efl_net_connman_manager_get(void);
/**
* Given a Efl.Net.Control find a technology instance given its name.
*
* @internal
*/
Efl_Net_Control_Technology *efl_net_connman_control_find_technology_by_type(Efl_Net_Control *ctl, const char *tech_type);
/**
* Ask Efl.Net.Control to reload access point list.
*
* @internal
*/
void efl_net_connman_control_access_points_reload(Efl_Net_Control *ctl);
/**
* Creates a new Efl.Net.Control.Technology child of an
* Efl.Net.Control with path and properties
*
* @internal
*/
Efl_Net_Control_Technology *efl_net_connman_technology_new(Efl_Net_Control *parent, const char *path, Eldbus_Message_Iter *properties);
/**
* Get the path of the given technology.
*
* @internal
*/
const char *efl_net_connman_technology_path_get(Efl_Net_Control_Technology *tech);
/**
* Convert connman's type string to enum value.
*
* @internal
*/
Efl_Net_Control_Technology_Type efl_net_connman_technology_type_from_str(const char *tech_type);
/**
* Creates a new Efl.Net.Control.Access_Point child of an
* Efl.Net.Control with path and properties
*
* @internal
*/
Efl_Net_Control_Access_Point *efl_net_connman_access_point_new(Efl_Net_Control *parent, const char *path, Eldbus_Message_Iter *properties, unsigned int priority);
/**
* Get the path of the given access_point.
*
* @internal
*/
const char *efl_net_connman_access_point_path_get(Efl_Net_Control_Access_Point *ap);
/**
* Updates Efl.Net.Control.Access_Point properties
*
* @internal
*/
void efl_net_connman_access_point_update(Efl_Net_Control *ap, Eldbus_Message_Iter *properties, unsigned int priority);
#endif /* _EFL_NET_CONNMAN_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,213 @@
import eina_types;
import efl_net_control_access_point;
import efl_net_control_technology;
enum Efl.Net.Control.State {
[[Provides the global network connectivity state.
For fine grained details, use @Efl.Net.Control access points and
their state property.
@since 1.19
]]
offline, [[no access point is connected]]
local, [[at least one access point was connected and the internet connectio wasn't verified]]
online, [[at least one access point was connected and the internet was verified]]
}
enum Efl.Net.Control.Agent_Request_Input.Field {
[[Bitwise-able fields requested to the agent.
@since 1.19
]]
name = (1 << 0), [[Used for hidden WiFi access points. If ssid is present, this is an alternative to that.]]
ssid = (1 << 1), [[Used for hidden WiFi access points. If name is present, this is an alternative to that.]]
username = (1 << 2), [[Identity or username requested]]
passphrase = (1 << 3), [[password or passphrase requested]]
wps = (1 << 4), [[Use WPS authentication. If passphrase is present, this is an alternative to that.]]
}
struct Efl.Net.Control.Agent_Request_Input.Information {
[[Name-value information pair provided to the agent.
@since 1.19
]]
name: string; [[The information name, such as PreviousPassphrase, Host, Name...]]
value: string; [[The contents of the information]]
}
struct Efl.Net.Control.Agent_Request_Input {
[[Request input to the agent.
@since 1.19
]]
access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this request.]]
fields: Efl.Net.Control.Agent_Request_Input.Field; [[Bitwise OR of fields present in this request.]]
passphrase_type: string; [[Extra detail on the meaning for the passphrase field, such as wep, psk, response (IEEE802.X GTC/OTP), string...]]
informational: list<Efl.Net.Control.Agent_Request_Input.Information>; [[such as the previous passphrase, VPN host]]
}
struct Efl.Net.Control.Agent_Error {
[[Report error to the agent.
@since 1.19
]]
access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this error.]]
message: string; [[The error message.]]
}
struct Efl.Net.Control.Agent_Browser_Url {
[[Report to agent that it should open a browser at given URL.
@since 1.19
]]
access_point: Efl.Net.Control.Access_Point; [[The access point that triggered this request.]]
url: string; [[The URL to point the browser at.]]
}
class Efl.Net.Control (Efl.Loop_User) {
[[Control network connectivity.
This class and its children objects are only useful to implement
control of the network connectivity. If your application is only
interested in requesting access to the network, use the
\@Efl.Net.Session instead.
The network connectivity is defined on top of technologies that
provide access points. A technology can be "ethernet", "wifi",
"bluetooth" or something else. Ethernet will provide a single
access point, while "wifi" will expose zero or more access
points that can come and go.
Users willing to use access points are expected to monitor
"access_point,add" event to know when access points were
added. To know when they were deleted, "access_point,del" or an
@Efl.Net.Control.Access_Point "del" event. Finally
"access_points,changed" is relative to additions, deletions and
reordering of access point due changes in their priorities.
The backend system is responsible to remember connection details
such as passphrase, last connected access point and
priority. The user is NOT supposed to do that.
For ease of use, @.state tells if at least one access point is
online (verified connectivity), local (connected but unverified)
or offline.
Due safety reasons all radio transmissions may be disabled with
@.radios_offline property. This is usually called "airplane
mode" in some platforms.
@since 1.19
]]
events {
access_point,add: Efl.Net.Control.Access_Point; [[The given access point was added]]
access_point,del: Efl.Net.Control.Access_Point; [[The given access point will be deleted]]
access_points,changed; [[Access points were added, deleted or reordered.]]
technology,add: Efl.Net.Control.Technology; [[The given technology was added]]
technology,del: Efl.Net.Control.Technology; [[The given technology will be deleted]]
radios_offline,changed; [[Property @.radios_offline changed]]
state,changed; [[Property @.state changed]]
agent_released; [[Notifies we're not the agent anymore]]
agent_error: Efl.Net.Control.Agent_Error; [[Requires the error to be reported to the user]]
agent_browser_url: Efl.Net.Control.Agent_Browser_Url; [[Requires the user to visit a web page]]
agent_request_input: Efl.Net.Control.Agent_Request_Input; [[Requires the user to enter information in order to proceed, such as hidden SSID, passphrase, etc. After the user entered information, reply by calling @.agent_reply]]
}
methods {
@property radios_offline {
[[If $true disable all network technologies that use radio transmission, such as bluetooth and wifi. If $false, allows radios to be used.]]
get { }
set { }
values {
radios_offline: bool;
}
}
@property state {
[[Summary of network connectivity.
- offline means no connectivity;
- local means local connectivity, that is, the access
point is connected but couldn't reach the internet;
- online means verified connectivity.
]]
get { }
values {
state: Efl.Net.Control.State;
}
}
@property access_points {
[[The iterator of current access points.
The iterator is valid only before the function returns
to the main loop, by then, if the events
"access_point,add" or "access_point,del" are emitted,
the iterator will become invalid.
]]
get { }
values {
access_points: free(own(iterator<Efl.Net.Control.Access_Point>), eina_iterator_free);
}
}
@property technologies {
[[The iterator of current access points.
The iterator is valid only before the function returns
to the main loop, by then, if the events
"technology,add" or "technology,del" are emitted,
the iterator will become invalid.
]]
get { }
values {
technologies: free(own(iterator<Efl.Net.Control.Technology>), eina_iterator_free);
}
}
@property agent_enabled {
[[The agent is responsible for user interaction.
When enabled, the local process will become the agent
for user interaction, such as requesting passphrases,
asking the user to open a browser to do
web-authentication and report connection errors.
There can be a single agent in the system at a given
time, registering one will unregister the other and
special permissions may be required to become an agent.
An useful agent should monitor "agent_error",
"agent_browser_url" and "agent_request_input"
events. When input is requested, reply using
@.agent_reply.
]]
get { }
set { }
values {
agent_enabled: bool;
}
}
agent_reply {
[[If event "agent_request_input" was emitted, this will reply with the requested data]]
params {
name: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.name was present, this should contain the network name or the 'ssid' parameter should be used.]]
ssid: const(Eina.Slice)* @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.ssid was present, this should contain the network SSID or the 'name' parameter should be used.]]
username: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.username was present, this should contain the identity or username]]
passphrase: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.passphrase was present, this should contain the password or passphrase, more details on how it should be interpreted was given in Efl.Net.Control.Agent_Request_Input.passphrase_type.]]
wps: string @nullable; [[If @Efl.Net.Control.Agent_Request_Input.Field.wps was present, this should contain the WPS PIN or an empty string "" to use the WPS push button instead.]]
}
}
}
implements {
Efl.Object.destructor;
Efl.Object.constructor;
Efl.Object.finalize;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,463 @@
enum Efl.Net.Control.Access_Point.State {
[[Provides the access point state.
@since 1.19
]]
idle, [[nothing is happening with this access point]]
association, [[the access point is trying to associate itself, this is the first state after a connection attempt]]
configuration, [[the access point is configuring itself, such as DHCP]]
local, [[the access point is connected, but the internet connection wasn't validated]]
online, [[the access point is connected and the internet connected was validated]]
disconnect, [[the access point is disconnecting]]
failure, [[a connection attempt failed, @Efl.Net.Control.Access_Point.error will provide more details]]
}
enum Efl.Net.Control.Access_Point.Error {
[[The connection error reason.
@since 1.19
]]
none, [[all right, no errors]]
out_of_range, [[wireless device is out of range]]
pin_missing, [[PIN was required and is missing]]
dhcp_failed, [[DHCP failed to provide configuration]]
connect_failed, [[couldn't connect to access point]]
login_failed, [[login or authentication information was incorrect, agent_request_input event may be emitted]]
}
enum Efl.Net.Control.Access_Point.Security {
[[Bitwise-able securities supported by an access point.
@since 1.19
]]
unknow = 0,
none = (1 << 0), [[open access, no security]]
wep = (1 << 1), [[WEP]]
psk = (1 << 2), [[PSK (Pre Shared Key), such as WPA or RSN]]
ieee802_1x = (1 << 3), [[IEEE 802.1X]]
}
enum Efl.Net.Control.Access_Point.Ipv4_Method {
[[The method used to configure IPv4
@since 1.19
]]
off, [[IPv4 is disabled]]
dhcp, [[IPv4 is configured using DHCP]]
manual, [[IPv4 is manually set using address, netmask and gateway]]
unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_ipv4]]
}
enum Efl.Net.Control.Access_Point.Ipv6_Method {
[[The method used to configure IPv6
@since 1.19
]]
off, [[IPv6 is disabled]]
fixed, [[IPv6 is fixed by operator and cannot be changed]]
manual, [[IPv6 is manually set using address, netmask and gateway]]
auto_privacy_none, [[IPv6 is set using dhcp or using a tunnel6to4, no privacy extensions should be used]]
auto_privacy_public, [[IPv6 is set using dhcp or using a tunnel6to4, privacy extensions are used and the system prefers a public IP address over temporary addresses]]
auto_privacy_temporary, [[IPv6 is set using dhcp or using a tunnel6to4, privacy extensions are used and the system prefers a temporary IP address over public addresses]]
tunnel6to4, [[IPv6 was configured using a 6-to-4 tunnel. This cannot be set by the user, which is expected to set "auto" instead]]
unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_ipv6]]
}
enum Efl.Net.Control.Access_Point.Proxy_Method {
[[The method used to configure Proxy.
@since 1.19
]]
off, [[direct connection to the internet, no proxy to be used]]
auto, [[proxy is autoconfigured using Proxy-Auto-Configuration (PAC) using given URL]]
manual, [[proxy is configured manually using servers and excludes]]
unset, [[Only to be used with @Efl.Net.Control.Access_Point.configuration_proxy]]
}
class Efl.Net.Control.Access_Point (Efl.Loop_User) {
[[An access point for network connectivity.
The @Efl.Net.Control is composed of multiple technologies, each
create access points to allow configuration and connection.
An application willing to just get a network connection should
prefer to use the \@Efl.Net.Session instead.
@since 1.19
]]
events {
changed; [[Some properties were changed.]]
}
methods {
connect {
[[Connect to this access point.
This connection will happen asynchronously in the
background, with results being delivered by events in
the access point object, such as the "changed".
Successful connections will remember the device and set
it to auto-connect using the property @.auto_connect.
See @.forget, @.remembered, @.auto_connect and
@.disconnect
The future may fail with non-fatal errors such as
EINPROGRESS (the connection was already ongoing) and
EALREADY (the connection was already stablished).
]]
return: future<void_ptr>; /* NOTE: This should be future<void> */
}
disconnect {
[[Disconnect from this access point.
When disconnected a previously connected access point it
won't be forgotten. The configuration and other details
such as priority and passphrase will be available for a
future re-connection with call to @.connect. If it is
desired to disconnect and forget all access point
information, use @.forget instead.
]]
}
forget {
[[Disconnect and forget about this access point.
Successful @.connect will always remember the access
point for future re-connections. This method reverts
that by disconnecting and forgetting about the access
point and its configuration, which will set the
@.remembered to $false.
]]
}
@property state {
[[The current state of this access point.
Whenever the state changes, "changed" will be emitted.
]]
get { }
values {
state: Efl.Net.Control.Access_Point.State;
}
}
@property error {
[[If the access point is in error state, this states the error.]]
get { }
values {
error: Efl.Net.Control.Access_Point.Error;
}
}
@property name {
[[The user-friendly access point name.
For hidden WiFi networks, this is empty.
]]
get { }
values {
name: string;
}
}
@property priority {
[[The access point priority in the current access point listing.
This property is dynamic and reflects the index of the
access point in the current access points list. As
access points may come and go, the value may change at
any time and notified with "changed" event.
If set, then it will reoder priorities, moving all other
services at equal or higher priority up. To move as the
first (most priority), then use 0. To move as the last
priority, use UINT32_MAX or the last known priority + 1.
\@note Only remembered access points may be reordered
among themselves. Those that are not remembered will
always come last in random order defined by the backend.
]]
get { }
set { }
values {
priority: uint;
}
}
@property technology {
[[The technology that generated this access point]]
get { }
values {
technology: Efl.Net.Control.Technology;
}
}
@property strength {
[[Signal strength percentage in 0-100]]
get { }
values {
strength: uint8;
}
}
@property roaming {
[[If it's a cellular access point and it's on roaming.]]
get { }
values {
roaming: bool;
}
}
@property auto_connect {
[[Whenever to auto-connect to this access point if no other is connected.
By default successfully connected access points are
remembered and set to auto-connect. This behavior can be
changed with this property.
An access point is only connected automatically if there
are no other connected and if it is not on roaming.
]]
get { }
set { }
values {
auto_connect: bool;
}
}
@property remembered {
[[Successfully connected access points are remembered.
To forget about this access point, call @.forget.
]]
get { }
values {
remembered: bool;
}
}
@property immutable {
[[Immutable access points are those defined in configuration files and its properties can't be changed using API.]]
get { }
values {
immutable: bool;
}
}
@property security {
[[Security options such as wep, wps, psk or none (open).]]
get { }
values {
security: Efl.Net.Control.Access_Point.Security; [[Bitwise OR of security supported by this access point]]
}
}
@property name_servers {
[[DNS (Domain Name Servers) in use for this access point.
These are the actual values in use, configure them using
@.configuration_name_servers, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
name_servers: free(own(iterator<string>), eina_iterator_free);
}
}
@property time_servers {
[[NTP (Time Server) in use for this access point.
These are the actual values in use, configure them using
@.configuration_time_servers, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
time_servers: free(own(iterator<string>), eina_iterator_free);
}
}
@property domains {
[[Search domains in use for this access point.
These are the actual values in use, configure them using
@.configuration_domains, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
domains: free(own(iterator<string>), eina_iterator_free);
}
}
@property ipv4 {
[[IPv4 in use for this access point.
These are the actual values in use, configure them using
@.configuration_ipv4, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
method: Efl.Net.Control.Access_Point.Ipv4_Method;
address: string;
netmask: string;
gateway: string;
}
}
@property ipv6 {
[[IPv6 in use for this access point.
These are the actual values in use, configure them using
@.configuration_ipv6, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
method: Efl.Net.Control.Access_Point.Ipv6_Method;
address: string;
prefix_length: uint8;
netmask: string;
gateway: string;
}
}
@property proxy {
[[Proxy in use for this access point.
These are the actual values in use, configure them using
@.configuration_proxy, which may generate change
to this property, being notified with the "changed"
event.
]]
get { }
values {
method: Efl.Net.Control.Access_Point.Proxy_Method;
url: string; [[if @Efl.Net.Control.Access_Point.Proxy_Method.auto, then states the URL to use for proxy auto-configuration]]
servers: free(own(iterator<string>), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the URI with proxy servers to use, like "http://proxy.domain.com:911"]]
excludes: free(own(iterator<string>), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the hosts or patterns to exclude from proxy access, such as "localhost", ".domain.com", or "10.0.0.0..."]]
}
}
@property configuration_name_servers {
[[DNS (Domain Name Servers) configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.name_servers property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
name_servers: free(own(iterator<string>), eina_iterator_free);
}
}
@property configuration_time_servers {
[[NTP (Time Server) configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.time_servers property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
time_servers: free(own(iterator<string>), eina_iterator_free);
}
}
@property configuration_domains {
[[Search domains configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.domains property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
domains: free(own(iterator<string>), eina_iterator_free);
}
}
@property configuration_ipv4 {
[[IPv4 configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.ipv4 property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
method: Efl.Net.Control.Access_Point.Ipv4_Method;
address: string;
netmask: string;
gateway: string;
}
}
@property configuration_ipv6 {
[[IPv6 configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.ipv6 property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
method: Efl.Net.Control.Access_Point.Ipv6_Method;
address: string;
prefix_length: uint8;
netmask: string;
gateway: string;
}
}
@property configuration_proxy {
[[Proxy configured by user for this access point.
These are the user configured values, that will be
applied by the backend system and in turn may result in
"changed" event to notify of @.proxy property
with the actual value in use, which may differ from
this.
]]
set { }
get { }
values {
method: Efl.Net.Control.Access_Point.Proxy_Method;
url: string; [[if @Efl.Net.Control.Access_Point.Proxy_Method.auto, then states the URL to use for proxy auto-configuration]]
servers: free(own(iterator<string>), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the URI with proxy servers to use, like "http://proxy.domain.com:911"]]
excludes: free(own(iterator<string>), eina_iterator_free); [[If @Efl.Net.Control.Access_Point.Proxy_Method.manual, then states the hosts or patterns to exclude from proxy access, such as "localhost", ".domain.com", or "10.0.0.0..."]]
}
}
}
implements {
Efl.Object.destructor;
}
}

View File

@ -0,0 +1,472 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net-connman.h"
typedef struct
{
/* Eldbus_Proxy/Eldbus_Object keeps a list of pending calls, but
* they are reference counted singletons and will only cancel
* pending calls when everything is gone. However we operate on
* our private data, that may be gone before other refs. So
* keep the pending list.
*/
Eina_List *pending;
Eina_List *signal_handlers;
Eldbus_Proxy *proxy;
Eina_Stringshare *path;
Eina_Stringshare *name;
struct {
Eina_Stringshare *identifier;
Eina_Stringshare *passphrase;
Eina_Bool enabled;
} tethering;
Efl_Net_Control_Technology_Type type;
Eina_Bool powered;
Eina_Bool connected;
} Efl_Net_Control_Technology_Data;
#define MY_CLASS EFL_NET_CONTROL_TECHNOLOGY_CLASS
static void
_efl_net_control_technology_property_powered_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
Eina_Bool powered;
if (!eldbus_message_iter_arguments_get(value, "b", &powered))
{
ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (pd->powered == powered) return;
pd->powered = powered;
DBG("powered=%hhu", powered);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_connected_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
Eina_Bool connected;
if (!eldbus_message_iter_arguments_get(value, "b", &connected))
{
ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (pd->connected == connected) return;
pd->connected = connected;
DBG("connected=%hhu", connected);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_name_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
const char *name;
if (!eldbus_message_iter_arguments_get(value, "s", &name))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (!eina_stringshare_replace(&pd->name, name)) return;
DBG("name=%s", name);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
Efl_Net_Control_Technology_Type
efl_net_connman_technology_type_from_str(const char *str)
{
const struct {
Efl_Net_Control_Technology_Type val;
const char *str;
} *itr, map[] = {
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_UNKNOWN, "unknown"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_SYSTEM, "system"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_ETHERNET, "ethernet"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_WIFI, "wifi"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_BLUETOOTH, "bluetooth"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_CELLULAR, "cellular"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_GPS, "gps"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_VPN, "vpn"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_GADGET, "gadget"},
{EFL_NET_CONTROL_TECHNOLOGY_TYPE_P2P, "p2p"},
{ }
};
for (itr = map; itr->str != NULL; itr++)
if (strcmp(itr->str, str) == 0) return itr->val;
ERR("Unknown type '%s'", str);
return EFL_NET_CONTROL_TECHNOLOGY_TYPE_UNKNOWN;
}
static void
_efl_net_control_technology_property_type_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
const char *str;
Efl_Net_Control_Technology_Type type;
if (!eldbus_message_iter_arguments_get(value, "s", &str))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(value));
return;
}
type = efl_net_connman_technology_type_from_str(str);
if (pd->type == type) return;
pd->type = type;
DBG("type=%d (%s)", type, str);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_tethering_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
Eina_Bool tethering;
if (!eldbus_message_iter_arguments_get(value, "b", &tethering))
{
ERR("Expected boolean, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (pd->tethering.enabled == tethering) return;
pd->tethering.enabled = tethering;
DBG("tethering=%hhu", tethering);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_tethering_identifier_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
const char *tethering_identifier;
if (!eldbus_message_iter_arguments_get(value, "s", &tethering_identifier))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (!eina_stringshare_replace(&pd->tethering.identifier, tethering_identifier)) return;
DBG("tethering identifier=%s", tethering_identifier);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_tethering_passphrase_changed(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *value)
{
const char *tethering_passphrase;
if (!eldbus_message_iter_arguments_get(value, "s", &tethering_passphrase))
{
ERR("Expected string, got %s", eldbus_message_iter_signature_get(value));
return;
}
if (!eina_stringshare_replace(&pd->tethering.passphrase, tethering_passphrase)) return;
DBG("tethering passphrase=%s", tethering_passphrase);
efl_event_callback_call(o, EFL_NET_CONTROL_TECHNOLOGY_EVENT_CHANGED, NULL);
}
static void
_efl_net_control_technology_property_changed_internal(Eo *o, Efl_Net_Control_Technology_Data *pd, Eldbus_Message_Iter *itr)
{
Eldbus_Message_Iter *value;
const char *name;
if (!eldbus_message_iter_arguments_get(itr, "sv", &name, &value))
{
ERR("Unexpected signature: %s", eldbus_message_iter_signature_get(itr));
return;
}
if (strcmp(name, "Powered") == 0)
_efl_net_control_technology_property_powered_changed(o, pd, value);
else if (strcmp(name, "Connected") == 0)
_efl_net_control_technology_property_connected_changed(o, pd, value);
else if (strcmp(name, "Name") == 0)
_efl_net_control_technology_property_name_changed(o, pd, value);
else if (strcmp(name, "Type") == 0)
_efl_net_control_technology_property_type_changed(o, pd, value);
else if (strcmp(name, "Tethering") == 0)
_efl_net_control_technology_property_tethering_changed(o, pd, value);
else if (strcmp(name, "TetheringIdentifier") == 0)
_efl_net_control_technology_property_tethering_identifier_changed(o, pd, value);
else if (strcmp(name, "TetheringPassphrase") == 0)
_efl_net_control_technology_property_tethering_passphrase_changed(o, pd, value);
else
WRN("Unknown property name: %s", name);
}
static void
_efl_net_control_technology_property_changed(void *data, const Eldbus_Message *msg)
{
Eo *o = data;
Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *itr;
itr = eldbus_message_iter_get(msg);
_efl_net_control_technology_property_changed_internal(o, pd, itr);
}
EOLIAN static void
_efl_net_control_technology_efl_object_destructor(Eo *o, Efl_Net_Control_Technology_Data *pd)
{
Eldbus_Pending *p;
Eldbus_Signal_Handler *sh;
EINA_LIST_FREE(pd->pending, p)
eldbus_pending_cancel(p);
EINA_LIST_FREE(pd->signal_handlers, sh)
eldbus_signal_handler_del(sh);
if (pd->proxy)
{
Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy);
eldbus_proxy_unref(pd->proxy);
pd->proxy = NULL;
eldbus_object_unref(obj);
}
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->path, NULL);
eina_stringshare_replace(&pd->name, NULL);
eina_stringshare_replace(&pd->tethering.identifier, NULL);
eina_stringshare_replace(&pd->tethering.passphrase, NULL);
}
static void
_efl_net_control_technology_property_set_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
pd->pending = eina_list_remove(pd->pending, pending);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not set property %p: %s=%s", o, err_name, err_msg);
return;
}
}
static void
_efl_net_control_technology_property_set(Eo *o, Efl_Net_Control_Technology_Data *pd, const char *name, const char *signature, ...)
{
Eldbus_Message *msg;
Eldbus_Message_Iter *msg_itr, *var;
Eldbus_Pending *p;
va_list ap;
msg = eldbus_proxy_method_call_new(pd->proxy, "SetProperty");
EINA_SAFETY_ON_NULL_RETURN(msg);
msg_itr = eldbus_message_iter_get(msg);
EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send);
eldbus_message_iter_basic_append(msg_itr, 's', name);
var = eldbus_message_iter_container_new(msg_itr, 'v', signature);
va_start(ap, signature);
eldbus_message_iter_arguments_vappend(var, signature, ap);
va_end(ap);
eldbus_message_iter_container_close(msg_itr, var);
p = eldbus_proxy_send(pd->proxy, msg, _efl_net_control_technology_property_set_cb, o, DEFAULT_TIMEOUT);
EINA_SAFETY_ON_NULL_GOTO(p, error_send);
pd->pending = eina_list_append(pd->pending, p);
DBG("Setting property %s", name);
return;
error_send:
eldbus_message_unref(msg);
}
EOLIAN static void
_efl_net_control_technology_powered_set(Eo *o, Efl_Net_Control_Technology_Data *pd, Eina_Bool powered)
{
_efl_net_control_technology_property_set(o, pd, "Powered", "b", powered);
}
EOLIAN static Eina_Bool
_efl_net_control_technology_powered_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd)
{
return pd->powered;
}
EOLIAN static void
_efl_net_control_technology_tethering_set(Eo *o, Efl_Net_Control_Technology_Data *pd, Eina_Bool enabled, const char *identifier, const char *passphrase)
{
if (passphrase)
_efl_net_control_technology_property_set(o, pd, "TetheringPassphrase", "s", passphrase);
if (identifier)
_efl_net_control_technology_property_set(o, pd, "TetheringIdentifier", "s", identifier);
_efl_net_control_technology_property_set(o, pd, "Tethering", "b", enabled);
}
EOLIAN static void
_efl_net_control_technology_tethering_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd, Eina_Bool *enabled, const char **identifier, const char **passphrase)
{
if (enabled) *enabled = pd->tethering.enabled;
if (identifier) *identifier = pd->tethering.identifier;
if (passphrase) *passphrase = pd->tethering.passphrase;
}
EOLIAN static Eina_Bool
_efl_net_control_technology_connected_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd)
{
return pd->connected;
}
EOLIAN static const char *
_efl_net_control_technology_name_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd)
{
return pd->name;
}
EOLIAN static Efl_Net_Control_Technology_Type
_efl_net_control_technology_type_get(Eo *o EINA_UNUSED, Efl_Net_Control_Technology_Data *pd)
{
return pd->type;
}
static void
_efl_net_control_technology_scan_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Efl_Promise *promise = data;
Eo *o = efl_parent_get(promise);
Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
EINA_SAFETY_ON_NULL_RETURN(pd);
pd->pending = eina_list_remove(pd->pending, pending);
efl_key_data_set(promise, "eldbus_pending", NULL);
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
Eina_Error err = EINVAL;
if (strcmp(err_name, "net.connman.Error.NotSupported") == 0)
err = ENOTSUP;
WRN("Could not Scan %p: %s=%s", o, err_name, err_msg);
efl_promise_failed_set(promise, err);
return;
}
efl_promise_value_set(promise, o, NULL);
efl_del(promise);
}
static void
_efl_net_control_technology_scan_promise_del(void *data, const Efl_Event *event)
{
Eldbus_Pending *p;
Eo *o = data;
Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS);
EINA_SAFETY_ON_NULL_RETURN(pd);
p = efl_key_data_get(event->object, "eldbus_pending");
if (!p) return; /* already gone, nothing to do */
pd->pending = eina_list_remove(pd->pending, p);
DBG("cancel pending scan %p", p);
eldbus_pending_cancel(p);
}
EOLIAN static Efl_Future *
_efl_net_control_technology_scan(Eo *o, Efl_Net_Control_Technology_Data *pd)
{
Eldbus_Pending *p;
Efl_Promise *promise;
promise = efl_add(EFL_PROMISE_CLASS, o,
efl_event_callback_add(efl_added, EFL_EVENT_DEL, _efl_net_control_technology_scan_promise_del, o));
EINA_SAFETY_ON_NULL_RETURN_VAL(promise, NULL);
p = eldbus_proxy_call(pd->proxy, "Scan",
_efl_net_control_technology_scan_cb, promise, -1.0, "");
EINA_SAFETY_ON_NULL_GOTO(p, error_dbus);
pd->pending = eina_list_append(pd->pending, p);
efl_key_data_set(promise, "eldbus_pending", p);
return efl_promise_future_get(promise);
error_dbus:
efl_promise_failed_set(promise, ENOSYS);
return promise;
}
const char *
efl_net_connman_technology_path_get(Efl_Net_Control_Technology *o)
{
Efl_Net_Control_Technology_Data *pd = efl_data_scope_get(o, MY_CLASS);
EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
return pd->path;
}
Efl_Net_Control_Technology *
efl_net_connman_technology_new(Efl_Net_Control *ctl, const char *path, Eldbus_Message_Iter *itr)
{
Eo *o;
Efl_Net_Control_Technology_Data *pd;
Eldbus_Message_Iter *entry;
Eldbus_Connection *conn;
Eldbus_Object *obj;
conn = efl_net_connman_connection_get();
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
o = efl_add(MY_CLASS, ctl);
EINA_SAFETY_ON_NULL_RETURN_VAL(o, NULL);
pd = efl_data_scope_get(o, MY_CLASS);
EINA_SAFETY_ON_NULL_GOTO(pd, error);
pd->path = eina_stringshare_add(path);
EINA_SAFETY_ON_NULL_GOTO(pd->path, error);
obj = eldbus_object_get(conn, "net.connman", pd->path);
EINA_SAFETY_ON_NULL_GOTO(obj, error);
pd->proxy = eldbus_proxy_get(obj, "net.connman.Technology");
EINA_SAFETY_ON_NULL_GOTO(pd->proxy, error);
#define SH(sig, cb) \
do { \
Eldbus_Signal_Handler *sh = eldbus_proxy_signal_handler_add(pd->proxy, sig, cb, o); \
if (sh) pd->signal_handlers = eina_list_append(pd->signal_handlers, sh); \
else ERR("could not add DBus signal handler %s", sig); \
} while (0)
SH("PropertyChanged", _efl_net_control_technology_property_changed);
#undef SH
efl_event_freeze(o);
while (eldbus_message_iter_get_and_next(itr, 'e', &entry))
_efl_net_control_technology_property_changed_internal(o, pd, entry);
efl_event_thaw(o);
return o;
error:
efl_del(o);
return NULL;
}
#include "efl_net_control_technology.eo.c"

View File

@ -0,0 +1,106 @@
enum Efl.Net.Control.Technology.Type {
[[Technology types
@since 1.19
]]
unknown,
system,
ethernet,
wifi,
bluetooth,
cellular,
gps,
vpn,
gadget,
p2p,
}
class Efl.Net.Control.Technology (Efl.Loop_User) {
[[A technology that enables network access points to be controlled.
The @Efl.Net.Control is composed of multiple technologies, each
can be disabled/enabled with @.powered property.
When powered, the technology will dynamically add and delete
access points to be available in
@Efl.Net.Control.access_points.
@since 1.19
]]
events {
changed; [[Some properties were changed.]]
}
methods {
@property powered {
[[If $true the technology is available. If $false technology is disabled and if possible rfkill is used.]]
get { }
set { }
values {
powered: bool;
}
}
@property tethering {
[[Tethering allows the default access point to be bridged to all clients connected through the technology.
A common case is to use the device as a router, such as
a phone doing tethering allows a laptop to connect to 4G
network.
]]
get { }
set { }
values {
enabled: bool; [[Whenever to enable or disable tethering for this technology]]
identifier: string @optional; [[The name to identify this tethering, in WiFi it will translate to SSID.]]
passphrase: string @optional; [[The passphrase for this tethering access, in WiFi it will translate to WPA passphrase.]]
}
}
@property connected {
[[If $true the technology has at least one access point connected.]]
get { }
values {
connected: bool;
}
}
@property name {
[[The user-friendly technology name]]
get { }
values {
name: string;
}
}
@property type {
[[The technology type, such as "ethernet" or "wifi"]]
get { }
values {
type: Efl.Net.Control.Technology.Type;
}
}
scan {
[[Explicitly trigger a scan.
The scan will happen asynchronously in the background,
with the results being delivered by events in the
technology, @Efl.Net.Control or @Efl.Net.Control.Access_Point
associated with the technology. For example, scan on
WiFi will add and delete access points.
It is worth to mention that explicit scans should be
avoided. Rare cases are when user requested them, like
entering a configuration dialog which demands fresh
state. Otherwise prefer to let the system passively do
scans in a timely manner.
]]
return: future<void_ptr>; /* NOTE: This should be future<void> */
}
}
implements {
Efl.Object.destructor;
}
}

View File

@ -0,0 +1,792 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net-connman.h"
typedef struct
{
Eldbus_Proxy *proxy; /* net.connman.Session */
Eldbus_Service_Interface *notifier; /* net.connman.Notification */
Eldbus_Pending *mgr_pending; /* on efl_net_connman_manager_get(), local proxy doesn't need it, done automatically */
struct {
Eldbus_Pending *pending;
Eina_Bool connected; /* if should be connected or not */
Eina_Bool online_required;
Efl_Net_Session_Technology technologies_allowed;
} connect;
/* properties notified by session, local cache */
Eina_Stringshare *name;
Eina_Stringshare *interface;
struct {
Eina_Stringshare *address;
Eina_Stringshare *netmask;
Eina_Stringshare *gateway;
} ipv4;
struct {
Eina_Stringshare *address;
Eina_Stringshare *netmask;
Eina_Stringshare *gateway;
uint8_t prefix_length;
} ipv6;
Efl_Net_Session_State state;
Efl_Net_Session_Technology technology;
} Efl_Net_Session_Data;
#define MY_CLASS EFL_NET_SESSION_CLASS
/* will SET BIT for technology, start with '0' for multiple techs */
static Eina_Bool
_efl_net_session_technology_from_str(const char *str, Efl_Net_Session_Technology *tech)
{
if (0) { }
#define MAP(X, s) \
else if (strcmp(str, s) == 0) *tech |= EFL_NET_SESSION_TECHNOLOGY_ ## X
MAP(ALL, "*");
MAP(ETHERNET, "ethernet");
MAP(WIFI, "wifi");
MAP(BLUETOOTH, "bluetooth");
MAP(CELLULAR, "cellular");
MAP(VPN, "vpn");
MAP(GADGET, "gadget");
#undef MAP
else if (str[0]) /* empty bearer = no technology */
{
WRN("Unknown technology name: %s", str);
return EINA_FALSE;
}
return EINA_TRUE;
}
static void _efl_net_session_connect_do(Eo *o, Efl_Net_Session_Data *pd);
/* NOTE: unlike most DBus servers where you create paths using
* ObjectManager and monitor properties on them using PropertyManager,
* ConnMan doesn't use any of those and their API for Session is
* different from all others in ConnMan itself:
*
* 1 - create a local service 'notifier' implementing
* net.connman.Notification, with method Release() and
* Update(dict settings).
*
* 2 - call / CreateSession(dict settings, path notifier), get an
* object path representing your session
*
* 3 - call Change(setting, value), Connect(), Disconnect() on
* object path returned on #2
*
* 4 - get Update() to be called on your local service notifier specified
* on step #1.
*/
static Eldbus_Message *
_efl_net_session_notifier_release(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
DBG("Session %p is released %s", o, eldbus_message_path_get(msg));
return eldbus_message_method_return_new(msg);
}
static Eina_Error
_efl_net_session_notifier_update_state(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
const char *str;
if (!eldbus_message_iter_arguments_get(var, "s", &str))
return EINVAL;
if (strcmp(str, "disconnected") == 0)
pd->state = EFL_NET_SESSION_STATE_OFFLINE;
else if (strcmp(str, "connected") == 0)
pd->state = EFL_NET_SESSION_STATE_LOCAL;
else if (strcmp(str, "online") == 0)
pd->state = EFL_NET_SESSION_STATE_ONLINE;
else
return EINVAL;
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_name(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
const char *str;
if (!eldbus_message_iter_arguments_get(var, "s", &str))
return EINVAL;
eina_stringshare_replace(&pd->name, str);
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_technology(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
const char *str;
if (!eldbus_message_iter_arguments_get(var, "s", &str))
return EINVAL;
pd->technology = 0;
if (!_efl_net_session_technology_from_str(str, &pd->technology))
return EINVAL;
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_interface(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
const char *str;
if (!eldbus_message_iter_arguments_get(var, "s", &str))
return EINVAL;
eina_stringshare_replace(&pd->interface, str);
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_ipv4(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
Eldbus_Message_Iter *sub, *entry;
if (!eldbus_message_iter_arguments_get(var, "a{sv}", &sub))
return EINVAL;
while (eldbus_message_iter_get_and_next(sub, 'e', &entry))
{
const void *key;
Eldbus_Message_Iter *value;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &value))
{
ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry));
continue;
}
if (strcmp(key, "Method") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("configuration method %s", str);
}
else if (strcmp(key, "Address") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("address %s", str);
eina_stringshare_replace(&pd->ipv4.address, str);
}
else if (strcmp(key, "Gateway") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("gateway %s", str);
eina_stringshare_replace(&pd->ipv4.gateway, str);
}
else if (strcmp(key, "Netmask") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("netmask %s", str);
eina_stringshare_replace(&pd->ipv4.netmask, str);
}
else
{
WRN("Unsupported field %s (signature=%s)", key, eldbus_message_iter_signature_get(value));
continue;
}
}
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_ipv6(Efl_Net_Session_Data *pd, Eldbus_Message_Iter *var)
{
Eldbus_Message_Iter *sub, *entry;
if (!eldbus_message_iter_arguments_get(var, "a{sv}", &sub))
return EINVAL;
while (eldbus_message_iter_get_and_next(sub, 'e', &entry))
{
const void *key;
Eldbus_Message_Iter *value;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &value))
{
ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry));
continue;
}
if (strcmp(key, "Method") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("configuration method %s", str);
}
else if (strcmp(key, "Address") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("address %s", str);
eina_stringshare_replace(&pd->ipv6.address, str);
}
else if (strcmp(key, "Gateway") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("gateway %s", str);
eina_stringshare_replace(&pd->ipv6.gateway, str);
}
else if (strcmp(key, "Netmask") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("netmask %s", str);
eina_stringshare_replace(&pd->ipv6.netmask, str);
}
else if (strcmp(key, "Netmask") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("netmask %s", str);
eina_stringshare_replace(&pd->ipv6.netmask, str);
}
else if (strcmp(key, "PrefixLength") == 0)
{
eldbus_message_iter_arguments_get(value, "y", &pd->ipv6.prefix_length);
DBG("prefix_length %hhu", pd->ipv6.prefix_length);
}
else if (strcmp(key, "Privacy") == 0)
{
const char *str;
eldbus_message_iter_arguments_get(value, "s", &str);
DBG("privacy %s (unused)", str);
}
else
{
WRN("Unsupported field %s (signature=%s)", key, eldbus_message_iter_signature_get(value));
continue;
}
}
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_bearers(Efl_Net_Session_Data *pd EINA_UNUSED, Eldbus_Message_Iter *var)
{
Eldbus_Message_Iter *sub;
const char *str;
eldbus_message_iter_arguments_get(var, "as", &sub);
while (eldbus_message_iter_get_and_next(sub, 's', &str))
DBG("allowed bearer '%s'", str);
return 0;
}
static Eina_Error
_efl_net_session_notifier_update_connection_type(Efl_Net_Session_Data *pd EINA_UNUSED, Eldbus_Message_Iter *var)
{
const char *str;
if (!eldbus_message_iter_arguments_get(var, "s", &str))
return EINVAL;
DBG("connection type '%s'", str);
return 0;
}
/* step #4: get the initial state and changed applied locally */
static Eldbus_Message *
_efl_net_session_notifier_update(const Eldbus_Service_Interface *service, const Eldbus_Message *msg)
{
Eo *o = eldbus_service_object_data_get(service, "efl_net");
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *array, *entry;
Eina_Bool updated = EINA_FALSE;
DBG("Session %p is updated %s", o, eldbus_message_path_get(msg));
EINA_SAFETY_ON_NULL_GOTO(o, end);
if (!eldbus_message_arguments_get(msg, "a{sv}", &array))
{
ERR("Unexpected net.connman.Notifier.Update() signature %s", eldbus_message_signature_get(msg));
goto end;
}
while (eldbus_message_iter_get_and_next(array, 'e', &entry))
{
const void *key;
Eldbus_Message_Iter *var;
Eina_Error err;
if (!eldbus_message_iter_arguments_get(entry, "sv", &key, &var))
{
ERR("Unexpected dict entry signature: %s", eldbus_message_iter_signature_get(entry));
continue;
}
if (strcmp(key, "State") == 0)
err = _efl_net_session_notifier_update_state(pd, var);
else if (strcmp(key, "Name") == 0)
err = _efl_net_session_notifier_update_name(pd, var);
else if (strcmp(key, "Bearer") == 0)
err = _efl_net_session_notifier_update_technology(pd, var);
else if (strcmp(key, "Interface") == 0)
err = _efl_net_session_notifier_update_interface(pd, var);
else if (strcmp(key, "IPv4") == 0)
err = _efl_net_session_notifier_update_ipv4(pd, var);
else if (strcmp(key, "IPv6") == 0)
err = _efl_net_session_notifier_update_ipv6(pd, var);
else if (strcmp(key, "AllowedBearers") == 0)
err = _efl_net_session_notifier_update_bearers(pd, var);
else if (strcmp(key, "ConnectionType") == 0)
err = _efl_net_session_notifier_update_connection_type(pd, var);
else
{
WRN("Unsupported setting %s (signature=%s)", key, eldbus_message_iter_signature_get(var));
continue;
}
if (err)
{
ERR("Could not handle property %s: %s", key, eina_error_msg_get(err));
continue;
}
updated |= EINA_TRUE;
}
if (updated) efl_event_callback_call(o, EFL_NET_SESSION_EVENT_CHANGED, NULL);
end:
return eldbus_message_method_return_new(msg);
}
static const Eldbus_Service_Interface_Desc _efl_net_session_notifier_desc = {
.interface = "net.connman.Notification",
.methods = (const Eldbus_Method []){
{
.member = "Release",
.cb = _efl_net_session_notifier_release,
},
{
.member = "Update",
.in = ELDBUS_ARGS({"a{sv}", "settings"}),
.cb = _efl_net_session_notifier_update,
},
{ }
},
};
/* return of step #2: session was created, get a proxy for it */
static void
_efl_net_session_create_session_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Connection *conn;
Eldbus_Object *obj;
const char *err_name, *err_msg, *path;
if (pd->mgr_pending == pending)
pd->mgr_pending = NULL;
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
ERR("Could not create session %p: %s=%s", o, err_name, err_msg);
return;
}
if (!eldbus_message_arguments_get(msg, "o", &path))
{
ERR("Could not get session %p DBus path!", o);
return;
}
conn = efl_net_connman_connection_get();
obj = eldbus_object_get(conn, "net.connman", path);
pd->proxy = eldbus_proxy_get(obj, "net.connman.Session");
if (!pd->proxy)
{
ERR("could not create DBus proxy for interface='net.connman.Session', name='net.connman', path='%s' o=%p", path, o);
eldbus_object_unref(obj);
return;
}
DBG("Created session %p (session=%s) with ConnMan...", o, path);
if (pd->connect.connected)
{
DBG("apply pending connect request: online_required=%d, technologies_allowed=%#x", pd->connect.online_required, pd->connect.technologies_allowed);
_efl_net_session_connect_do(o, pd);
}
}
static void
_efl_net_session_clear(Efl_Net_Session_Data *pd)
{
eina_stringshare_replace(&pd->name, NULL);
eina_stringshare_replace(&pd->interface, NULL);
eina_stringshare_replace(&pd->ipv4.address, NULL);
eina_stringshare_replace(&pd->ipv4.netmask, NULL);
eina_stringshare_replace(&pd->ipv4.gateway, NULL);
eina_stringshare_replace(&pd->ipv6.address, NULL);
eina_stringshare_replace(&pd->ipv6.netmask, NULL);
eina_stringshare_replace(&pd->ipv6.gateway, NULL);
pd->ipv6.prefix_length = 0;
pd->state = EFL_NET_SESSION_STATE_OFFLINE;
pd->technology = EFL_NET_SESSION_TECHNOLOGY_UNKNOWN;
}
/* step #2: once connman is there, call CreateSession */
static void
_efl_net_session_connman_name_owner_changed(void *data, const char *bus, const char *old_id, const char *new_id)
{
Eo *o = data;
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message_Iter *msg_itr, *cont;
Eldbus_Proxy *mgr;
Eldbus_Message *msg;
const char *path;
DBG("Name Owner Changed %s: %s->%s", bus, old_id, new_id);
if ((!new_id) || (new_id[0] == '\0'))
{
/* connman is gone, remove proxy as it became useless */
if (pd->proxy)
{
Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy);
eldbus_proxy_unref(pd->proxy);
pd->proxy = NULL;
eldbus_object_unref(obj);
}
_efl_net_session_clear(pd);
efl_event_callback_call(o, EFL_NET_SESSION_EVENT_CHANGED, NULL);
return;
}
path = eldbus_service_object_path_get(pd->notifier);
EINA_SAFETY_ON_NULL_RETURN(path);
INF("Create session %p notifier=%s with %s (%s)", o, path, bus, new_id);
mgr = efl_net_connman_manager_get();
EINA_SAFETY_ON_NULL_RETURN(mgr);
msg = eldbus_proxy_method_call_new(mgr, "CreateSession");
EINA_SAFETY_ON_NULL_RETURN(msg);
msg_itr = eldbus_message_iter_get(msg);
EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_send);
cont = eldbus_message_iter_container_new(msg_itr, 'a', "{sv}");
eldbus_message_iter_container_close(msg_itr, cont); /* empty, use defaults */
eldbus_message_iter_basic_append(msg_itr, 'o', path);
if (pd->mgr_pending)
eldbus_pending_cancel(pd->mgr_pending);
pd->mgr_pending = eldbus_proxy_send(mgr, msg, _efl_net_session_create_session_cb, o, DEFAULT_TIMEOUT);
EINA_SAFETY_ON_NULL_GOTO(pd->mgr_pending, error_send);
return;
error_send:
eldbus_message_unref(msg);
}
EOLIAN static Eo *
_efl_net_session_efl_object_constructor(Eo *o, Efl_Net_Session_Data *pd EINA_UNUSED)
{
if (!efl_net_connman_init())
{
ERR("could not initialize connman infrastructure");
return NULL;
}
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN static Eo *
_efl_net_session_efl_object_finalize(Eo *o, Efl_Net_Session_Data *pd)
{
Eldbus_Connection *conn;
char path[128];
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
conn = efl_net_connman_connection_get();
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
/* step #1: create local notifier path */
snprintf(path, sizeof(path), "/connman/notifier_%p", o);
pd->notifier = eldbus_service_interface_register(conn, path, &_efl_net_session_notifier_desc);
EINA_SAFETY_ON_NULL_RETURN_VAL(pd->notifier, NULL);
eldbus_service_object_data_set(pd->notifier, "efl_net", o);
eldbus_name_owner_changed_callback_add(conn, "net.connman", _efl_net_session_connman_name_owner_changed, o, EINA_TRUE);
DBG("waiting for net.connman to show on the DBus system bus...");
return o;
}
EOLIAN static void
_efl_net_session_efl_object_destructor(Eo *o, Efl_Net_Session_Data *pd)
{
Eldbus_Connection *conn;
conn = efl_net_connman_connection_get();
eldbus_name_owner_changed_callback_del(conn, "net.connman", _efl_net_session_connman_name_owner_changed, o);
if (pd->mgr_pending)
{
eldbus_pending_cancel(pd->mgr_pending);
pd->mgr_pending = NULL;
}
if (pd->notifier)
{
eldbus_service_object_data_del(pd->notifier, "efl_net");
eldbus_service_object_unregister(pd->notifier);
pd->notifier = NULL;
}
if (pd->proxy)
{
Eldbus_Object *obj = eldbus_proxy_object_get(pd->proxy);
Eldbus_Proxy *mgr = efl_net_connman_manager_get();
/* DestroySession is required since the manager proxy and bus
* may be alive, thus connman won't get any NameOwnerChanged
* or related to know the object is gone
*/
eldbus_proxy_call(mgr, "DestroySession", NULL, NULL, DEFAULT_TIMEOUT,
"o", eldbus_object_path_get(obj));
eldbus_proxy_unref(pd->proxy);
eldbus_object_unref(obj);
pd->proxy = NULL;
}
_efl_net_session_clear(pd);
efl_destructor(efl_super(o, MY_CLASS));
efl_net_connman_shutdown();
}
EOLIAN static const char *
_efl_net_session_name_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd)
{
return pd->name;
}
EOLIAN static Efl_Net_Session_State
_efl_net_session_state_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd)
{
return pd->state;
}
EOLIAN static Efl_Net_Session_Technology
_efl_net_session_technology_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd)
{
return pd->technology;
}
EOLIAN static const char *
_efl_net_session_interface_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd)
{
return pd->interface;
}
EOLIAN static void
_efl_net_session_ipv4_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd, const char **address, const char **netmask, const char **gateway)
{
if (address) *address = pd->ipv4.address;
if (netmask) *netmask = pd->ipv4.netmask;
if (gateway) *gateway = pd->ipv4.gateway;
}
EOLIAN static void
_efl_net_session_ipv6_get(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd, const char **address, uint8_t *prefix_length, const char **netmask, const char **gateway)
{
if (address) *address = pd->ipv6.address;
if (netmask) *netmask = pd->ipv6.netmask;
if (gateway) *gateway = pd->ipv6.gateway;
if (prefix_length) *prefix_length = pd->ipv6.prefix_length;
}
static void
_efl_net_session_connect_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
if (pd->connect.pending == pending)
pd->connect.pending = NULL;
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
WRN("Could not Connect: %s=%s", err_name, err_msg);
return;
}
DBG("Successfully requested a connection online_required=%hhu, technologies_allowed=%#x", pd->connect.online_required, pd->connect.technologies_allowed);
}
static void
_efl_net_session_connect_do_connect(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
const char *err_name, *err_msg;
if (pd->connect.pending == pending)
pd->connect.pending = NULL;
if (eldbus_message_error_get(msg, &err_name, &err_msg))
{
WRN("Could not Change('ConnectionType'): %s=%s", err_name, err_msg);
return;
}
pd->connect.pending = eldbus_proxy_call(pd->proxy, "Connect",
_efl_net_session_connect_cb, o,
DEFAULT_TIMEOUT, "");
EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending);
}
static void
_efl_net_session_connect_change_online_required(void *data, const Eldbus_Message *msg_ret, Eldbus_Pending *pending)
{
Eo *o = data;
Efl_Net_Session_Data *pd = efl_data_scope_get(o, MY_CLASS);
Eldbus_Message *msg;
Eldbus_Message_Iter *msg_itr, *itr;
const char *err_name, *err_msg;
Eina_Bool ret;
if (pd->connect.pending == pending)
pd->connect.pending = NULL;
if (eldbus_message_error_get(msg_ret, &err_name, &err_msg))
{
WRN("Could not Change('AllowedBearers'): %s=%s", err_name, err_msg);
return;
}
msg = eldbus_proxy_method_call_new(pd->proxy, "Change");
EINA_SAFETY_ON_NULL_RETURN(msg);
msg_itr = eldbus_message_iter_get(msg);
EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_msg);
eldbus_message_iter_basic_append(msg_itr, 's', "ConnectionType");
itr = eldbus_message_iter_container_new(msg_itr, 'v', "s");
EINA_SAFETY_ON_NULL_GOTO(itr, error_msg);
ret = eldbus_message_iter_basic_append(itr, 's', pd->connect.online_required ? "internet" : "local");
EINA_SAFETY_ON_FALSE_GOTO(ret, error_msg);
eldbus_message_iter_container_close(msg_itr, itr);
pd->connect.pending = eldbus_proxy_send(pd->proxy, msg,
_efl_net_session_connect_do_connect, o,
DEFAULT_TIMEOUT);
EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending);
return;
error_msg:
eldbus_message_unref(msg);
}
static void
_efl_net_session_connect_do(Eo *o, Efl_Net_Session_Data *pd)
{
Eldbus_Message_Iter *msg_itr, *itr, *array;
Eldbus_Message *msg;
if (!pd->connect.connected) return;
if (pd->connect.pending)
{
eldbus_pending_cancel(pd->connect.pending);
pd->connect.pending = NULL;
}
msg = eldbus_proxy_method_call_new(pd->proxy, "Change");
EINA_SAFETY_ON_NULL_RETURN(msg);
msg_itr = eldbus_message_iter_get(msg);
EINA_SAFETY_ON_NULL_GOTO(msg_itr, error_msg);
eldbus_message_iter_basic_append(msg_itr, 's', "AllowedBearers");
itr = eldbus_message_iter_container_new(msg_itr, 'v', "as");
EINA_SAFETY_ON_NULL_GOTO(itr, error_msg);
array = eldbus_message_iter_container_new(itr, 'a', "s");
#define MAP(X, s) \
if ((pd->connect.technologies_allowed & EFL_NET_SESSION_TECHNOLOGY_ ## X) == EFL_NET_SESSION_TECHNOLOGY_ ## X) \
eldbus_message_iter_basic_append(array, 's', s)
MAP(ALL, "*");
if (pd->connect.technologies_allowed == EFL_NET_SESSION_TECHNOLOGY_ALL) goto end;
MAP(ETHERNET, "ethernet");
MAP(WIFI, "wifi");
MAP(BLUETOOTH, "bluetooth");
MAP(CELLULAR, "cellular");
MAP(VPN, "vpn");
MAP(GADGET, "gadget");
#undef MAP
end:
eldbus_message_iter_container_close(itr, array);
eldbus_message_iter_container_close(msg_itr, itr);
pd->connect.pending = eldbus_proxy_send(pd->proxy, msg,
_efl_net_session_connect_change_online_required, o,
DEFAULT_TIMEOUT);
EINA_SAFETY_ON_NULL_RETURN(pd->connect.pending);
return;
error_msg:
eldbus_message_unref(msg);
}
EOLIAN static void
_efl_net_session_connect(Eo *o, Efl_Net_Session_Data *pd, Eina_Bool online_required, Efl_Net_Session_Technology technologies_allowed)
{
pd->connect.connected = EINA_TRUE;
pd->connect.online_required = online_required;
pd->connect.technologies_allowed = technologies_allowed;
if (pd->proxy) _efl_net_session_connect_do(o, pd);
}
EOLIAN static void
_efl_net_session_disconnect(Eo *o EINA_UNUSED, Efl_Net_Session_Data *pd)
{
if (pd->connect.pending)
{
eldbus_pending_cancel(pd->connect.pending);
pd->connect.pending = NULL;
}
pd->connect.connected = EINA_FALSE;
eldbus_proxy_call(pd->proxy, "Disconnect", NULL, NULL, DEFAULT_TIMEOUT, "");
}
#include "efl_net_session.eo.c"

View File

@ -0,0 +1,137 @@
enum Efl.Net.Session.State {
[[Provides the session connectivity state.
@since 1.19
]]
offline, [[no access point is connected]]
local, [[at least one access point was connected and the internet connectio wasn't verified]]
online, [[at least one access point was connected and the internet was verified]]
}
/* keep in sync with efl_net_control_technology.eo, comment what doesn't make sense */
enum Efl.Net.Session.Technology {
[[Bitwise-able technologies to allow for a network session.
@since 1.19
]]
unknown = 0,
/* system = (1 << 0), */
ethernet = (1 << 1),
wifi = (1 << 2),
bluetooth = (1 << 3),
cellular = (1 << 4),
/* gps = (1 << 5), */
vpn = (1 << 6),
gadget = (1 << 7),
/* p2p = (1 << 8), */
all = (Efl.Net.Session.Technology.ethernet | Efl.Net.Session.Technology.wifi | Efl.Net.Session.Technology.bluetooth | Efl.Net.Session.Technology.cellular | Efl.Net.Session.Technology.vpn | Efl.Net.Session.Technology.gadget),
}
class Efl.Net.Session (Efl.Loop_User) {
[[Used by application to request network connectivity.
This API is targeted at applications that need access to the
network, specifying the allowed bearer technologies to connect
to the internet, as well as if just local networking is enough
or validated internet access is required.
\@note the @.connect method is subject to backend policy. For
instance, ConnMan uses
https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt
@since 1.19
]]
events {
changed; [[Some properties were changed.]]
}
methods {
connect {
[[Asks the session to be connected.
This method doesn't need to be called if all an
application want is to monitor the connectivity state,
like a poller that will only try to access the
webservice when there is an existing connection without
triggering one.
This method is subject to backend policy. For instance,
ConnMan uses
https://github.com/aldebaran/connman/blob/master/doc/session-policy-format.txt
]]
params {
online_required: bool; [[if $false, access points with local state are enough. If $true, the access point must be in online state, with a validated internet connection]]
technologies_allowed: Efl.Net.Session.Technology; [[Bitwise OR of technologies to allow]]
}
}
disconnect {
[[Indicates this session doesn't need a connection anymore.
This reverses the effect of @.connect, let the system
disconnect if nothing else needs a connection. One may
still use the session object to monitor the connectivity
state via properties and "changed" event.
]]
}
@property name {
[[The user-friendly access point name.]]
get { }
values {
name: string;
}
}
@property state {
[[If the session connectivity is offline, local or online.]]
get { }
values {
state: Efl.Net.Session.State;
}
}
@property technology {
[[The access point technology that backs this session]]
get { }
values {
technology: Efl.Net.Session.Technology;
}
}
@property interface {
[[The interface allows the application to assign the socket to a given device using SO_BINDTODEVICE]]
get { }
values {
interface: string;
}
}
@property ipv4 {
[[IPv4 in use for this session.]]
get { }
values {
address: string;
netmask: string;
gateway: string;
}
}
@property ipv6 {
[[IPv6 in use for this session.]]
get { }
values {
address: string;
prefix_length: uint8;
netmask: string;
gateway: string;
}
}
}
implements {
Efl.Object.destructor;
Efl.Object.constructor;
Efl.Object.finalize;
}
}