Telepathy Clients are applications that interact with Telepathy services to provide some service to the user. It could be a chat client, a VoIP client, a file transfer client, a Telepathy Tubes enabled application, or something else or maybe even some combination of these.
Just like Telepathy Connection Managers, the Account Manager and the Channel Dispatcher, Telepathy Clients implement a D-Bus API that is used by the Channel Dispatcher to dispatch incoming channels to the client.
Telepathy Clients appear as D-Bus services registered or activatable on a user's session bus. Every running or activatable Telepathy client must provide a D-Bus well-known name of the form org.freedesktop.Telepathy.Client.clientname (e.g. org.freedesktop.Telepathy.Client.Empathy) and an object with path of the form /org/freedesktop/Telepathy/Client/clientname (e.g. /org/freedesktop/Telepathy/Client/Empathy).
Telepathy clients can be activated by the Channel Dispatcher using D-Bus Activation. This is done in the regular way by registering a .service file with D-Bus.
An additional .client file is used by the Channel Dispatcher to look up properties such as what interfaces the client supports and what channels its interested in (see Section 4.1.2 ― .client File below).
An activatable client can be used by the Dispatcher to handle an incoming channel at any time. Examples of clients that might be activatable are clients for handling incoming text, VoIP or file transfers or Telepathy Tubes.
Clients needn't be activatable if you only wish for a client to be considered by the Channel Dispatcher while the client is running. Applications that you wish to integrate with Telepathy, but do not wish to have launched except by the user are in this category. An example might be a full-screen media-centre type application.
Be aware that all Approvers for a channel are dispatched (e.g. if six Approvers match a given incoming channel, that channel will be passed to all six clients), so generic Approvers (Approvers that aren't associated with a specific application — i.e. Tube applications) should not be activatable by D-Bus activation and instead be started by the desktop session.
There are three types of Telepathy clients:
Observers are used to monitor the creation of new channels and the status of those channels. Observers can be used for functions such as chat logging, or monitoring file transfer progress. Observers must not make method calls that are the role of the channel Handler (e.g. acknowledging chat messages), but they may change channel state as the result of user interaction (e.g. an Observer may offer the user a Cancel button to terminate a file transfer).
Approvers serve to notify users of new channels and allow the user to accept or reject the channel. The Approver is also able to choose the preferred Handler for a channel from a list of possible Handlers (which it might choose to offer to the user or select based on some other criteria).
Handlers do the heavy lifting to handle a channel. A Handler might be a text-chat or video-chat window, or a handler to receive a file or an application that uses Tubes for collaboration. Ultimately all interaction with a channel is done in the Handler, for instance acknowledging text messages.
It is the Approver's job to accept the channel, but it should not call methods on the channel, that is the responsibility of the Handler.
Take, for example, a file transfer channel. The Approver is tasked with asking the user to accept the incoming file transfer, providing the details about the incoming file; but the action of calling AcceptFile is the responsibility of the Handler.
Often there will be a common Approver provided by the desktop.
Telepathy clients are services that implement a D-Bus API which is called by the Channel Dispatcher (just as the client itself makes method calls to the Account Manager and Connection Managers).
All Telepathy clients must implement the org.freedesktop.Telepathy.Client interface plus (at least) one of org.freedesktop.Telepathy.Client.Observer, org.freedesktop.Telepathy.Client.Approver or org.freedesktop.Telepathy.Client.Handler.
Observers need to implement the Client and Client.Observer interfaces. The Client.Interfaces property should include org.freedesktop.Telepathy.Client.Observer.
The ObserverChannelFilter property must be implemented so that the Channel Dispatcher knows what types of channels your Observer is interested in.
Channel filters (i.e. ObserverChannelFilter, ApproverChannelFilter and HandlerChannelFilter all take a value of type Channel_Class_List. This is a list of key-value pair maps of channel properties, like those used to request channels, or provide information about incoming channels.
Channels have their properties matched against the maps listed in the filter. Only certain D-Bus types have useful semantics for matching. These are listed in Table 4-1.
Type | D-Bus Type | Match rule |
---|---|---|
Integers | y, n, q, i, u, x, t | Matched for numeric equality, regardless of type. For example 42 as a 16-bit signed integer 'n' is considered equal to 42 as a 32-bit unsigned integer 'u'. |
Booleans | b | Matched for equality. Not considered equal to any other type. |
Strings | s | Matched for equality. Not considered equal to any other type. |
Objects | o | Matched for equality. Not considered equal to any other type. |
Specified properties are matched for equality, unspecified properties will not be matched. Thus, a filter to match all channels is a list containing an empty map. Only equality can be represented and the matching of each map is an OR operation. Example 4-1 gives some examples.
If you require more complex channel matching, you should install a more generic handlers and simply apply your own filtering rules when the Dispatcher gives you the channel.
This filter (a single empty map) matches all channels.
dbus.Array([ dbus.Dictionary({ }, signature='sv'), ], signature='a{sv}')
This filter matches requested text channels that have a target handle which is either a contact or a chat room. This is semantically the same example as Example 4-4 below.
dbus.Array([ dbus.Dictionary({ 'org.freedesktop.Telepathy.Channel.ChannelType': 'org.freedesktop.Telepathy.Channel.Type.Text', 'org.freedesktop.Telepathy.Channel.TargetHandleType': HANDLE_TYPE_CONTACT, 'org.freedesktop.Telepathy.Channel.Requested': True, }, signature='sv'), dbus.Dictionary({ 'org.freedesktop.Telepathy.Channel.ChannelType': 'org.freedesktop.Telepathy.Channel.Type.Text', 'org.freedesktop.Telepathy.Channel.TargetHandleType': HANDLE_TYPE_ROOM, 'org.freedesktop.Telepathy.Channel.Requested': True, }, signature='sv'), ], signature='a{sv}')
Finally the ObserveChannels method must be implemented. This method is called by the Channel Dispatcher whenever a new channel matching your filter arrives. It takes six parameters:
Parameter | Type | Description |
---|---|---|
Account | o | The path to the Account object. Look this up on the Account Manager (org.freedesktop.Telepathy.AccountManager). |
Connection | o | The path to the Connection object. Look this up with the Connection (remove the leading / and replace all subsequent /s with .s). |
Channels | Channel_Details_List — a(oa{sv}) | The Channels to observe. Look these up using the well-known bus name of the Connection. |
Dispatch Operation | o | The path to the ChannelDispatchOperation, or / if there is no dispatch operation for this dispatch. Look this up on the Channel Dispatcher (org.freedesktop.Telepathy.AccountManager). |
Requests Satisfied | ao | Outgoing ChannelRequest objects satisfied for this dispatch. Look these up on the Channel Dispatcher (org.freedesktop.Telepathy.AccountManager). |
Observer Info | a{sv} | Additional information for Observers. |
It is very rare that you will need to implement an Approver, in general the Approver will be provided by your desktop environment.
Approvers are very similar to Observers. Approvers need to implement the Client and Client.Approver interfaces. The Client.Interfaces property must include org.freedesktop.Telepathy.Client.Approver.
The ApproverChannelFilter property must be implemented so that the Channel Dispatcher knows what types of channels your Approver is interested in. This property has the same form as for Observers.
Finally the AddDispatchOperation method must be implemented. This method is called by the Channel Dispatcher whenever a new channel matching your filter arrives. This method has three parameters:
Parameter | Type | Description |
---|---|---|
Channels | Channel_Details_List — a(oa{sv}) | The intial value of the Channels property for the dispatch operation. Provided so that it doesn't need to be requested. Look these up using the well-known bus name of the Connection. |
Dispatch Operation | o | The ChannelDispatchOperation for this dispatch. This object is provided by the Channel Dispatcher (org.freedesktop.Telepathy.ChannelDispatcher). |
Properties | a{sv} | The immutable properties for the dispatch operation, including the Account, Connection and PossibleHandlers properties. Provided so that they needn't be requested. |
Each Approver should connect to the dispatch operation's Finished and ChannelLost signals. The Finished signal indicates that some Approver has handled the dispatch, and this dispatch object is no longer valid. The ChannelLost indicates that one of the original channels making up this dispatch has been closed for the reason given.
If an Approver returns from AddDispatchOperation with no error, the Channel Dispatcher will assume the Approver accepts the dispatch and is offering it to the user, and thus will wait for the Approver to handle the dispatch operation by calling Claim or HandleWith methods. If you return from this method without error, you must intend to call one of these two functions, else the channel may never be dispatched to a Handler.
To indicate you're not interested in approving a channel, return a D-Bus error from AddDispatchOperation.
Otherwise call the HandleWith or Claim method on the dispatch operation.
The HandleWith method tells the Channel Dispatcher to pass the dispatch to a Handler by calling HandleChannels on that Handler. You must either provide the name of a Handler from the list of possible Handlers provided in the PossibleHandlers property or an empty string if you wish to use the Channel Dispatcher's preferred Handler.
HandleWith may return one of several errors, for instance raising NotYours if the dispatch has already been approved; or possibly even some error returned from HandleChannels.
The Claim method tells the Channel Dispatcher that you are claiming the dispatch for yourself. Assuming the method returns without error (i.e. the dispatch wasn't already handled), the dispatch belongs to your client. The Channel Dispatcher will not call HandleChannels in this instance. Clients that claim a dispatch, but do not immediately reject it should also implement the Handler interface, specifically HandledChannels.
The Claim method is also used to reject an incoming dispatch.
The Channel Dispatcher does not know how to politely close or reject the various types of different channels, and instead makes this the responsibility of the Telepathy Clients.
To reject a dispatch, first Claim it. If this method returns without error (i.e. you have successfully claimed the dispatch), you can then close the channels in a way that is specific to the channel type (e.g. acknowledging the messages and calling Close or calling Destroy).
Approvers that claim channels only to reject them do not need to present as Handlers.
Handlers are the most commonly implemented class of Telepathy Client. Handlers need to implement the Client and Client.Handler interfaces. They may also optionally implement the Client.Interface.Requests interface.
The Client.Interfaces property must include org.freedesktop.Telepathy.Client.Handler and optionally org.freedesktop.Telepathy.Client.Interface.Requests.
The HandlerChannelFilter property must be implemented so that the Channel Dispatcher knows what types of channels your Handler is interested in. This property has the same form as for Observers.
The Capabilities property is the list of additional Telepathy capabilities this Handler supports. These capabilities can also be listed in the .client file.
The BypassApproval property indicates that channels in your HandlerChannelFilter. This property is normally used when a Client registers a second Client object with channels that its expecting.
The HandledChannels property is a list of D-Bus object paths for Channels this Client is handling. This property only exists to recover state in the event of a Channel Dispatcher crash, so there is no need to emit a signal when the property is updated. This property should include the channels for any temporary transient Client objects registered by the client.
The HandleChannels method must be implemented. This is the method the Channel Dispatcher will call to dispatch channels to a Handler, either because it chose a Handler or in response to an Approver calling HandleWith (this method is not called is an Approver calls Claim). The method takes six parameters:
Parameter | Type | Description |
---|---|---|
Account | o | The path to the Account object. Look this up with the Account Manager (org.freedesktop.Telepathy.AccountManager). |
Connection | o | The path to the Connection object. |
Channels | Channel_Details_List — a(oa{sv}) | The Channels to be handled, as well as their immutable properties. |
Requests Satisfied | ao | The Channel Requests that are handled by this dispatch (see below). Look these up with the Channel Dispatcher (org.freedesktop.Telepathy.Channel Dispatcher). |
User Action Time | t | The time at which this user action occured (or 0 if unknown). Used for focus stealing prevention. |
Handler Info | a{sv} | Additional information about this dispatch. |
Returning an error from HandleChannels causes the Channel Dispatcher to assume the Handler has failed or crashed, what happens in this circumstance is left up to the Channel Dispatcher.
Handlers may also implement the optional Requests interface. This interface allows the Channel Dispatcher to notify a Handler of outgoing requests it is likely to be asked to handle (i.e. channels requested via the Channel Dispatcher). This is not the same as being asked to handle the channels, and is only to be used for notifying the user (e.g. displaying an "In progress" dialog).
There are two methods making up this interface: AddRequest and RemoveRequest.
AddRequest notifies the Handler of a request that has been made. It takes two parameters:
Parameter | Type | Description |
---|---|---|
Request | o | Path to the ChannelRequest object for this request, provided by the Channel Dispatcher (org.freedesktop.Telepathy.ChannelDispatcher). |
Properties | a{sv} | The immutable properties of the ChannelRequest object. It includes the Requests property, an array of requested channel property maps. |
One of the properties, UserActionTime, can be used for focus stealing prevention.
RemoveRequest notifies the Handler that a request has been cancelled, or passed off to a different Handler. It takes three parameters:
Parameter | Type | Description |
---|---|---|
Request | o | Path to the ChannelRequest object for this request, provided by the Channel Dispatcher (org.freedesktop.Telepathy.ChannelDispatcher). |
Error | s | The name of a D-Bus error saying why the request failed. |
Message | s | A more detailed error message. |
The error org.freedesktop.Telepathy.NotYours indicates that the request was successful, but another Handler was chosen to handle the request.
RemoveRequest is not called if your Handler is chosen to handle the request, instead this request will be included in the Requests Satisfied parameter in HandleChannels.
ChannelRequest objects are documented in Section 6.1.1 ― Channel Request Objects.
telepathy-glib provides a class, TpBaseClient, that simplifies implementing Telepathy clients by providing the client service on the D-Bus. TpBaseClient is an abstract base class that can be inherited to create any mixture of client types. telepathy-glib also provides three basic concrete classes, one for each client type: TpSimpleObserver, TpSimpleApprover and TpSimpleHandler.
Example 4-2 provides an example of creating a TpSimpleHandler to handle file transfer channels.
/* create a new Handler */ handler = tp_simple_handler_new (dbus, FALSE, FALSE, CLIENT_NAME, FALSE, handle_channels, NULL, NULL); /* add a channel filter */ tp_base_client_take_handler_filter (handler, tp_asv_new ( /* only FT channels */ TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER, /* we always need a TargetHandleType so that the Handler's * capabilities are exported via the Connection Manager */ TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT, NULL)); /* register the Handler on the D-Bus */ if (!tp_base_client_register (handler, &error)) g_error ("%s", error->message);
The TpAccount, TpConnection and TpChannel objects passed to the callback are all prepared with the core feature. You must hold a reference to the object to access it outside the callback.
Each callback is passed a context object; one of TpObserveChannelsContext, TpAddDispatchOperationContext, or TpHandleChannelsContext; named after the D-Bus method that triggers the callback. Each context object provides three methods: accept, fail and delay; one of which must be called before the end of the callback.
The Accept and Fail methods are used to return from the ObserveChannels, AddDispatchOperation and HandleChannels D-Bus methods, either successfully or with an error condition.
The Delay method is used if you have further preparation to carry out before ultimately calling either Accept or Fail. This is often used for things that Observers or Approvers need to do before the Handler is given the channel, like inspecting the pending message queue. You must reference the context object to hold it outside the callback. Example 4-3 shows an example of using the delay method.
static void observe_channels (TpSimpleObserver *observer, TpAccount *account, TpConnection *connection, GList *channels, TpChannelDispatchOperation *dispatch, GList *requests, TpObserveChannelsContext *context, gpointer user_data) { GList *l; g_debug ("ObserveChannels"); for (l = channels; l != NULL; l = l->next) { TpChannel *channel = l->data; TpHandleType handle_type; /* request the pending message queue */ tp_cli_channel_type_text_call_list_pending_messages (channel, -1, FALSE, list_pending_messaged_cb, NULL, NULL, G_OBJECT (context)); increment_pending (context); tp_channel_get_handle (channel, &handle_type); if (handle_type == TP_HANDLE_TYPE_ROOM) { /* prepare the group property */ GQuark features[] = { TP_CHANNEL_FEATURE_GROUP, 0 }; tp_proxy_prepare_async (channel, features, channel_group_prepared, context); increment_pending (context); } /* hold a reference to the channel, that we release on invalidation */ g_object_ref (channel); g_signal_connect (channel, "invalidated", G_CALLBACK (channel_invalided), NULL); } /* hold a reference to @context */ g_object_ref (context); /* delay responding to @context until our callbacks have finished */ tp_observe_channels_context_delay (context); }
A .client file is used by the Channel Dispatcher to pre-lookup properties for D-Bus activatable Telepathy clients. Non-activatable clients can also install a client file, but there is little point as the Channel Dispatcher can read the properties directly.
The file is installed into $XDG_DATA_DIRS/telepathy/clients/clientname.client (e.g. /usr/share/telepathy/clients/gnome-approver.client)
The file contains keys for the immutable D-Bus properties the Channel Dispatcher is interested in. For instance, an Observer would provide the Interfaces and ObserverChannelFilter properties. Other properties that might appear include ApproverChannelFilter, HandlerChannelFilter and Capabilities.
Example 4-4 includes some .client file examples.
This example is semantically the same as part 2 of Example 4-1 above.
[org.freedesktop.Telepathy.Client] Interfaces=org.freedesktop.Telepathy.Client.Observer; [org.freedesktop.Telepathy.Client.Observer.ObserverChannelFilter 0] org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Text org.freedesktop.Telepathy.Channel.TargetHandleType u=1 org.freedesktop.Telepathy.Channel.Requested b=true [org.freedesktop.Telepathy.Client.Observer.ObserverChannelFilter 1] org.freedesktop.Telepathy.Channel.ChannelType s=org.freedesktop.Telepathy.Channel.Type.Text org.freedesktop.Telepathy.Channel.TargetHandleType u=2 org.freedesktop.Telepathy.Channel.Requested b=true
[org.freedesktop.Telepathy.Client.Handler.Capabilities] org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/ice-udp=true org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/audio/speex=true org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/video/theora=true org.freedesktop.Telepathy.Channel.Interface.MediaSignalling/video/h264=true