From ecccce4b62c048272dd48378cc05d7165746b83e Mon Sep 17 00:00:00 2001 From: Scott Murray Date: Tue, 10 Oct 2023 18:03:44 -0400 Subject: Move application framework section Move application framework section from "Developer Guides" to "Component Documentation" and fix all links. Bug-AGL: SPEC-4928 Signed-off-by: Scott Murray Change-Id: I6bb10908860860d6634b23f6a458959397a98e81 Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/documentation/+/29283 Reviewed-by: Jan-Simon Moeller Tested-by: Jan-Simon Moeller --- .../01_AGL_components.md | 2 +- .../02_agl_compositor.md | 2 +- .../06_application_framework.md | 7 - .../Application_Framework/01_Introduction.md | 186 ++++++++++++++++ .../02_Application_Startup.md | 248 +++++++++++++++++++++ .../02_Application_Startup_Dbus.md | 190 ++++++++++++++++ .../03_Creating_a_New_Service.md | 151 +++++++++++++ .../04_Creating_a_New_Application_Dbus.md | 139 ++++++++++++ .../images/application_switching.msc | 29 +++ .../images/application_switching.png | Bin 0 -> 31633 bytes .../images/start_and_activation.msc | 30 +++ .../images/start_and_activation.png | Bin 0 -> 36479 bytes 12 files changed, 975 insertions(+), 9 deletions(-) delete mode 100644 docs/06_Component_Documentation/06_application_framework.md create mode 100644 docs/06_Component_Documentation/Application_Framework/01_Introduction.md create mode 100644 docs/06_Component_Documentation/Application_Framework/02_Application_Startup.md create mode 100644 docs/06_Component_Documentation/Application_Framework/02_Application_Startup_Dbus.md create mode 100644 docs/06_Component_Documentation/Application_Framework/03_Creating_a_New_Service.md create mode 100644 docs/06_Component_Documentation/Application_Framework/04_Creating_a_New_Application_Dbus.md create mode 100755 docs/06_Component_Documentation/Application_Framework/images/application_switching.msc create mode 100644 docs/06_Component_Documentation/Application_Framework/images/application_switching.png create mode 100755 docs/06_Component_Documentation/Application_Framework/images/start_and_activation.msc create mode 100644 docs/06_Component_Documentation/Application_Framework/images/start_and_activation.png (limited to 'docs/06_Component_Documentation') diff --git a/docs/06_Component_Documentation/01_AGL_components.md b/docs/06_Component_Documentation/01_AGL_components.md index 57dbe29..b36580d 100644 --- a/docs/06_Component_Documentation/01_AGL_components.md +++ b/docs/06_Component_Documentation/01_AGL_components.md @@ -24,4 +24,4 @@ title: AGL Components ### Lifecycle management -- [Application Framework](../../04_Developer_Guides/01_Application_Framework/01_Introduction/) +- [Application Framework](Application_Framework/01_Introduction.md) diff --git a/docs/06_Component_Documentation/02_agl_compositor.md b/docs/06_Component_Documentation/02_agl_compositor.md index c5221cb..9e62890 100644 --- a/docs/06_Component_Documentation/02_agl_compositor.md +++ b/docs/06_Component_Documentation/02_agl_compositor.md @@ -16,7 +16,7 @@ library, to control and signal back to the compositor when applications were started, among other things. Management of applications, starting, running and stopping them is done in AGL -with AppFW [Application Framework Management](../04_Developer_Guides/01_Application_Framework/01_Introduction.md), +with AppFW [Application Framework Management](Application_Framework/01_Introduction.md), which is an umbrella name to denote the suite of tools and daemons that handle all of that. It is integrated with systemd and with the current security model. Applications can use AppFW to hang off data, and to pass it down to diff --git a/docs/06_Component_Documentation/06_application_framework.md b/docs/06_Component_Documentation/06_application_framework.md deleted file mode 100644 index 1af8796..0000000 --- a/docs/06_Component_Documentation/06_application_framework.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Application Framework ---- - -# AppFW - -FIXME. diff --git a/docs/06_Component_Documentation/Application_Framework/01_Introduction.md b/docs/06_Component_Documentation/Application_Framework/01_Introduction.md new file mode 100644 index 0000000..1deaec4 --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/01_Introduction.md @@ -0,0 +1,186 @@ +--- +title: Introduction +--- + +# Foreword + +The AGL Application Framework is nothing new. However, the implementation used +up until the `lamprey` release has been retired starting with the `marlin` +release and replaced by a redesigned Application Framework one. However, this +new implementation isn't a 1:1 replacement, and as such it doesn't provide all +of the features of the previous Application Framework. Some of those will be +added back over time, others have been discarded in favor of more modern and/or +widely-used alternatives. + +With the `needlefish` release, further changes have been added, including a +[gRPC IPC](https://grpc.io/about), alongside a deprecated D-Bus one, as well as +using as using systemd units as opposed on using +[Desktop Entry specification](https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/) +to list applications, and relies entirely on systemd to start application, +rather than spawning them directly. + +Once all platforms transitioned to gRPC, the D-Bus functionality will be +removed entirely, mentioning it in only in documentation for history purposes. + +# Introduction + +As a provider of an integrated solution to build up on, AGL needs to define a +reliable and well-specified method for managing the deployment and integration +of applications and services, as well as the way they can interact with the +rest of the system. + +This is achieved by providing a common set of rules and components, known as +the Application Framework. By ensuring conformity to those rules, application +developers can have a good understanding of the requirements for creating and +packaging applications targeting AGL-based systems. Likewise, system developers +and integrators have a clear path for including such applications in AGL-based +products. + +The Application Framework's scope extends to the following areas: +- system services integration and lifecycle management +- user session management, including user-level applications and services + lifecycle management +- inter-process communication + +In order to be as simple as possible and avoid any unneeded custom +implementation, the Application Framework relies mainly on third-party +technologies and/or software components, most of those being maintained under +the [freedesktop.org](https://www.freedesktop.org) umbrella. Those include: + + +- [systemd](https://www.freedesktop.org/wiki/Software/systemd/): system + services and user session services management + + +- [D-Bus](https://www.freedesktop.org/wiki/Software/dbus/): inter-process + communication, with `needlefish' release deprecated phase. + +- [gRPC](https://grpc.io/about): inter-process communication, new recommmended + system-wide IPC, which should be used instead of D-Bus. + + +- [Desktop Entry specification](https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/): + application enumeration and startup, now in deprecated phase, systemd being + the one would list out applications and handling start-up. + +AGL also provides reference implementations whenever possible and relevant, +located in the [meta-agl](../../04_Developer_Guides/02_AGL_Layers/02_meta_agl.md) +layer under `meta-app-framework`. At the moment, the Application Framework +contains 2 such components: + +- `agl-session`: `systemd` unit files for user sessions management + +- `applaunchd`: application launcher service + +# Services management + +Both system and user services are managed by `systemd`, which provides a number +of important features, such as dependency management or service monitoring: +when starting a service, `systemd` will ensure any other units this service +depends on are available, and otherwise start those dependencies. Similarly, +`systemd` can automatically restart a crashed service, ensuring minimal +downtime. + +`systemd` also provides an efficient first layer of security through its +[sandboxing](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Sandboxing) +and other security-related options. + +It is also well integrated with D-Bus and can be used for a more fine-grained +control over D-Bus activated services: by delegating the actual service startup +to `systemd`, developers can take advantage of some of its advanced features, +allowing for improved reliability and security. + +Each service should be represented by a `systemd` unit file installed to the +appropriate location. More details can be obtained from the [Creating a New +Service](03_Creating_a_New_Service.md) document. + +# User session management + +Similarly, user sessions and the services they rely on are also managed by +`systemd`. + +AGL provides 2 `systemd` units: + + +1\. `agl-session@.service` is a template system service for managing user +sessions; it takes a username or UID as a parameter, creating a session for the +desired user. Instanciating this service can be achieved by enabling +`agl-session@USER.service`, for example by executing the following command on a +running system: + +``` +$ systemctl enable agl-session@USER.service +``` + +By default, AGL enables this service as `agl-session@agl-driver.service`, +running as user `agl-driver`. + +*Note: while you can create sessions for as many users as needed, only one +instance of `agl-session@.service` is allowed per user.* + + +2\. `agl-session.target` is a user target for managing user services and their +dependencies. It is started by `agl-session@.service`. + +By default, `agl-compositor` is part of this target. It is therefore +automatically started for user `agl-driver`. + +Any other service needed as part of the user session should similarly depend on +this target by appending the following lines to their unit file: + +``` +[Install] +WantedBy=agl-session.target +``` + +# Inter-process communication + +In order to provide a "standard", language-independent IPC mechanism and avoid +the need for maintaining custom bindings for each programming language to be +used on top of AGL, the Application Framework used to promote the use of +[D-Bus](https://www.freedesktop.org/wiki/Software/dbus/) as the preferred way +for applications to interact with services. Starting with `needlefish` release, +we instead switched to using [gRPC](https://grpc.io) for our system-wide IPC, +with D-Bus being kept to provide functionality to services and application +which haven't transitioned yet to using gRPC. + +Most services already included in AGL provide one or several D-Bus interfaces, +and can therefore interact with D-Bus capable applications and services +without requiring any additional component. Those services include, among +others: + +- [ConnMan](https://git.kernel.org/pub/scm/network/connman/connman.git/): + network connectivity + +- [BlueZ](http://www.bluez.org/): Bluetooth connectivity + +- [oFono](https://git.kernel.org/pub/scm/network/ofono/ofono.git): telephony + and modem management + +- [GeoClue](https://gitlab.freedesktop.org/geoclue/geoclue/-/wikis/home): + geolocation + +Similarly, we're in the phase of expanding various services to expose a +gRPC interface. + +# Application launcher service + +The Application Framework used to follow the guidelines of the [Desktop Entry +specification](https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/) +for application enumeration and startup, but with the `needlefish` release +instead it relies on systemd to provide that functionality, indirectly, by +using the `applaunchd` application. + +As no simple reference implementation exists for this part of the +specification, AGL provides an application launcher service named `applaunchd`. +This service is part of the default user session, and as such is automatically +started on session startup. It can therefore be considered always available. + +`applaunchd` enumerates applications installed on the system and provides a +D-bus (deprecated)/gRPC interface for services and applications to: +- query the list of available applications +- request the startup and/or activation of a specific application +- be notified when applications are started or terminated + +`applaunchd` with the D-Bus interface is described with more details in +[the following document](02_Application_Startup_Dbus.md). diff --git a/docs/06_Component_Documentation/Application_Framework/02_Application_Startup.md b/docs/06_Component_Documentation/Application_Framework/02_Application_Startup.md new file mode 100644 index 0000000..3cd7ca0 --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/02_Application_Startup.md @@ -0,0 +1,248 @@ +--- +title: Application Startup +--- + +# Introduction + +At system runtime, it may be necessary for applications to start other +applications on demand. Such actions can be executed in reaction to a user +request, or they may be needed to perform a specific task. + +In order to do so, running applications and services need an established way of +discovering installed applications and executing those. + +In order to provide a language-independent interface for applications and +service to use, AGL includes `applaunchd`, a system service. + +# Application launcher service + +The purpose of `applaunchd` is to enumerate applications available on the +system and provide a way for other applications to query this list and start +those on demand. It is also able to notify clients of the startup and +termination of applications it manages. + +To that effect, `applaunchd` provides a gRPC interface which other applications +can use in order to execute those actions. + +*Note: `applaunchd` will only send notifications for applications it started; +it isn't aware of applications started by other means (`systemd`, direct +executable call...), and therefore can't send notifications for those.* + +## Application discovery + +Applications are enumerated from systemd's list of available units based on the +pattern `agl-app*@*.service`, and are started and controled using their systemd +unit. Please note `applaunchd` allows only one instance of a given +application. + +## Application identifiers + +Each application is identified by a unique Application ID. Although this ID can +be any valid string, it is highly recommended to use the "reverse DNS" +convention in order to avoid potential name collisions. + +## gRPC interface + +The interface provides methods for the following actions: + +- retrieve the list of available applications +- request an application to be started +- subscribe to status events + +Moreover, with the gRPC the client subscribes to a status signal to be notified +when an application has successfully started or its execution terminated. + +The gRPC protobuf file provides a Request and Response arguments to RPC methods +even though in some cases these might be empty in order to allow forward +compatibility in case additional fields are required. +It is a good standard practice to follow up with these recommendation when +developing a new protobuf specification. + +### Applications list + +The `ListApplications` method allows clients to retrieve the list of available +applications. + +The `ListRequest` is an empty message, while `ListResponse` contains the following: + +``` +message AppInfo { + string id = 1; + string name = 2; + string icon_path = 3; +} + +message ListResponse { + repeated AppInfo apps = 1; +} +``` + +### Application startup request + +Applications can be started by using the `StartApplication` method, passing the +`StartRequest` message, defined as: + +``` +message StartRequest { + string id = 1; +} +``` + +In reply, the following `StartResponse` will be returned: + +``` +message StartResponse { + bool status = 1; + string message = 2; +} +``` + +The "message" string of `StartResponse` message will contain an error message +in case we couldn't start the application for whatever reason, or if the "id" +isn't a known application ID. The "status" string would be boolean set to +boolean `TRUE` otherwise. + +If the application is already running, `applaunchd` won't start another +instance, but instead reply with a `AppStatus` message setting the `status` +string to "started". + +### Status notifications + +The gRPC interface provides clients with a subscription model to receive +status events. Client should subscribe to `GetStatusEvents` method to receive +them. + +The `StatusRequest` is empty, while the `StatusResponse` is defined as +following: + +``` +message AppStatus { + string id = 1; + string status = 2; +} + +message LauncherStatus { +} + +message StatusResponse { + oneof status { + AppStatus app = 1; + LauncherStatus launcher = 2; + } +} +``` + +As mentioned above, the `status` string is set to "started" and is also emitted +if `applaunchd` receives a request to start an already running application. +This can be useful, for example, when switching between graphical applications: + +- the application switcher doesn't need to track the state of each application; + instead, it can simply send a `StartApplication` request to `applaunchd` + every time the user requests to switch to another application. Obviously, the + client needs to subscribe to get these events and act accordingly. +- the shell client then receives the `StatusResponse` with the message `status` + string set to "started" indicating it that it should activate the window with + the corresponding `id` string, or alternatively the string `status` is + set to "terminated" to denote that the application has been terminated, + forcibly or not + +## A deeper look at start-up, activation and application switching + +Application start-up, activation and application switching are sometimes +conflated into a single operation but underneath some of these are distinct +steps, and a bit flaky in some circumstances. +The [AGL compositor](../02_agl_compositor.md) has +some additional events which one can use when creating an application +start-up & switching scheme in different run-times. + +Start-up of application is handled entirely by `applaunchd` service while +activation -- the window which I want to display, but which has never been +shown, and application switching -- bring forward an application already +shown/displayed in the past, are operations handled entirely by the +AGL compositor. + +The issue stems from the fact that underneath `applaunchd` can't make any +guarantees when the application actually started, as it calls into libsystemd +API to start the specific application systemd unit. + +If `StartApplication` can't start the systemd unit, it returns a false +`status` boolean value and a error message in `StartResponse` message, but if +the application is indeed started we doesn't really know the *moment* when the +application is ready to be displayed. Additionally, the AGL compositor +performed the activation on its own when it detected that a new application +has been started, but that implicit activation can now be handled outside +by the desktop run-time/shell client. + +*Note: Some of the run-times still rely on the compositor to perform activation +as this synchronization part between `applaunchd` has not been implemented. The +plan is to migrate all of remaining run-times to using this approach.* + +### Start-up & activation + +This means that we require some sort of interaction between `StartApplication` +method and the events sent by the AGL compositor in order to correctly handle +start-up & activation of application. + +There are couple of ways of achieving that, either using Wayland native calls, +or using the gRPC proxy interface, which underneath is using the same Wayland +native calls. + +For the first approach, the AGL compositor has an `app_state` Wayland event +which contains the application ID, and an enum `app_state` that will propagate +the following application state events: + +``` + + + + + + +``` + +The `started` event can be used in correlation with the `StartApplication` +method from `applaunchd` such that upon received the `started` even, it can +explicitly activate that particular appid in order for the compositor to +display it. See [AGL compositor](../02_agl_compositor.md) +about how activation should be handled. + +*Note: These can only be received if by the client shell which binds to the +agl_shell interface*. + +Alternatively, when using the gRPC proxy one can register to receive these +status events similar to the `applaunchd` events, subscribing to +`AppStatusState` method from the grpc-proxy helper application, which has the +following protobuf messages: + +``` +message AppStateRequest { +} +message AppStateResponse { + int32 state = 1; + string app_id = 2; +} +``` + +The integer state maps to the `enum app_state` from the Wayland protocol, so +they are a 1:1 match. + +Here's the state diagram for the Qt homescreen implementation of the +application start-up: + +![Application_start](images/start_and_activation.png) + +### Application switching + +With the compositor providing application status events, it might seem that the +`applaunchd`'s, `GetStatusEvents` might be redundant, but in fact it is being +used to perform application switching. The run-time/shell client would in fact +subscribe to `GetStatusEvents` and each application wanting to switch to another +application would basically call `StartApplication`. That would eventually reach +the run-time/shell-client and have a handler that would ultimately activate the +application ID. + +![Application_switching](images/application_switching.png) + +*Note: In practice, the run-time/shell-client would subscribe to both `applaunchd` +and to the AGL compositor, either Wayland native events, or using the gPRC-proxy +helper client, although the diagrams show them partly decoupled*. diff --git a/docs/06_Component_Documentation/Application_Framework/02_Application_Startup_Dbus.md b/docs/06_Component_Documentation/Application_Framework/02_Application_Startup_Dbus.md new file mode 100644 index 0000000..f951e0e --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/02_Application_Startup_Dbus.md @@ -0,0 +1,190 @@ +--- +title: Application Startup with D-Bus +--- + +*Note: The that the D-Bus interface is in deprecation phase and for the time +being only available for application & services that still rely on them. Once +we migrate everything to gRPC, we will remove D-Bus IPC support. Please see +[Application Startup with gRPC](02_Application_Startup.md) for the latest +information* + +# Introduction + +At system runtime, it may be necessary for applications to start other applications +on demand. Such actions can be executed in reaction to a user request, or they may +be needed to perform a specific task. + +In order to do so, running applications and services need an established way of +discovering installed applications and executing those. The +[Desktop Entry specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html) +defines how applications can be discovered by using `.desktop` files, but there's no +simple reference implementation for this function. + +In order to provide a language-independent interface for applications and service to +use, AGL includes `applaunchd`, a user service part of the default session. + +*Note: as mentioned [previously](01_Introduction.md), services are managed using `systemd` +and are therefore not in the scope of this document.* + +# Application launcher service + +The purpose of `applaunchd` is to enumerate applications available on the system and +provide a way for other applications to query this list and start those on demand. +It is also able to notify clients of the startup and termination of applications it +manages. + +To that effect, `applaunchd` provides a D-Bus interface other applications can use +in order to execute those actions. + +*Note: `applaunchd` will only send notifications for applications it started; it isn't +aware of applications started by other means (`systemd`, direct executable call...), +and therefore can't send notifications for those.* + +## Application discovery + +On startup, `applaunchd` inspects all `.desktop` files present under the `applications/` +subfolder of any element of the `XDG_DATA_DIRS` environment variable, ignoring all entries +containing either the `NoDisplay=true` or `Hidden=true` lines. + +It then looks for the following keys: +- `Terminal` +- `DBusActivatable` + +If the desktop entry file contains the `Terminal` key set to `true`, then the application +is marked as a non-graphical one. As such, it won't be included in the applications list +if the client requests only graphical applications. + +If `DBusActivatable` is set to `true`, then the application is marked as D-Bus activated. +Additionally, `applaunchd` will search for a corresponding D-Bus service file in case this +line is missing. This is a workaround allowing D-Bus activated applications providing +an incomplete desktop entry file (i.e missing the `DBusActivatable` key) to be +identified as such. + +### Requirements for D-Bus activation + +`applaunchd` will always start D-Bus activatable applications using D-Bus activation +instead of executing the command line stated in the desktop entry file. + +This is handled by calling the `Activate` method of the +[org.freedesktop.Application](https://specifications.freedesktop.org/desktop-entry-spec/1.1/ar01s07.html) +interface with an empty argument. + +As a consequence, all D-Bus activatable applications **must** implement this D-Bus +interface. + +## Application identifiers + +Each application is identified by a unique Application ID. Although this ID can be +any valid string, it is highly recommended to use the "reverse DNS" convention in order +to avoid potential name collisions and ease D-Bus integration. + +The application ID is set in the desktop entry file itself for +[graphical applications](04_Creating_a_New_Application_Dbus.md#graphical-applications): +it is the value of the `StartupWMClass` field, which must be identical to the `app-id` +advertised through the Wayland XDG toplevel protocol. In case this field is missing +(as is usually the case for non-graphical application), the application ID will be the +desktop entry file name, stripped from its `.desktop` extension. + +## D-Bus interface + +The `applaunchd` D-Bus interface is named `org.automotivelinux.AppLaunch`. The object +path for `applaunchd` is `/org/automotivelinux/AppLaunch`. The interface provides methods +for the following actions: +- retrieve the list of available applications; the client can choose to retrieve all + available applications, or only those suitable for a graphical environment +- request an application to be started + +Moreover, signals are available for clients to be notified when an application has +successfully started or its execution terminated. + +### Applications list + +The `listApplications` method allows clients to retrieve the list of available applications. +It takes one boolean argument named `graphical`: +- if set to `true`, only applications suitable for graphical environments are returned +- otherwise, the list contains all applications + +This method returns an array of variants (type `av`), each element being a structure made up +of 3 strings (type `(sss)`): +- the application ID +- the application's displayed name +- the full path to the application icon file (or an empty string if no icon was specified in + the application's desktop entry file) + +### Application startup request + +Applications can be started by using the `start` method, passing the corresponding application +ID as the only argument. This method doesn't return any data. + +If the application is already running, `applaunchd` won't start another instance, but instead +emit a `started` signal to notify clients the application is ready. + +### Status notifications + +The `org.automotivelinux.AppLaunch` interface provides 2 signals clients can connect to: +- `started` indicates an application has started + - for D-Bus activated applications, it is emitted upon successful completion of the + call to the `Activate` method of the `org.freedesktop.Application` interface + - for other applications, this signal is emitted as soon as the child process has been + successfully created +- `terminated` is emitted when an application quits + +Both signals have an additional argument named `appid`, containing the ID of the application +affected by the event. + +As mentioned above, the `started` signal is also emitted if `applaunchd` receives a request to +start an already running application. This can be useful, for example, when switching between +graphical applications: +- the application switcher doesn't need to track the state of each application; instead, it can + simply send a `start` request to `applaunchd` every time the user requests to switch to another + application +- the desktop environment then receives the `started` signal, indicating it should activate the + window with the corresponding `app-id` + +## Testing + +`applaunchd` can be manually tested using the `gdbus` command-line tool: + +1. Query the application list (graphical applications only): + +``` +$ gdbus call --session --dest "org.automotivelinux.AppLaunch" \ + --object-path "/org/automotivelinux/AppLaunch" \ + --method "org.automotivelinux.AppLaunch.listApplications" \ + true +``` + +This command will output something similar to what follows: + +``` +([<('navigation', 'Navigation', '/usr/share/icons/hicolor/scalable/navigation.svg')>, + <('settings', 'Settings', '/usr/share/icons/hicolor/scalable/settings.svg')>, + <('dashboard', 'Dashboard', '/usr/share/icons/hicolor/scalable/dashboard.svg')>, + <('hvac', 'HVAC', '/usr/share/icons/hicolor/scalable/hvac.svg')>, + <('org.freedesktop.weston.wayland-terminal', 'Weston Terminal', '/usr/share/icons/Adwaita/scalable/apps/utilities-terminal-symbolic.svg')>],) +``` + +2. Request startup of the `org.freedesktop.weston.wayland-terminal` application: + +``` +$ gdbus call --session --dest "org.automotivelinux.AppLaunch" \ + --object-path "/org/automotivelinux/AppLaunch" \ + --method "org.automotivelinux.AppLaunch.start" \ + "org.freedesktop.weston.wayland-terminal" +``` + +3. Monitor signals emitted by `applaunchd`: + +``` +$ gdbus monitor --session --dest "org.automotivelinux.AppLaunch" +``` + +This results in the following output when starting, then exiting, the +`org.freedesktop.weston.wayland-terminal` application: + +``` +Monitoring signals from all objects owned by org.automotivelinux.AppLaunch +The name org.automotivelinux.AppLaunch is owned by :1.4 +/org/automotivelinux/AppLaunch: org.automotivelinux.AppLaunch.started ('org.freedesktop.weston.wayland-terminal',) +/org/automotivelinux/AppLaunch: org.automotivelinux.AppLaunch.terminated ('org.freedesktop.weston.wayland-terminal',) +``` diff --git a/docs/06_Component_Documentation/Application_Framework/03_Creating_a_New_Service.md b/docs/06_Component_Documentation/Application_Framework/03_Creating_a_New_Service.md new file mode 100644 index 0000000..69bde48 --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/03_Creating_a_New_Service.md @@ -0,0 +1,151 @@ +--- +title: Creating a New Service +--- + +Services are software running in the background and providing, as their name suggests, +various services to other software: access to specific system hardware, connectivity +management, and network servers. Services can be split into 2 categories: + +- **System services:** those usually run as a privileged user and make use of shared system + resources which they should have exclusive access to + +- **User services:** such services run as part of an unprivileged user's session and can + only be called by said user + +# Basic requirements + +The only mandatory requirement is that service packages provide a `.service` file +so they can be properly managed by `systemd`. This file must be installed to a specific +location, determined by the service type (system or user): + +- `/usr/lib/systemd/system/` for system services + +- `/usr/lib/systemd/user/` for user services + +Below is an example of a simple user service, running in a graphical session and +therefore requiring a compositor to be already running before the service starts: + +``` +[Unit] +Requires=agl-compositor.service +After=agl-compositor.service + +[Service] +Type=simple +ExecStart=/usr/bin/homescreen +Restart=on-failure + +[Install] +WantedBy=agl-session.target +``` + +The `WantedBy=agl-session.target` indicates the service is part of the default AGL +user session, as mentioned in the [Application Framework](01_Introduction.md#user-session-management) +documentation. + +The `Restart=on-failure` directive ensures the service will be automatically +restarted by `systemd` in case it crashes. + +More details about `systemd` service files can be found in the +[systemd documentation](https://www.freedesktop.org/software/systemd/man/systemd.service.html). + +# D-Bus activation + +Services can also provide a D-Bus interface. In this case, they need not be started +on system boot (or user session startup in the case of user services) but can be +automatically started only when a client sends a request to the D-Bus name the service +registers. + +D-Bus activated services must name their `systemd` service file `dbus-NAME.service` +where `NAME` is the D-Bus name registered by the service. This file must include the +following lines: + +``` +[Service] +Type=dbus +BusName=NAME +ExecStart=/path/to/executable +``` + +In addition, they must provide a D-Bus service file named `NAME.service` and installed +to one of the following locations: + +- `/usr/share/dbus-1/system-services` for system services + +- `/usr/share/dbus-1/services` for user services + +The contents of the D-Bus service file must be the following: + +``` +[D-BUS Service] +Name=NAME +Exec=/path/to/executable +SystemdService=dbus-NAME.service +``` + +This ensures the service can be safely activated through D-Bus and no conflict will occur +between `systemd` and the D-Bus daemon. + +More details about D-Bus activation can be found in the +[D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-starting-services), +under the "Message Bus Starting Services (Activation)" section. + +# Services startup + +For D-Bus activated services, no additional action is required as those will be automatically +started whenever needed. Other services, however, need a few more steps in order to be +executed on system or session startup. + +## System services + +System services can take advantage of the Yocto `systemd` class which automates the process of +enabling such services. + +1\. Ensure the recipe inherits from the `systemd` class: + +``` +inherit systemd +``` + +2\. Declare the system services that needs to be enabled on boot: + +``` +SYSTEMD_SERVICE:${PN} = "NAME.service" +``` + +3\. Ensure the `FILES` variable includes the systemd service directory the corresponding +file will be installed to: + +``` +FILES:${PN} = "\ + ... + ${systemd_system_unitdir}/* \ +" +``` + +## User services + +The `systemd` class doesn't provide an equivalent mechanism for user services. This must +therefore be done manually as part of the package's install process. + +1\. Make the service a part of the user session: + +``` +do_install:append() { + install -d ${D}${systemd_user_unitdir}/agl-session.target.wants + ln -s ../NAME.service ${D}${systemd_user_unitdir}/agl-session.target.wants/NAME.service +} +``` + +This ensures `agl-session.target` depends on `NAME.service`, the latter being therefore +automatically started on session creation. + +2\. Ensure the `FILES` variable includes the systemd service directory the corresponding +file will be installed to: + +``` +FILES:${PN} = "\ + ... + ${systemd_user_unitdir}/* \ +" +``` diff --git a/docs/06_Component_Documentation/Application_Framework/04_Creating_a_New_Application_Dbus.md b/docs/06_Component_Documentation/Application_Framework/04_Creating_a_New_Application_Dbus.md new file mode 100644 index 0000000..2ee6bdf --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/04_Creating_a_New_Application_Dbus.md @@ -0,0 +1,139 @@ +--- +title: Creating a New Application with D-bus activation +--- + +*Note: The that the D-Bus interface is in deprecation phase and for the time +being only available for application & services that still rely on them. Once +we migrate everything to gRPC, we will remove D-Bus IPC support.* + +Applications are: + +- Software designed to perform a specific task during a limited amount of time. +- Graphical interface allowing user to interact with. + +Applications are executed by `applaunchd`, the AGL +[application launcher service](02_Application_Startup_Dbus.md). + +# Basic requirements + +In order to be enumerated by `applaunchd`, applications must provide the a `.desktop` file, as +defined by the [Desktop Entry specification](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html). + +The desktop entry file should be installed to `/usr/share/applications` (or the `applications` +sub-directory of any entry present in the `XDG_DATA_DIRS` environment variable) and have a +meaningful name. It is considered good practice to use reverse-DNS notation for the desktop +entry file name, following the recommendations of the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names): +- this avoids potential name collisions with other desktop entry files +- it makes it easier to enable [D-Bus activation](#d-bus-activation) during the application + development cycle if needed +- for [graphical applications](#graphical-applications), it ensures the chosen Wayland `app-id` + will be unique + +Such a file must contain at least the following keys: +- `Type`: desktop entry type, must be set to `Application` +- `Name`: application name, as it should be displayed in menus and application launchers +- `Exec`: full path to the main executable + +Below is an example of a minimal desktop entry file: + +``` +[Desktop Entry] +Type=Application +Name=Example Application +Exec=/usr/bin/example-app +``` + +Graphical applications must also provide an `Icon` entry pointing to the application icon. +The value for this entry must either be the full path to the icon's file or, for icons part +of an existing [icon theme](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html), +the name of an element of this theme. + +In addition, a number of optional fields can be used to change how `applaunchd` will consider the +application: +- `Version`: version of the Desktop Entry Specification the file conforms to, must be `1.5` +- `Hidden`: boolean value, if `true` the application is always ignored by `applaunchd` and + won't be listed nor executed +- `Terminal`: boolean value, if `true` the application is excluded when requesting the list of + graphical applications from `applaunchd` +- `DBusActivatable`: boolean value, must be `true` for [D-Bus activated applications](#d-bus-activation) +- `Implements`: list of D-Bus interfaces the application implements, only used for D-Bus activated + applications. + +Finally, graphical applications may also define the `StartupWMClass` key in some cases. Please +refer to the [graphical applications](#graphical-applications) section for more information. + +# D-Bus activation + +Similarly to [services](03_Creating_a_New_Service.md#d-bus-activation), applications can +also be activated through D-Bus. + +Such applications must name their `.desktop` file after the D-Bus name they register. In addition, +this file must contain the following entries: + +``` +DBusActivatable=true +Implements=IFACE1;IFACE2;... +``` + +Where `IFACEn` are the names of the D-Bus interfaces the application implements. + +In addition, they must provide a D-Bus service file named `NAME.service` and installed +to `/usr/share/dbus-1/services`. + +The contents of the D-Bus service file must be the following: + +``` +[D-BUS Service] +Name=NAME +Exec=/path/to/executable +``` + +For example, an application registering the `org.automotivelinux.Example` D-Bus name +and implementing the `org.automotivelinux.Example.Search1` and `org.automotivelinux.Example.Execute1` +interfaces would provide the following files: + +* Desktop entry (`/usr/share/applications/org.automotivelinux.Example.desktop`): + +``` +[Desktop Entry] +Type=Application +Version=1.5 +Name=Example Application +Exec=/usr/bin/example-app +Icon=example-icon +Terminal=false +DBusActivatable=true +Implements=org.automotivelinux.Example.Search1;org.automotivelinux.Example.Execute1 +``` + +* D-Bus service file (`/usr/share/dbus-1/services/org.automotivelinux.Example.service`): + +``` +[D-BUS Service] +Name=org.automotivelinux.Example +Exec=/usr/bin/example-app +``` + +*Note: in addition to their own D-Bus interface, D-Bus activated applications must also +implement the `org.freedesktop.Application` interface as defined in the +[Desktop Entry specification](https://specifications.freedesktop.org/desktop-entry-spec/1.1/ar01s07.html).* + +# Graphical applications + +In addition, graphical applications need to comply with a few more requirements: + +1\. Each application must set a Wayland application ID appropriately as soon as its main window +is created. + +2\. The `app-id` must be specified in the desktop entry file by adding the following line: + +``` +StartupWMClass=APP_ID +``` + +3\. The desktop entry file must be named `APP_ID.desktop`. + +Doing so will ensure other software can associate the actual `app-id` to the proper application. + +*Note: application ID's are set using the [XDG toplevel](https://wayland-book.com/xdg-shell-basics/xdg-toplevel.html) +Wayland interface.* diff --git a/docs/06_Component_Documentation/Application_Framework/images/application_switching.msc b/docs/06_Component_Documentation/Application_Framework/images/application_switching.msc new file mode 100755 index 0000000..ceeab7c --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/images/application_switching.msc @@ -0,0 +1,29 @@ +#!/usr/bin/mscgen -Tpng + +msc { + hscale="1.5"; + + u [label = "touch/pointer event" ], + l [label = "launcher app" ], + s [label = "runtime/shell-client"], + a [label = "applaunchd" ], + g [label = "gRPC-proxy" ], + c [label = "compositor" ], + d [label = "libsystemd"]; + + |||; + + --- [label = "initial phase - subscribe for signal/status events, assume app_id already started" ]; + + s >> a [label = "subscribe for applaunchd GetStatusEvents"]; + + --- [label = "handling of application switching" ]; + + u => l [label = "tapShortCut(appid)" ]; + l => a [label = "StartApplication(appid)"]; + a => d [label = "start application's systemd unit"]; + d => a [label = "return status from starting systemd unit"]; + a => s [label = "StartResponse(status = TRUE)"]; + a => s [label = "StatusResponse(app_id, 'started')"]; + s => c [label = "activate_app(app_id)"]; +} diff --git a/docs/06_Component_Documentation/Application_Framework/images/application_switching.png b/docs/06_Component_Documentation/Application_Framework/images/application_switching.png new file mode 100644 index 0000000..0b5584a Binary files /dev/null and b/docs/06_Component_Documentation/Application_Framework/images/application_switching.png differ diff --git a/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.msc b/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.msc new file mode 100755 index 0000000..d835f8b --- /dev/null +++ b/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.msc @@ -0,0 +1,30 @@ +#!/usr/bin/mscgen -Tpng + +msc { + hscale="1.5"; + + u [label = "touch/pointer event" ], + s [label = "runtime/shell-client"], + a [label = "applaunchd" ], + g [label = "gRPC-proxy" ], + c [label = "compositor" ], + d [label = "libsystemd"]; + + |||; + + --- [label = "initial phase - subscribe for signal/status events" ]; + + s >> g [label = "subscribe for AGL compositor AppStatusState"]; + g >> c [label = "listen for app_state Wayland events"]; + + --- [label = "handling start-up & activation" ]; + + u => s [label = "tapShortCut(appid)" ]; + s => a [label = "StartApplication(appid)"]; + a => d [label = "start application's systemd unit"]; + d => a [label = "return status from starting systemd unit"]; + a => s [label = "StartResponse(status = TRUE)"]; + c => g [label = "app_state(app_state = APP_STARTED)"]; + g => s [label = "AppStatusResponse(app_id, APP_STARTED)"]; + s => c [label = "activate_app(app_id)"]; +} diff --git a/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.png b/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.png new file mode 100644 index 0000000..593fc0e Binary files /dev/null and b/docs/06_Component_Documentation/Application_Framework/images/start_and_activation.png differ -- cgit 1.2.3-korg