Skip to content

Design of the uAAL Android App

Alvaro Fides edited this page Oct 6, 2017 · 3 revisions

This page describes the components that build up the Native Android universAAL Middleware Application (a.k.a. the uAAL App), as well as the interactions among them. It is intended to give some insight into how the middleware works in Android and some of its particularities, but it does not go in depth to every single implementation detail. This can only be observed from the code itself and the javadoc and comments within it.

Table of Contents

The idea behind the universAAL Application

Android, as an execution environment, is quite different from OSGi. The universAAL middleware was originally implemented with OSGi in mind, and as such it relies on a lot of modularity, based on OSGi bundles. In Android this modularity is more oriented to "applications". We decided that the best compromise was to package all of universAAL middleware within a single Android application. A driving principle was also that existing Android applications from 3rd parties that wanted to communicate through universAAL buses shuold require very little modifications. The current design tries to satisfy these objectives.

The unviersAAL middleware is run within its own uAAL Android application, always executing in the background. Other Android applications can communicate with it by means of Android Intents. These are translated to/from universAAL ontology-based messages by the uAAL application, thanks to some metadata included by these 3rd party uAAL-compatible apps. The uAAL app will take care of connecting these communications with the right uAAL-compatible apps and other instances of uAAL in the same network, or remote uAAL nodes.

We know a service always running in the background goes against the principles of resource economy in Android, but it was the best compromise solution between speed, resources, and refactoring effort of existing code. In any case, we have found battery consumption to be reasonable.

As a final note, the UI bus is included in the uAAL app, but its capabilities are not available to Android apps - only to OSGi apps connected to from other instance, that can use the uAAL app as a UI Handler.

Structure of the universAAL Application

The following diagram depicts the components of the application. Each block represents a class, grouped into packages, and this is how the actual code is organized in the application. The lists contained by Services represent the Android Intent Actions they respond to, and the list in the Receiver classes represent the system events they receive. Further details will be given in each specific subsection.

There are other auxiliary classes and packages in the application that are not shown. Other items that usually compose an Android application, like the manifest, resource files and such are not explained in this page. The purpose of this page is not to keep an up-to-date representation of the current code but rather describe the overall structure.

Operation of the universAAL Application

The app includes all the universAAL middleware libraries. The application basically consists of a background service that runs the middleware modules. It scans for ontologies and registers them, then scans for uAAL-compatible apps and registers their metadata grounding as subscribers, publishers, callers and callees in the uAAL buses. Whenever there is a uAAL interaction, it takes care of translating between Android Intents and uAAL messages.

The following diagrams shows the basic startup process for the middleware application, from system boot to registration of the proxies. Notice that the amount of groundings found in metadata and number and types of registered proxies varies depending on the installed universAALized apps.

Activities

There is an activity in the middleware application that displays the current state of the middleware with a progress bar. This activity can also display content from other applications sent through the UI bus if properly configured in the settings. It includes an overflow menu with access to the app settings (pressing the hardware menu key or the on-screen menu button).

Another activity handles the settings screen. It contains all the app settings and the options for starting and stopping the middleware. More information about the settings is available in this page

Receivers

In the middleware application the broadcast receivers in this package are used to react to certain system actions. Then they relay this action embedded in an intent to a certain service, since services themselves cannot receive these actions in the same manner. There are three receiver classes in this package:

BootReceiver: Receives BOOT_COMPLETED actions issued by the system when it boots up. Then this receiver sends an intent to start the middleware service. This is how the middleware service will start every time the device is booted.

PackageReceiver: Receives PACKAGE_ADDED, PACKAGE_REMOVED and PACKAGE_REPLACED system actions, sent by the system whenever an application is installed or uninstalled. The receiver will relay the action to the scan service, which can then scan the application looking for universAAL metadata and register (if installed) or unregister (if uninstalled) it in the middleware (see Scan Service section for details).

WifiReceiver: Receives CONNECTIVITY_CHANGE system actions, which notify whenever there is a change in the data connection of the Android device. This is triggered in several situations: connection established or lost to a WiFi network, connection established or lost to mobile data network. The receiver relays the action to the middleware service, which will then decide what to do.

Services

Middleware Service

The middleware service is the core class (MiddlewareService) and component of the middleware application. It is an Android service that will be kept constantly running and starts and holds in memory all components of the universAAL middleware. This service is the middleware. Any intent it receives will create and start the service if it was not running before. This is what happens whenever the service is created and started:

  1. The service is started in foreground mode. In this mode Android tries to keep the service constantly running.
  2. A permanent notification is displayed in the notification bar, indicating the service is running
  3. The universAAL middleware is started.
  4. An intent with action ONT_REG_ALL is sent to the ontology service, which will load up all ontologies.
  5. If the device is connected to a Wifi Network, it will start the jSLP and jGroups connectors in order to discover other universAAL instances. If there is no such connection, a mock up empty version of jSLP and jGroups connectors is started that do nothing
  6. If set up, the uSpace Gateway, REST API or the Remote API connection is started, which will connect the device to a remote node.
  7. An intent with action PCK_REG_ALL is sent to the scan service, which will start all proxies of universAALized apps.
When the service is stopped for whatever reason (mainly because the user stopped it from the activity, or because it is killed by Android due to lack of memory) the inverse process is followed. If an intent is sent with an action to the middleware service, this is what it does depending on such action:
  • No Action: Used to simply start the middleware service, which will trigger the above mechanism if the service wasn't started already.
  • BOOT_COMPLETED: Same as above. This is sent by the BootReceiver.
  • CONNECTIVITY_ACTION: This is sent by the WifiReceiver. It reacts differently depending on what is the new connection status, what network is available, and what are the connectivity settings.
  • PCK_REG: This is sent by the ScanService. It contains information that the scan service has collected from an app universAAL metadata. This information is sent to the AndroidRegistry to create the pertinent proxy.
  • PCK_UNREG: The same as PCK_REG but for unregistering the proxy (such as when an app is uninstalled).
