policy: Init policy framework API 97/24197/4
authorMarius Vlad <marius.vlad@collabora.com>
Tue, 3 Mar 2020 12:25:25 +0000 (14:25 +0200)
committerMarius Vlad <marius.vlad@collabora.com>
Wed, 11 Mar 2020 14:44:10 +0000 (16:44 +0200)
This patch adds the policy framework, comprised from hooks which a
policy engine can further customize, and which are checked upon surface
creation, commit and activation. Users should create specialized
versions of these callbacks when creating/desining a policy engine.

Further more, it adds the posibility to further customize the policy
engine by adding new states, events and policy rules.
By default, the policy API is aware of known states like 'start' or
'stop', but also 'show' and 'hide' as events.

A policy rule would allow to define the setup in which an event can happen
based on the state of the rule compared to that of the system, the action
event itself, the application and (optional) an timeout.

The policy rules are there to specify the state, the event, and
application, but it is ultimately handled by a hook which will be called
to handle the event. The compositor will arrange to pass all that
information back to the handler, so the policy engine is in control to
check in what circumstances the policy can be satisfied.

These policy rules allow to handle transitional states which are common
in AGL. For instance:

If one would want to display the application 'navigation' in 3
seconds after and state has been changed to 'start', it should do
add the following rule:

('navigation', STATE_START, EVENT_SHOW, timeout, main_output)

Then, when a 'STATE_START' state is propagated to the compositor the
policy API will trigger a state change signal which in turn wil arm an
timer to execute the event handler after a timeout 'timeout'.

Bug-AGL: SPEC-3217

Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: Ie03c5f9b1ddb964949e4f9797cbbe2dd2b32a6b6

meson.build
src/desktop.c
src/ivi-compositor.h
src/layout.c
src/policy.c [new file with mode: 0644]
src/policy.h [new file with mode: 0644]

