/* * Copyright (C) 2017 Mentor Graphics Development (Deutschland) GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "app.hpp" #include "json_helper.hpp" #include "layers.hpp" #include "layout.hpp" #include "util.hpp" #include "wayland.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace wm { namespace { using nlohmann::json; result file_to_json(char const *filename) { std::ifstream i(filename); if (i.fail()) { return Err("Could not open config file"); } json j; i >> j; return Ok(j); } struct result load_layer_map(char const *filename) { HMI_DEBUG("wm", "loading IDs from %s", filename); auto j = file_to_json(filename); if (j.is_err()) { return Err(j.unwrap_err()); } json jids = j.unwrap(); return to_layer_map(jids); } } // namespace /** * App Impl */ App::App(wl::display *d) : api{this}, chooks{this}, display{d}, controller{}, outputs(), config(), layers(), id_alloc{}, pending_events(false), policy{} { try { { auto l = load_layer_map( this->config.get_string("layers.json").value().c_str()); if (l.is_ok()) { this->layers = l.unwrap(); } else { HMI_ERROR("wm", "%s", l.err().value()); } } } catch (std::exception &e) { HMI_ERROR("wm", "Loading of configuration failed: %s", e.what()); } } int App::init() { if (!this->display->ok()) { return -1; } if (this->layers.mapping.empty()) { HMI_ERROR("wm", "No surface -> layer mapping loaded"); return -1; } // Make afb event for (int i=Event_Val_Min; i<=Event_Val_Max; i++) { map_afb_event[kListEventName[i]] = afb_daemon_make_event(kListEventName[i]); } this->display->add_global_handler( "wl_output", [this](wl_registry *r, uint32_t name, uint32_t v) { this->outputs.emplace_back(std::make_unique(r, name, v)); }); this->display->add_global_handler( "ivi_controller", [this](wl_registry *r, uint32_t name, uint32_t v) { this->controller = std::make_unique(r, name, v); // Init controller hooks this->controller->chooks = &this->chooks; // This protocol needs the output, so lets just add our mapping here... this->controller->add_proxy_to_id_mapping( this->outputs.back()->proxy.get(), wl_proxy_get_id(reinterpret_cast( this->outputs.back()->proxy.get()))); }); // First level objects this->display->roundtrip(); // Second level objects this->display->roundtrip(); // Third level objects this->display->roundtrip(); return init_layers(); } int App::dispatch_events() { if (this->dispatch_events() == 0) { return 0; } int ret = this->display->dispatch(); if (ret == -1) { HMI_ERROR("wm", "wl_display_dipatch() returned error %d", this->display->get_error()); return -1; } this->display->flush(); return 0; } int App::dispatch_pending_events() { if (this->pop_pending_events()) { this->display->dispatch_pending(); return 0; } return -1; } bool App::pop_pending_events() { bool x{true}; return this->pending_events.compare_exchange_strong( x, false, std::memory_order_consume); } void App::set_pending_events() { this->pending_events.store(true, std::memory_order_release); } optional App::lookup_id(char const *name) { return this->id_alloc.lookup(std::string(name)); } optional App::lookup_name(int id) { return this->id_alloc.lookup(id); } /** * init_layers() */ int App::init_layers() { if (!this->controller) { HMI_ERROR("wm", "ivi_controller global not available"); return -1; } if (this->outputs.empty()) { HMI_ERROR("wm", "no output was set up!"); return -1; } auto &c = this->controller; auto &o = this->outputs.front(); auto &s = c->screens.begin()->second; auto &layers = c->layers; // Write output dimensions to ivi controller... c->output_size = compositor::size{uint32_t(o->width), uint32_t(o->height)}; // Clear scene layers.clear(); // Clear screen s->clear(); // Quick and dirty setup of layers for (auto const &i : this->layers.mapping) { c->layer_create(i.second.layer_id, o->width, o->height); auto &l = layers[i.second.layer_id]; l->set_destination_rectangle(0, 0, o->width, o->height); l->set_visibility(1); HMI_DEBUG("wm", "Setting up layer %s (%d) for surface role match \"%s\"", i.second.name.c_str(), i.second.layer_id, i.second.role.c_str()); } // Add layers to screen s->set_render_order(this->layers.layers); this->layout_commit(); return 0; } void App::surface_set_layout(int surface_id, optional sub_surface_id) { if (!this->controller->surface_exists(surface_id)) { HMI_ERROR("wm", "Surface %d does not exist", surface_id); return; } auto o_layer_id = this->layers.get_layer_id(surface_id); if (!o_layer_id) { HMI_ERROR("wm", "Surface %d is not associated with any layer!", surface_id); return; } uint32_t layer_id = *o_layer_id; auto const &layer = this->layers.get_layer(layer_id); auto rect = layer.value().rect; auto &s = this->controller->surfaces[surface_id]; int x = rect.x; int y = rect.y; int w = rect.w; int h = rect.h; // less-than-0 values refer to MAX + 1 - $VALUE // e.g. MAX is either screen width or height if (w < 0) { w = this->controller->output_size.w + 1 + w; } if (h < 0) { h = this->controller->output_size.h + 1 + h; } if (sub_surface_id) { if (o_layer_id != this->layers.get_layer_id(*sub_surface_id)) { HMI_ERROR("wm", "surface_set_layout: layers of surfaces (%d and %d) don't match!", surface_id, *sub_surface_id); return; } int x_off = 0; int y_off = 0; // split along major axis if (w > h) { w /= 2; x_off = w; } else { h /= 2; y_off = h; } auto &ss = this->controller->surfaces[*sub_surface_id]; HMI_DEBUG("wm", "surface_set_layout for sub surface %u on layer %u", *sub_surface_id, layer_id); // configure surface to wxh dimensions ss->set_configuration(w, h); // set source reactangle, even if we should not need to set it. ss->set_source_rectangle(0, 0, w, h); // set destination to the display rectangle ss->set_destination_rectangle(x + x_off, y + y_off, w, h); } HMI_DEBUG("wm", "surface_set_layout for surface %u on layer %u", surface_id, layer_id); // configure surface to wxh dimensions s->set_configuration(w, h); // set source reactangle, even if we should not need to set it. s->set_source_rectangle(0, 0, w, h); // set destination to the display rectangle s->set_destination_rectangle(x, y, w, h); HMI_DEBUG("wm", "Surface %u now on layer %u with rect { %d, %d, %d, %d }", surface_id, layer_id, x, y, w, h); } void App::layout_commit() { this->controller->commit_changes(); this->display->flush(); } char const *App::api_activate_surface(char const *drawing_name, char const *drawing_area) { ST(); auto const &surface_id = this->lookup_id(drawing_name); if (!surface_id) { return "Surface does not exist"; } if (!this->controller->surface_exists(*surface_id)) { return "Surface does not exist in controller!"; } auto layer_id = this->layers.get_layer_id(*surface_id); if (!layer_id) { return "Surface is not on any layer!"; } auto o_state = *this->layers.get_layout_state(*surface_id); if (o_state == nullptr) { return "Could not find layer for surface"; } struct LayoutState &state = *o_state; // disable layers that are above our current layer for (auto const &l : this->layers.mapping) { if (l.second.layer_id <= *layer_id) { continue; } bool flush = false; if (l.second.state.main != -1) { this->deactivate(l.second.state.main); l.second.state.main = -1; flush = true; } if (l.second.state.sub != -1) { this->deactivate(l.second.state.sub); l.second.state.sub = -1; flush = true; } if (flush) { this->layout_commit(); } } auto layer = this->layers.get_layer(*layer_id); if (state.main == -1) { this->try_layout( state, LayoutState{*surface_id}, [&] (LayoutState const &nl) { HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal); this->surface_set_layout(*surface_id); state = nl; // Commit for configuraton this->layout_commit(); if (!(layer->is_normal_layout_only)) { // Wait for configuration listener controller->is_configured = false; while (!(controller->is_configured)) { dispatch_pending_events(); } } std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull); this->emit_syncdraw(drawing_name, str_area.c_str()); this->enqueue_flushdraw(state.main); }); } else { if (0 == strcmp(drawing_name, "HomeScreen")) { this->try_layout( state, LayoutState{*surface_id}, [&] (LayoutState const &nl) { HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal); std::string str_area = std::string(kNameLayoutNormal) + "." + std::string(kNameAreaFull); this->emit_syncdraw(drawing_name, str_area.c_str()); this->enqueue_flushdraw(state.main); }); } else { bool can_split = this->can_split(state, *surface_id); if (can_split) { this->try_layout( state, LayoutState{state.main, *surface_id}, [&] (LayoutState const &nl) { HMI_DEBUG("wm", "Layout: %s", kNameLayoutSplit); std::string main = std::move(*this->lookup_name(state.main)); this->surface_set_layout(state.main, surface_id); if (state.sub != *surface_id) { if (state.sub != -1) { this->deactivate(state.sub); } } state = nl; // Commit for configuraton and visibility(0) this->layout_commit(); // Wait for configuration listener controller->is_configured = false; while (!(controller->is_configured)) { dispatch_pending_events(); } std::string str_area_main = std::string(kNameLayoutSplit) + "." + std::string(kNameAreaMain); std::string str_area_sub = std::string(kNameLayoutSplit) + "." + std::string(kNameAreaSub); this->emit_syncdraw(main.c_str(), str_area_main.c_str()); this->emit_syncdraw(drawing_name, str_area_sub.c_str()); this->enqueue_flushdraw(state.main); this->enqueue_flushdraw(state.sub); }); } else { this->try_layout( state, LayoutState{*surface_id}, [&] (LayoutState const &nl) { HMI_DEBUG("wm", "Layout: %s", kNameLayoutNormal); this->surface_set_layout(*surface_id); if (state.main != *surface_id) { this->deactivate(state.main); } if (state.sub != -1) { if (state.sub != *surface_id) {
AGL framework, IoT.bzh proposal overview
========================================

Foreword
--------

This document describes what we intend to do. It may happen that our
current implementation and the content of this document differ.

In case of differences, it is assumed that this document is right
and the implementation is wrong.


Introduction
------------

During the first works in having the security model of Tizen
integrated in AGL (Automotive Grade Linux) distribution, it became
quickly obvious that the count of components specific to Tizen
to integrate was huge.

Here is a minimal list of what was needed:

 - platform/appfw/app-installers
 - platform/core/security/cert-svc
 - platform/core/appfw/ail
 - platform/core/appfw/aul-1
 - platform/core/appfw/libslp-db-util
 - platform/core/appfw/pkgmgr-info
 - platform/core/appfw/slp-pkgmgr

But this list isn't complete because many dependencies are hidden.
Those hidden dependencies are including some common libraries but also many
tizen specific sub-components (iniparser, bundle, dlog, libtzplatform-config,
db-util, vconf-buxton, ...).

This is an issue because AGL is not expected to be Tizen. Taking it would
either need to patch it for removing unwanted components or to take all
of them.

However, a careful study of the core components of the security framework
of Tizen showed that their dependencies to Tizen are light (and since some
of our work, there is no more dependency to tizen).
Those components are **cynara**, **security-manager**, **D-Bus aware of cynara**.

Luckyly, these core security components of Tizen are provided
by [meta-intel-iot-security][meta-intel], a set of yocto layers.
These layers were created by Intel to isolate Tizen specific security
components from the initial port of Tizen to Yocto.
The 3 layers are providing components for:

 * Implementing Smack LSM
 * Implementing Integrity Measurement Architecture
 * Implementing Tizen Security Framework

The figure below shows the history of these layers.

![Security_model_history][Security_model_history]

We took the decision to use these security layers that provides the
basis of the Tizen security, the security framework.

For the components of the application framework, built top of
the security framework, instead of pulling the huge set of packages
from Tizen, we decided to refit it by developping a tiny set of
components that would implement the same behaviour but without all
the dependencies and with minor architectural improvements for AGL.

These components are **afm-system-daemon** and **afm-user-daemon**.
They provides infrastructure for installing, uninstalling,
launching, terminating, pausing and resuming applications in
a multi user secure environment.

A third component exists in the framework, the binder **afb-daemon**.
The binder provides the easiest way to provide secured API for
any tier. Currently, the use of the binder is not absolutely mandatory.

This documentation explains the framework created by IoT.bzh
by rewriting the Tizen Application Framework. Be aware of the
previous foreword.


Overview
--------

The figure below shows the major components of the framework
and their interactions going through the following scenario:
APPLICATION installs an other application and then launch it.

![AppFW-APP_install_sequences][AppFW-APP_install_sequences]

Let follow the sequence of calls:

1. APPLICATION calls its **binder** to install the OTHER application.

2. The binding **afm-main-binding** of the **binder** calls, through
   **D-Bus** system, the system daemon to install the OTHER application.

3. The system **D-Bus** checks wether APPLICATION has the permission
   or not to install applications by calling **CYNARA**.

4. The system **D-Bus** transmits the request to **afm-system-daemon**.

   **afm-system-daemon** checks the application to install, its
   signatures and rights and install it.

5. **afm-system-daemon** calls **SECURITY-MANAGER** for fullfilling
   security context of the installed application.

6. **SECURITY-MANAGER** calls **CYNARA** to install initial permissions
   for the application.

7. APPLICATION call its binder to start the nearly installed OTHER application.

8. The binding **afm-main-binding** of the **binder** calls, through
   **D-Bus** session, the user daemon to launch the OTHER application.

9. The session **D-Bus** checks wether APPLICATION has the permission
   or not to start an application by calling **CYNARA**.

10. The session **D-Bus** transmits the request to **afm-user-daemon**.

11. **afm-user-daemon** checks wether APPLICATION has the permission
    or not to start the OTHER application **CYNARA**.

12. **afm-user-daemon** uses **SECURITY-MANAGER** features to set
    the seciruty context for the OTHER application.

13. **afm-user-daemon** launches the OTHER application.

This scenario does not cover all the features of the frameworks.
Shortly because details will be revealed in the next chapters,
the components are:

* ***SECURITY-MANAGER***: in charge of setting Smack contexts and rules,
  of setting groups, and, of creating initial content of *CYNARA* rules
  for applications.

* ***CYNARA***: in charge of handling API access permissions by users and by
  applications.

* ***D-Bus***: in charge of chec