Just to finish, it's worth mentioning that the middleware modules are kept as local variables in the MiddlewareService, so they are maintained in memory as long as the service is running. This simplifies the development and management of the lifecycle even if it is against the objective of "lazyness" and resource economy.

Scan Service

It is just run on demand to execute its task and then it stops. The purpose of the service is to scan universAAL metadata from installed applications, then process that data and send it to the middleware service in order to register (or unregister) the appropriate proxies represented by that metadata (see Adapting or developing your app for uAAL). When an intent is sent to start this service, it performs different operations depending on the action included in the intent:

  • PCK_REG_ALL: Sent by the MiddlewareService. It instructs the scan service to perform a complete search for installed applications in the Android device. For each application found, its Android manifest is scanned. If the universAAL metadata tag is found then the xml metadata file it references is analysed. Suffice to say that it contains serialized information that represents "groundings". These groundings are sent to the middleware service by adding them to an intent with action PCK_REG, in order to register the proxies that result from those groundings.
  • PCK_UNREG_ALL: Sent by the MiddlewareService. It does the same as the above, but instead of sending the groundings to the middleware service to register them, they are sent to unregister, with the PCK_UNREG action.
  • PACKAGE_ADDED: Sent by PackageReceiver. It triggers the same process as in PCK_REG_ALL, but only for a single application, which identity is embedded in the action.
  • PACKAGE_REMOVED: Same as above but for unregistering.
  • PACKAGE_CHANGED: Like PACKAGE_REMOVED followed by PACKAGE_ADDED.

Ontology Service

It is just run on demand to execute its task and then it stops. Its purpose is to scan ontologies that should be loaded when the middleware starts. It solely responds to an intent with action ONT_REG_ALL, sent by the middleware service. When received it loads ontologies from 4 different sources (see also this page:

  • Embedded in the Native Android Middleware Application. This is a predefined set of ontologies which JAR files are included as libraries in the middleware application.
  • From the ontology folder following the list of activators in a configuration file.
  • From the ontology folder following the list of ontology classes in a configuration file.
  • Automatically from the ontology folder, by scanning each JAR file looking for its Activator, which should be org.universAAL.ontology.***Activator and extend ModuleActivator.
Loading ontologies upon installation of apps is not yet supported, so any change on the list of ontologies to load requires restarting the middleware.

Container classes

In Android there must be a counterpart of the OSGi container-specific code of the middleware. This is represented by the classes in this package. These are static singletons. The are kept in memory as long as the application is loaded.

AndroidContainer: It represents the "environment" of Android as it can be interacted with by the modules of the middleware. Its main purpose is to allow middleware components to access the instances of the other modules. The container itself only holds weak references to the instances, which are actually kept by the MiddlewareService class.

AndroidContext: This is used for 3 purposes: Accessing module-specific information, logging messages, and accessing the Container instance. In OSGi, there is a context for each module (bundle), providing bundle-specific information form this context. In Android however the modules are managed directly from the middleware service, there are no bundles, so all this module-specific information is useless. Only the logging mechanisms are used.

AndroidRergistry: This class holds in memory the different universAAL proxies for the universAALized apps. Basically, when a grounding is received by the middleware service to be registered, it calls the AndroidRegistry, which will create the appropriate proxy for the grounding, and add it to the list.

Proxy classes

Proxy classes act as a bridge between Android and universAAL. More specifically, between Android services and receivers, and universAAL "wrappers" (Service Callees, Service Callers, Context Susbscriber and Context Publishers). There is a proxy class for each wrapper. All proxies are created by AndroidRegistry when instructed by the middleware service, with a grounding represented by a GroundingParcel.

ContextPublisherProxy: The grounding in this case contains an action and a category, and a serialized context event. An Android broadcast receiver is created that listens to the action+category. A universAAL context publisher is created that publishes events of the type of the serialized event. When an intent is received in the receiver (sent by the universAALized app), the serialized event is sent to the context bus. There are mapping features in the grounding that map values of the received intent to variables in the sent event.

ContextSubscriberProxy: The grounding contains an action and a category, and a serialized context event pattern. A universAAL context subscriber is created that subscribes to events that match the pattern. When an event is received, an intent is sent with the action+category (which should be received by the universAALized app). There are mapping features in the grounding that map values of the received event to variables in the sent intent.

ServiceCalleeProxy: Grounding contains action and category, a serialized service profile, and optionally reply action and reply category. A universAAL service callee is created that provides the serialized service profile. When a service request is received there, an intent is sent with action+category (which should be received by the universAALized app). If reply action and category were set in the grounding it is because the service is supposed to return something. In that case, a broadcast receiver is created that listens to reply action+reply category (the universAAlized app will send that in response). When the response is received in that temporary broadcast receiver, the universAAL service response is built and returned to the service bus. There are mapping options in the grounding that map service inputs and outputs to intent values, and response intent values to service response outputs.

ServiceCallerProxy: Grounding contains action and category, a serialized service request, and optionally reply action and reply category. A broadcast receiver is created that listens to action+category. When an intent is received there (sent by universAALized app), the serialized service request is sent to the service bus. If reply action and category are set, once a response is received from the service bus, an intent is sent to those reply action+reply category (which should be received by the universAALized app). There are mapping features in the grounding that map values of the first intent to input and output of the service request, and also from service response outputs to reply intent values.