index 9d6e800..cfe446d 100644 (file)
@@ -120,6 +120,7 @@ srcs_agl_compositor = [
        'src/main.c',
        'src/desktop.c',
        'src/layout.c',
+       'src/policy.c',
        'src/shell.c',
        'shared/option-parser.c',
        'shared/os-compatibility.c',
index eaace19..7c6c19a 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include "ivi-compositor.h"
+#include "policy.h"
 
 #include <libweston/libweston.h>
 #include <libweston-desktop/libweston-desktop.h>
@@ -80,6 +81,13 @@ desktop_surface_added(struct weston_desktop_surface *dsurface, void *userdata)
        surface->dsurface = dsurface;
        surface->role = IVI_SURFACE_ROLE_NONE;
 
+       if (ivi->policy && ivi->policy->api.surface_create &&
+           !ivi->policy->api.surface_create(surface, ivi)) {
+               free(surface);
+               wl_client_post_no_memory(client);
+               return;
+       }
+
        weston_desktop_surface_set_user_data(dsurface, surface);
 
        if (ivi->shell_client.ready) {
@@ -131,6 +139,12 @@ desktop_committed(struct weston_desktop_surface *dsurface,
 {
        struct ivi_surface *surface =
                weston_desktop_surface_get_user_data(dsurface);
+       struct ivi_policy *policy = surface->ivi->policy;
+
+       if (policy && policy->api.surface_commited &&
+           !policy->api.surface_commited(surface, surface->ivi))
+               return;
+
        weston_compositor_schedule_repaint(surface->ivi->compositor);
 
        switch (surface->role) {
index 446390d..4da12bc 100644 (file)
@@ -82,6 +82,7 @@ struct ivi_compositor {
        struct wl_list surfaces; /* ivi_surface.link */
 
        struct weston_desktop *desktop;
+       struct ivi_policy *policy;
 
        struct wl_list pending_surfaces;
 
index 0c65b6d..b531ed1 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include "ivi-compositor.h"
+#include "policy.h"
 
 #include <assert.h>
 #include <string.h>
@@ -345,10 +346,17 @@ ivi_layout_activate(struct ivi_output *output, const char *app_id)
        struct weston_desktop_surface *dsurf;
        struct weston_view *view;
        struct weston_geometry geom;
+       struct ivi_policy *policy = output->ivi->policy;
 
        surf = ivi_find_app(ivi, app_id);
        if (!surf)
                return;
+
+       if (policy && policy->api.surface_activate &&
+           !policy->api.surface_activate(surf, surf->ivi)) {
+               return;
+       }
+
 #ifdef AGL_COMP_DEBUG
        weston_log("Found app_id %s\n", app_id);
 #endif
diff --git a/src/policy.c b/src/policy.c
new file mode 100644 (file)
index 0000000..78994fa
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * Copyright © 2020 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <string.h>
+#include <libweston/zalloc.h>
+#include <assert.h>
+
+#include "shared/helpers.h"
+#include "ivi-compositor.h"
+
+#include "policy.h"
+
+static void
+ivi_policy_remove_state_event(struct state_event *st_ev)
+{
+       free(st_ev->name);
+       wl_list_remove(&st_ev->link);
+       free(st_ev);
+}
+
+static void
+ivi_policy_destroy_state_event(struct wl_list *list)
+{
+       struct state_event *st_ev, *tmp_st_ev;
+       wl_list_for_each_safe(st_ev, tmp_st_ev, list, link)
+               ivi_policy_remove_state_event(st_ev);
+}
+
+static struct state_event *
+ivi_policy_state_event_create(uint32_t val, const char *value)
+{
+       struct state_event *ev_st = zalloc(sizeof(*ev_st));
+       size_t value_len = strlen(value);
+
+       ev_st->value = val;
+       ev_st->name = zalloc(sizeof(char) * value_len + 1);
+       memcpy(ev_st->name, value, value_len);
+
+       return ev_st;
+}
+
+void
+ivi_policy_add_state(struct ivi_policy *policy, uint32_t state, const char *value)
+{
+       struct state_event *ev_st;
+       if (!policy)
+               return;
+
+       ev_st = ivi_policy_state_event_create(state, value);
+       wl_list_insert(&policy->states, &ev_st->link);
+}
+
+void
+ivi_policy_add_event(struct ivi_policy *policy, uint32_t ev, const char *value)
+{
+       struct state_event *ev_st;
+       if (!policy)
+               return;
+
+       ev_st = ivi_policy_state_event_create(ev, value);
+       wl_list_insert(&policy->events, &ev_st->link);
+}
+
+static void
+ivi_policy_add_default_states(struct ivi_policy *policy)
+{
+       const char *default_states[] = { "invalid", "start", "stop", "reverse" };
+       if (!policy)
+               return;
+
+       for (uint32_t i = 0; i < ARRAY_LENGTH(default_states); i ++) {
+               struct state_event *ev_st =
+                       ivi_policy_state_event_create(i, default_states[i]);
+               wl_list_insert(&policy->states, &ev_st->link);
+       }
+}
+
+static void
+ivi_policy_add_default_events(struct ivi_policy *policy)
+{
+       const char *default_events[] = { "show", "hide" };
+       if (!policy)
+               return;
+
+       for (uint32_t i = 0; i < ARRAY_LENGTH(default_events); i ++) {
+               struct state_event *ev_st =
+                       ivi_policy_state_event_create(i, default_events[i]);
+               wl_list_insert(&policy->events, &ev_st->link);
+       }
+}
+
+static void
+ivi_policy_try_event(struct ivi_a_policy *a_policy)
+{
+       struct ivi_policy *policy = a_policy->policy;
+
+       if (policy->api.policy_rule_try_event)
+           return policy->api.policy_rule_try_event(a_policy);
+}
+
+static int
+ivi_policy_try_event_timeout(void *user_data)
+{
+       struct ivi_a_policy *a_policy = user_data;
+       ivi_policy_try_event(a_policy);
+       return 0;
+}
+
+static void
+ivi_policy_setup_event_timeout(struct ivi_policy *ivi_policy,
+                              struct ivi_a_policy *a_policy)
+{
+       struct ivi_compositor *ivi = ivi_policy->ivi;
+       struct wl_display *wl_display = ivi->compositor->wl_display;
+       struct wl_event_loop *loop = wl_display_get_event_loop(wl_display);
+
+       a_policy->timer = wl_event_loop_add_timer(loop,
+                                                 ivi_policy_try_event_timeout,
+                                                 a_policy);
+
+       wl_event_source_timer_update(a_policy->timer, a_policy->timeout);
+}
+
+static void
+ivi_policy_check_policies(struct wl_listener *listener, void *data)
+{
+       struct ivi_a_policy *a_policy;
+       struct ivi_policy *ivi_policy =
+               wl_container_of(listener, ivi_policy, listener_check_policies);
+
+       ivi_policy->state_change_in_progress = true;
+       wl_list_for_each(a_policy, &ivi_policy->policies, link) {
+               if (ivi_policy->current_state == a_policy->state) {
+                       /* check the timeout first to see if there's a timeout */
+                       if (a_policy->timeout > 0)
+                               ivi_policy_setup_event_timeout(ivi_policy,
+                                                              a_policy);
+                       else
+                               ivi_policy_try_event(a_policy);
+               }
+       }
+
+       ivi_policy->previous_state = ivi_policy->current_state;
+       ivi_policy->state_change_in_progress = false;
+}
+
+
+struct ivi_policy *
+ivi_policy_create(struct ivi_compositor *ivi,
+                  const struct ivi_policy_api *api, void *user_data)
+{
+       struct ivi_policy *policy = zalloc(sizeof(*policy));
+
+       policy->user_data = user_data;
+       policy->ivi = ivi;
+       policy->state_change_in_progress = false;
+
+       policy->api.struct_size =
+               MIN(sizeof(struct ivi_policy_api), api->struct_size);
+       /* install the hooks */
+       memcpy(&policy->api, api, policy->api.struct_size);
+
+       /* to trigger a check for policies use */
+       wl_signal_init(&policy->signal_state_change);
+
+       policy->listener_check_policies.notify = ivi_policy_check_policies;
+       wl_signal_add(&policy->signal_state_change,
+                     &policy->listener_check_policies);
+
+       policy->current_state = AGL_SHELL_POLICY_STATE_INVALID;
+       policy->previous_state = AGL_SHELL_POLICY_STATE_INVALID;
+
+       /* policy rules */
+       wl_list_init(&policy->policies);
+
+       wl_list_init(&policy->events);
+       wl_list_init(&policy->states);
+
+       /* add the default states and enums */
+       ivi_policy_add_default_states(policy);
+       ivi_policy_add_default_events(policy);
+
+       return policy;
+}
+
+void
+ivi_policy_destroy(struct ivi_policy *ivi_policy)
+{
+       struct ivi_a_policy *a_policy, *a_policy_tmp;
+
+       if (!ivi_policy)
+               return;
+
+       wl_list_for_each_safe(a_policy, a_policy_tmp,
+                             &ivi_policy->policies, link) {
+               free(a_policy->app_id);
+               wl_list_remove(&a_policy->link);
+               free(a_policy);
+       }
+
+       ivi_policy_destroy_state_event(&ivi_policy->states);
+       ivi_policy_destroy_state_event(&ivi_policy->events);
+
+       free(ivi_policy);
+}
+
+
+/* verifies if the state is one has been added */
+static bool
+ivi_policy_state_is_known(uint32_t state, struct ivi_policy *policy)
+{
+       struct state_event *ev_st;
+
+       wl_list_for_each(ev_st, &policy->states, link) {
+               if (ev_st->value == state) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/*
+ * The generic way would be the following:
+ *
+ * - 'car' is in 'state' ->
+ *     { do 'event' for app 'app_id' at 'timeout' time if same state as 'car_state' }
+ *
+ * a 0 timeout means, immediately, a timeout > 0, means to install timer an
+ * execute when timeout expires
+ *
+ * The following happens:
+ * 'car' changes its state -> verify what policy needs to be run
+ * 'car' in same state -> no action
+ *
+ */
+int
+ivi_policy_add(struct ivi_policy *policy, const char *app_id, uint32_t state,
+              uint32_t event, uint32_t timeout, struct wl_resource *output_res)
+{
+       size_t app_id_len;
+       struct weston_head *head = weston_head_from_resource(output_res);
+       struct weston_output *woutput = weston_head_get_output(head);
+       struct ivi_output *output = to_ivi_output(woutput);
+       struct ivi_a_policy *a_policy;
+
+       if (!policy) {
+               weston_log("Failed to retrieve policy!\n");
+               return -1;
+       }
+
+       a_policy = zalloc(sizeof(*a_policy));
+       if (!a_policy)
+               return -1;
+
+       if (policy->state_change_in_progress)
+               return -1;
+
+       /* we should be allow to do this in the first place, only if the
+        * hooks allows us to  */
+       if (policy->api.policy_rule_allow_to_add &&
+           !policy->api.policy_rule_allow_to_add(policy))
+               return -1;
+
+       if (!ivi_policy_state_is_known(state, policy))
+               return -1;
+
+       app_id_len = strlen(app_id);
+       a_policy->app_id = zalloc(sizeof(char) * app_id_len + 1);
+       memcpy(a_policy->app_id, app_id, app_id_len);
+
+       a_policy->state = state;
+       a_policy->event = event;
+       a_policy->timeout = timeout;
+       a_policy->output = output;
+       a_policy->policy = policy;
+
+       wl_list_insert(&policy->policies, &a_policy->link);
+
+       return 0;
+}
+
+/* we start with 'invalid' state, so a initial state to even 'stop' should
+ * trigger a check of policies
+ */
+int
+ivi_policy_state_change(struct ivi_policy *policy, uint32_t state)
+{
+       bool found_state = false;
+       if (!policy) {
+               weston_log("Failed to retrieve policy!\n");
+               return -1;
+       }
+
+       if (policy->current_state == state) {
+               return -1;
+       }
+
+       /* if we don't know the state, make sure it is first added */
+       found_state = ivi_policy_state_is_known(state, policy);
+       if (!found_state) {
+               return -1;
+       }
+
+       /* current_state is actually the new state */
+       policy->current_state = state;
+
+       /* signal that we need to check the current policies */
+       wl_signal_emit(&policy->signal_state_change, policy);
+
+       return 0;
+}
diff --git a/src/policy.h b/src/policy.h
new file mode 100644 (file)
index 0000000..947c326
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2020 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef POLICY_H
+#define POLICY_H
+
+#include "ivi-compositor.h"
+
+/* default state, invalid should at least be in order
+ * to signal states */
+#ifndef AGL_SHELL_POLICY_STATE_INVALID
+#define AGL_SHELL_POLICY_STATE_INVALID 0
+#endif
+
+/* default events */
+#ifndef AGL_SHELL_POLICY_EVENT_SHOW
+#define AGL_SHELL_POLICY_EVENT_SHOW 0
+#endif
+
+#ifndef AGL_SHELL_POLICY_EVENT_HIDE
+#define AGL_SHELL_POLICY_EVENT_HIDE 1
+#endif
+
+struct ivi_policy;
+
+struct state_event {
+       uint32_t value;
+       char *name;
+       struct wl_list link;    /* ivi_policy::states or ivi_policy::events */
+};
+
+struct ivi_a_policy {
+       struct ivi_policy *policy;
+
+       char *app_id;
+       uint32_t state;
+       uint32_t event;
+       uint32_t timeout;
+       struct ivi_output *output;
+       struct wl_event_source *timer;  /* for policies that have a timeout */
+
+       struct wl_list link;    /* ivi_policy::ivi_policies */
+};
+
+struct ivi_policy_api {
+       size_t struct_size;
+
+       bool (*surface_create)(struct ivi_surface *surf, void *user_data);
+       bool (*surface_commited)(struct ivi_surface *surf, void *user_data);
+       bool (*surface_activate)(struct ivi_surface *surf, void *user_data);
+
+       bool (*surface_activate_by_default)(struct ivi_surface *surf, void *user_data);
+
+       /** see also ivi_policy_add(). If set this will be executed before
+        * adding a new policy rule  */
+       bool (*policy_rule_allow_to_add)(void *user_data);
+
+       /** this callback will be executed when a there's a policy state change */
+       void (*policy_rule_try_event)(struct ivi_a_policy *a_policy);
+};
+
+struct ivi_policy {
+       struct ivi_compositor *ivi;
+       /* user-defined hooks */
+       struct ivi_policy_api api;
+       void *user_data;
+
+       /* represents the policy rules */
+       struct wl_list policies;        /* ivi_a_policy::link */
+
+       /* no state update chnage is being done as long as we have the same
+        * state */
+       uint32_t current_state;
+       uint32_t previous_state;
+
+       /* guards against current in change in progress */
+       bool state_change_in_progress;
+
+       /* additional states which can be verified in
+        * ivi_policy_api::policy_rule_try_event() */
+       struct wl_list states;  /* state_event::link */
+       struct wl_list events;  /* state_event::link */
+
+       /* necessary to for signaling the state change */
+       struct wl_listener listener_check_policies;
+       struct wl_signal signal_state_change;
+};
+
+
+/** Initialize the policy setup
+ *
+ * Policy engine should call ivi_policy_create() with its own ivi_policy_api
+ * setup.
+ */
+struct ivi_policy *
+ivi_policy_create(struct ivi_compositor *compositor,
+                  const struct ivi_policy_api *api, void *user_data);
+
+/** Destroys the policy setup
+ *
+ */
+void
+ivi_policy_destroy(struct ivi_policy *ivi_policy);
+
+/** Add a policy rule.
+ *
+ * ivi_policy_api::policy_rule_allow_to_add() can be used to limit adding
+ * policy rules.
+ *
+ * Returns 0 in case of success, or -1 in case of failure.
+ *
+ */
+int
+ivi_policy_add(struct ivi_policy *policy, const char *app_id, uint32_t state,
+              uint32_t event, uint32_t timeout, struct wl_resource *output_res);
+
+/** Trigger a state change. This should be called **each time** there is a need
+ * to apply the policy rules.
+ *
+ * Returns 0 in case of success, or -1 in case of failure.
+ */
+int
+ivi_policy_state_change(struct ivi_policy *policy, uint32_t state);
+
+
+/** Add a new state. The state can be verified in ivi_policy_api::policy_rule_try_event()
+ *
+ */
+void
+ivi_policy_add_state(struct ivi_policy *policy, uint32_t state, const char *value);
+
+/** Add a new event. The event can be verified in ivi_policy_api::policy_rule_try_event()
+ *
+ */
+void
+ivi_policy_add_event(struct ivi_policy *policy, uint32_t state, const char *value);
+
+/** Initialize the policy. Not implemented.
+ *
+ * Should be implemented by the policy engine. A single policy engine can be used
+ * at one time.
+ *
+ * Returns 0 in case of success, or -1 in case of failure.
+ */
+int
+ivi_policy_init(struct ivi_compositor *ivi);
+
+#endif