Newer
Older
+++
date = "2015-12-03"
weight = 100
title = "Interface discovery"
aliases = [
"/old-wiki/Interface_discovery"
]
Various features on Apertis require a way to discover the applications
and/or agents that implement a particular set of functionality. We refer
to the "API contract" for this set of functionality as an
***interface***.
## Use cases
- A [global search user interface](/images/apertis-global-search-design-0.3.2.pdf)
requires a list of agents that can act as "Auxiliary
Sources" (see §6.2 in the Global Search design document). For
example, a Spotify client might register itself as a search provider
so that searching for a term in a global search will find artists or
songs matching that term.
- An application that will display a [Sharing]( {{< ref "/sharing.md" >}} )
menu similar to the one in Android requires a list of applications
with which files or data can be shared.
- A navigation app, potentially from an app-store, obtains [points of
interest]( {{< ref "/points_of_interest.md" >}} ) from a number of
providers, again potentially from an app-store. In a "pull" model,
the navigation app would consume the interface "points-of-interest
provider" by sending queries to the implementors and getting results
back, and the points-of-interest providers would implement that
interface. Conversely, in a "push" model, the navigation app could
implement the interface "points-of-interest sink", and the
points-of-interest providers could consume that interface by sending
points of interest to each sink.
- If more than one navigation app is installed (for example because an
Apertis system includes the OEM's own simple navigation solution,
but it is possible to install premium navigation software from the
app-store), a settings user interface to select the preferred
navigation app might need to list all the possible navigation apps.
- Interface discovery could potentially be used with the interface "is
the preferred navigation app" to start the navigation app on-demand.
If it is, it must be possible to mark one as preferred.
- A navigation app could have a preferences dialog in which [points of
interest]( {{< ref "/points_of_interest.md" >}} ) providers can be selected
or deselected. It should not display points of interest from
deselected providers, and should not waste system resources on
receiving points of interest from those providers. However, if
another application also consumes points of interest, disabling a
points of interest provider in the navigation app should not prevent
it from being used by the other application.
- The platform could have a preferences dialog in which [points of
interest]( {{< ref "/points_of_interest.md" >}} ) providers can be selected
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
or deselected. If a POI provider is deselected here, POI consumers
such as the navigation app should behave as though the deselected
provider had not been installed at all.
## In other systems
[GNOME Shell's search provider
API](https://git.gnome.org/browse/gnome-shell/tree/js/ui/remoteSearch.js)
relies on applications registering their support for the search provider
"interface" by installing files in
`/usr/share/gnome-shell/search-providers`. This is not ideally suited to
a platform like Apertis with a strong division between the "platform"
and "app bundle" layers, and does not generalize trivially (each
interface would have to define its own location in which to place
metadata files).
The [freedesktop.org Desktop Entry
specification](http://standards.freedesktop.org/desktop-entry-spec/latest/)
shared by GNOME, KDE and other open source desktop environments uses
`.desktop` metadata files to store metadata about applications. It
defines [an `Interfaces`
key](http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#interfaces)
whose value is a list of syntactically valid [D-Bus interface
names](http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface).
Each interface name may represent either a D-Bus interface, or any other
"API contract"; there is no requirement that D-Bus is actually used.
## Security considerations
### Restricting who can advertise a given interface
If arbitrary
[ISVs](https://en.wikipedia.org/wiki/Independent_software_vendor) can
publish app-bundles that advertise arbitrary interfaces, there is a risk
that consumers of those interfaces would have an inappropriate level of
trust in those app-bundles by assuming that only their own app-bundles
can advertise "their" interfaces, for example "leaking" private
information to them.
### Communication between consumers and implementors
If a particular interface involves direct communication between a
consumer and an implementor, then discovery is not sufficient: it is
also necessary to ensure that the security model allows the consumer and
the implementor to communicate. Conversely, if a particular interface
forces all communication between a consumer and an implementor through a
trusted intermediary such as Didcot, then it is necessary to ensure that
the security model allows both the consumer and the implementor to
communicate with the trusted intermediary, and that the trusted
intermediary is able to determine that forwarding data between consumer
and implementor will not violate the security model.
The desired security model for this interface is that some subset of
interfaces are considered to be ***public interfaces***. Trusted
platform components may list the implementors of any interface, public
or not, and may initiate communication with those implementors. Store
applications may list the implementors of public interfaces, and may
initiate communication with the implementors of public interfaces, but
cannot do the same for non-public interfaces.
### Visibility of applications to other applications
Our security model does not consider it to be acceptable for app-bundles
to be able to enumerate other app-bundles' entry points (with the
exception that public interfaces may be enumerated). This implies that
the implementation of get_implementations() (and the objects that it
returns) must be done via IPC (most likely D-Bus) to a trusted service
such as Didcot or Canterbury, which can read the .desktop files in
XDG_DATA_DIRS/applications and apply appropriate filtering for the
caller's limited view of the system.
## Recommendation
For each application or agent (*entry point*) in an application bundle,
we recommend that a freedesktop.org `.desktop` file is provided in a
standard location such as `/var/lib/apertis_extensions/applications` by
installing the application bundle. Possible implementations of this:
- The store publication process could verify that the contents of the
provided `.desktop` file are appropriate for the application's
manifest.
- The store publication process could generate a `.desktop` file from
the application's manifest, with no control from the application
author, other than to the extent that they can control the manifest
and still have it approved by the [app-store
curator]( {{< ref "/app_store_approval.md" >}} ).
- The application manager could generate a `.desktop` file from the
application's manifest during installation.
The resulting `.desktop` file should contain the standardized
`Interfaces` key as described above.
This information should be made available to API users via a C API
resembling GLib's
[GAppInfo](https://developer.gnome.org/gio/stable/GAppInfo.html) and
[GDesktopAppInfo](https://developer.gnome.org/gio/stable/gio-Desktop-file-based-GAppInfo.html)
APIs, in particular
[g_desktop_app_info_get_implementations()](https://developer.gnome.org/gio/stable/gio-Desktop-file-based-GAppInfo.html#g-desktop-app-info-get-implementations).
However, we recommend an asynchronous version of that API in order to
support the implementation being via D-Bus. Specifically, it should look
something like this, with Namespace replaced by some suitable API
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
`void namespace_app_registry_get_implementations_async (NamespaceAppRegistry *self,`
` const gchar *interface_name,`
` GCancellable *cancellable,`
` GAsyncReadyCallback *callback,`
` gpointer user_data);`
`/* Returns: (element-type GAppInfo) (transfer full): */`
`GList *namespace_app_registry_get_implementations_finish (NamespaceAppRegistry *self,`
` GAsyncResult *result,`
` GError **error);`
where the result is a list of objects that implement the GAppInfo
GInterface. If there is an order of preference, the most-preferred
should come first. If there is no particular preference order, the
implementation should use a predictable order, such as ordering by
most-recently-used, most-recently-installed or alphabetically.
Either this could be implemented in terms of a D-Bus API, or it could
have a D-Bus API based on it for access by non-C applications, for
example:
`/* returns a list of pairs (desktop file ID, text of .desktop file) */`
`org.apertis.Namespace1.GetImplementations(s interface_name) → a(ss)`
For interfaces (API contracts) that already have a system-wide
registration mechanism, such as Telepathy connection managers, D-Bus
session services and systemd user services, we recommend adopting the
existing mechanism instead, using appropriate subdirectories of
`/var/lib/apertis_extensions` where necessary.
### Selecting a preferred implementation
Some of the possible use-cases for interfaces benefit from the concept
of a preferred implementation: for example, a navigation button should
launch the preferred (default) navigation application, and if
points-of-interest providers have a "push" model, they should not start
non-preferred navigation applications in order to push points of
interest into those implementations.
For other use-cases, having a preferred implementation is unnecessary:
for example, for a Sharing menu, global search, or points-of-interest
providers with a "pull" model, the natural design is to query all known
implementations in parallel, possibly excluding some that have been
disabled.
We recommend addressing the question of a default/preferred
implementation on a case-by-case basis (for example by introducing a
platform setting for each interface that needs a preferred choice), and
only developing a more general solution if experience demonstrates that
it is needed in practice.
For example, a preferred navigation application could be selected with
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
`void namespace_app_registry_get_default_navigation_implementation_async (NamespaceAppRegistry *self,`
` GCancellable *cancellable,`
` GAsyncReadyCallback *callback,`
` gpointer user_data);`
`GAppInfo *namespace_app_registry_get_default_navigation_implementation_finish (NamespaceAppRegistry *self,`
` GAsyncResult *result,`
` GError **error);`
if required.
The storage of preferred implementations should be considered to be an
implementation detail of the platform component that implements this
platform API. For example, it could have a GSetting for each well-known
interface, whose value is the string app-ID (D-Bus well-known name) of
the preferred entry-point, or an ordered list of preferred entry-points
with the most-preferred first.
### Enabling/disabling providers
If a provider is disabled system-wide, the platform component that
implements interface discovery (for example Didcot) must behave as
though it was not installed at all when answering queries from other
components. The storage of enabled/disabled implementations can be
considered to be an implementation detail of that component: for example
it could store a string-list of disabled app IDs in GSettings.
Uninstalling a provider should probably remove it from that list, so
that reinstalling the provider automatically enables it.
If a provider is disabled for a particular consumer, we recommend that
the consumer stores its own string-list of disabled app IDs, and filters
the results of queries on the client-side, encapsulated in a library.
This probably only makes sense for interfaces where the consumer will
use all non-disabled implementations.
### Restricting who can advertise a given interface
We recommend that interfaces advertised by a provider should be
restricted by [app-store curators]( {{< ref "/app_store_approval.md" >}} ), as
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
follows:
- each
[ISV](https://en.wikipedia.org/wiki/Independent_software_vendor)
that will publish apps on the app-store registers one or more
reversed-DNS prefixes with the app-store curator as part of their
app-developer account (for example, [Collabora
Ltd.](https://www.collabora.com/) might register com.collabora
and/or uk.co.collabora)
- the app-store curator verifies the ISV's ownership of the relevant
domain names before accepting uploads from that ISV
- app-bundles published by the ISV may implement interface names in
the namespace of those reversed-DNS prefixes without necessarily
triggering extensive checking by the app-store curator (for example,
Collabora Ltd. could publish an app-bundle implementing
com.collabora.MyInterface)
- a whitelist of known-"safe" interface names in shared namespaces
such as org.apertis, org.freedesktop and org.gnome could also be
implemented without necessarily triggering extra checks by the
app-store curator (for example, an org.apertis.SharingProvider
interface which adds the app to the Sharing menu might be considered
to be "safe" for anyone to implement)
- all other interface names would be "red flags" leading to rejection
or additional checking by the app-store curator
This implies that cooperating ISVs cannot invent their own interfaces
without app-store curators' involvement.
It is important to note that if the platform initially has this policy,
it cannot be relaxed to "anyone may implement any interface" later. If
it was, ISVs writing previously-correct code would potentially become
susceptible to cross-app resource access attacks (for example, if the
ISV owning example.net had assumed that every implementation of
net.example.MyInterface was necessarily trusted code).
### Communication between consumers and implementors
App-bundles that implement of public interfaces should receive AppArmor
profiles allowing them to receive D-Bus method calls from anywhere.
App-bundles that do not implement public interfaces should receive
AppArmor profiles that allow D-Bus method calls from platform services,
and from other processes from the same app-bundle, but deny other D-Bus
method calls.
### Visibility of applications to other applications
App-bundles' AppArmor profiles should not give them read access to
`/var/lib/apertis_extensions/applications` or to other app-bundles'
manifests.
The implementation of the interface discovery should be done via D-Bus.
The service providing this D-Bus API (for example Didcot) should be a
platform component. It is considered to be a trusted component for the
purposes of security between app-bundles: it must reveal public
interface implementations to other app-bundles, but must only reveal
non-public interface implementations to trusted platform components.