πŸ“œ ⬆️ ⬇️

We study the principles of interaction between Ubuntu Touch and Android


Hi, Habr.

A couple of months ago I was porting Ubuntu Touch to the Allwinner A10 platform,
in the process of taking notes on my memory. Now, in my opinion, they are still relevant, until Ubuntu Touch has finally moved to its graphic server Mir and so on.

This article will help interested parties find the starting point from which to start a close acquaintance with UT.
')
The style of presentation is far from technical, but if you don't mind, then
I invite under the cat.


Introduction


What is libhybris

libhybris is a layer that allows you to load libraries from Bionic userspace into Glibc userspace, on the fly replacing some symbols with variants from Glibc. Simply put, this solution allows the use of proprietary libraries for Android in Linux-space. The greatest benefit, of course, is the ability to use proprietary GPU drivers, assembled by the manufacturer only for Android.

What is surfaceflinger

surfaceflinger is a native android service, a composite graphics layer manager.



In more detail about Binder IPC and SurfaceFlinger :


Ubuntu touch

Ubuntu Touch Developer Preview itself is based on Android, borrows the necessary services for working with hardware. You can read a general overview of dependencies here - Ubuntu Touch Porting or in a note on OpenNet

The base operating system uses the usual Android JB 4.2 , or rather CyagenMod-10.1 (the CM subprojects repository is phablet.ubuntu.com/gitweb ). Everything connected with dalvik and java has been removed from it - only the native part is left, consisting of system services and HAL . If you wish, you can use AOSP 4.1 , but be prepared to adapt to the native API from 4.1, it is not covered by any documentation and much less by specification and changes from release to release.

UT components are located in chroot , the self-written utility uchroot is used , excerpt:
static int ubuntum(void *a) { /* Chroot */ chroot("/data/ubuntu"); chdir("/"); /* Set basic env variables */ char *const envp[8] = { "container=aal", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "SHELL=/bin/bash", "HOME=/root", "USER=root", "USERNAME=root", "LOGNAME=root", NULL }; /* Exec shell */ execle("/sbin/init", "/sbin/init", "--verbose", NULL, envp); return 0; } 

The libhybris mechanism is used for the interaction between the Android environment and the Ubuntu chroot environment.


Ubuntu Touch components


Soup from various UT components can be seen in the ~ phablet-team lanchpad repositories.
We are interested in the following two components responsible for the operation of the platform on Android devices:

Download the latest version of the source code:
 bzr branch lp:platform-api bzr branch lp:qtubuntu 


Ubuntu Platform API

The Ubuntu platform API is a low-level API for performing basic operations using platform capabilities (Android).

Examples of methods:

From the doc / mainpage.md documentation file, we learn that the source - tree platform-api can be divided into two parts:

And the only thing that a third-party developer can rely on when working with this API is the headers from
the directory includes / ubuntu / application , and everything else is supposed to change over time.

From the debian / control file, we find out that:
 Package: libplatform-api1-hybris Depends: libhybris Description: Hybris implementation of the Platform API (runtime) This package provides the hybris implementation of the Platform API. The produced library should be used via libhybris, to communicate with the Android userspace, which is where the Ubuntu Application Manager lives. 

Yeah, so judging by android / hybris / Android.mk , the implementation of the platform API is compiled as libubuntu_application_api library with linkage with native android libs and is placed in android userspace:
 LOCAL_SRC_FILES := \ ubuntu_application_api_for_hybris.cpp \ ubuntu_application_gps_for_hybris.cpp \ ubuntu_application_sensors_for_hybris.cpp \ ../default/default_ubuntu_application_sensor.cpp \ ../default/default_ubuntu_application_ui.cpp \ ../default/default_ubuntu_ui.cpp \ application_manager.cpp LOCAL_MODULE := libubuntu_application_api LOCAL_SHARED_LIBRARIES := \ libandroidfw \ libbinder \ libutils \ libgui \ libEGL \ libGLESv2 \ libhardware \ libhardware_legacy 

The platform-api / src / android directory has remained unaddressed, consider it in detail. Judging by the presence of the CMakeLists.txt file, the build is already going on for glibc.

There is a single file with the code - ubuntu_application_api.cpp , looking in which we will see:
 extern void *android_dlopen(const char *filename, int flag); extern void *android_dlsym(void *handle, const char *symbol); 
- using libhybris procedures for dynamically loading symbols from a shared-lib from android userspace.

 struct Bridge { static const char* path_to_library() { return "/system/lib/libubuntu_application_api.so"; } Bridge() : lib_handle(android_dlopen(path_to_library(), RTLD_LAZY)) { assert(lib_handle && "Error loading ubuntu_application_api"); } ....... void* resolve_symbol(const char* symbol) const { return android_dlsym(lib_handle, symbol); } void* lib_handle; }; 
- a simple bridge for loading symbols from libubuntu_application_api.so , which gets along with native android services, and which we have recently mentally β€œcollected” using android / hybris / Android.mk .

 #define IMPLEMENT_VOID_FUNCTION3(symbol, arg1, arg2, arg3) \ void symbol(arg1 _1, arg2 _2, arg3 _3) { \ static void (*f)(arg1, arg2, arg3) = NULL; \ DLSYM(&f, #symbol); \ f(_1, _2, _3); } ....... IMPLEMENT_VOID_FUNCTION2(ubuntu_application_ui_init, int, char**); IMPLEMENT_FUNCTION0(StageHint, ubuntu_application_ui_setup_get_stage_hint); IMPLEMENT_FUNCTION0(FormFactorHint, ubuntu_application_ui_setup_get_form_factor_hint); IMPLEMENT_VOID_FUNCTION1(ubuntu_application_ui_start_a_new_session, SessionCredentials*); IMPLEMENT_VOID_FUNCTION2(ubuntu_application_ui_set_clipboard_content, void*, size_t); ....... 
- a bunch of wrappers for the API characters implemented in libubuntu_application_api.so .

So in order to avoid confusion:

The developers decided to reduce the entropy of the universe by creating libraries of the same name.
If you look at their debates about the naming of the components merge-153874 discussion , then your ears wilt.

Ubuntu application manager

In platform-api / android / hybris, in addition to the implementation of the Ubuntu platform API, there are sources for ubuntuappmanager , an application service for Ubuntu, it lives in android userspace and, judging by Android.mk , it actively uses libubuntu_application_api and communicates via Binder IPC with Android services.
 LOCAL_SRC_FILES:= \ application_manager.cpp \ default_application_manager.cpp \ LOCAL_MODULE:= ubuntuappmanager LOCAL_MODULE_TAGS := optional LOCAL_SHARED_LIBRARIES := \ libbinder \ libinput \ libgui \ libskia \ libandroidfw \ libutils \ libEGL \ libGLESv2 \ libubuntu_application_api 

Solves a bunch of tasks managing applications and sessions, a quick look at default_application_manager.h :
  void update_app_lists(); void binderDied(const android::wp<android::IBinder>& who); void register_a_surface(...); void request_fullscreen(...); int get_session_pid(const android::sp<android::IApplicationManagerSession>& session); void focus_running_session_with_id(int id); void unfocus_running_sessions(); int32_t query_snapshot_layer_for_session_with_id(int id); android::IApplicationManagerSession::SurfaceProperties query_surface_properties_for_session_id(int id); void switch_focused_application_locked(size_t index_of_next_focused_app); void switch_focus_to_next_application_locked(); void kill_focused_application_locked(); void start_a_new_session( int32_t session_type, int32_t stage_hint, const android::String8& app_name, const android::String8& desktop_file, const android::sp<android::IApplicationManagerSession>& session, int fd); 

QtUbuntu

We understand the part of UT responsible for the interaction between the Ubuntu platform API and Qt / QML applications.

If you are not familiar with Qt Platform Abstraction , then, in short, this is an opportunity to abstract from the platform on which Qt applications are launched using specially written QPA plug-ins.

The QPA plugin implements the basic methods like createPlatformWindow , and then the Qt application, when it wants to create a window, uses the createPlatformWindow symbol from the abstraction plugin and in no way blows where it went further.

In this case, we will deal with the QPA plugin for working with the Ubuntu application API.
 ~/ubuntu/qtubuntu $ tree . β”œβ”€β”€ qtubuntu.pro β”œβ”€β”€ src β”‚ β”œβ”€β”€ modules β”‚ β”‚ β”œβ”€β”€ application <------------------ QML plugin    β”‚ β”‚ β”‚ β”œβ”€β”€ application.cc | Ubuntu Application Manager β”‚ β”‚ β”‚ β”œβ”€β”€ application.h |  QtQuick β”‚ β”‚ β”‚ β”œβ”€β”€ application.pro | β”‚ β”‚ β”‚ β”œβ”€β”€ application_image.cc | β”‚ β”‚ β”‚ β”œβ”€β”€ application_image.h | β”‚ β”‚ β”‚ β”œβ”€β”€ application_list_model.cc | β”‚ β”‚ β”‚ β”œβ”€β”€ application_list_model.h | β”‚ β”‚ β”‚ β”œβ”€β”€ application_manager.cc | β”‚ β”‚ β”‚ β”œβ”€β”€ application_manager.h | β”‚ β”‚ β”‚ β”œβ”€β”€ application_window.cc | β”‚ β”‚ β”‚ β”œβ”€β”€ application_window.h | β”‚ β”‚ β”‚ β”œβ”€β”€ input_filter_area.cc | β”‚ β”‚ β”‚ β”œβ”€β”€ input_filter_area.h | β”‚ β”‚ β”‚ β”œβ”€β”€ logging.h | β”‚ β”‚ β”‚ β”œβ”€β”€ plugin.cc | β”‚ β”‚ β”‚ └── qmldir | β”‚ β”‚ β”œβ”€β”€ ---------------------------------- β”‚ β”‚ └── modules.pro β”‚ β”œβ”€β”€ platforms β”‚ β”‚ β”œβ”€β”€ base β”‚ β”‚ β”œβ”€β”€ platforms.pro β”‚ β”‚ └── ubuntu <-------------- QPA   β”‚ β”‚ β”œβ”€β”€ clipboard.cc β”‚ β”‚ β”œβ”€β”€ clipboard.h β”‚ β”‚ β”œβ”€β”€ input.cc β”‚ β”‚ β”œβ”€β”€ input.h β”‚ β”‚ β”œβ”€β”€ integration.cc <--   `createPlatformWindow`  β”‚ β”‚ β”œβ”€β”€ integration.h β”‚ β”‚ β”œβ”€β”€ main.cc β”‚ β”‚ β”œβ”€β”€ screen.cc β”‚ β”‚ β”œβ”€β”€ screen.h β”‚ β”‚ β”œβ”€β”€ ubuntu.json β”‚ β”‚ β”œβ”€β”€ ubuntu.pro β”‚ β”‚ β”œβ”€β”€ window.cc β”‚ β”‚ └── window.h β”‚ └── src.pro └── tests 

Judging by the contents of ubuntu.pro , the platform is linked with the glibc version of libubuntu_application_api.so
Note the following method calls from the platform API set used in integration.cc and window.cc :
 #include <ubuntu/application/ui/ubuntu_application_ui.h> ubuntu_application_ui_start_a_new_session(&credentials); ubuntu_application_ui_destroy_surface(surface_); ubuntu_application_ui_create_surface(&surface_, "QUbuntuWindow", geometry.width(), geometry.height(), static_cast<SurfaceRole>(role), flags, eventCallback, this); ubuntu_application_ui_move_surface_to(surface_, geometry.x(), geometry.y()); ubuntu_application_ui_request_fullscreen_for_surface(surface_); ubuntu_application_ui_move_surface_to(surface_, rect.x(), rect.y()); ubuntu_application_ui_resize_surface_to(surface_, rect.width(), rect.height()); ubuntu_application_ui_request_fullscreen_for_surface(surface_); ubuntu_application_ui_show_surface(surface_); ubuntu_application_ui_hide_surface(surface_); 

Now it is clear that when our Qt application wants to create a window, it will call the method from the QPA platform qubuntu - QUbuntuIntegration :: createPlatformWindow from the integration.cc file:
 QPlatformWindow* QUbuntuIntegration::createPlatformWindow(QWindow* window) { ....... // Create the window. QPlatformWindow* platformWindow = new QUbuntuWindow(.......); ....... } 

Looking into the QUbuntuWindow constructor in the window.cc file, we find the call to the QUbuntuWindow :: createWindow () method:
 void QUbuntuWindow::createWindow() { ....... ubuntu_application_ui_create_surface( &surface_, "QUbuntuWindow", geometry.width(), geometry.height(), static_cast<SurfaceRole>(role), flags, eventCallback, this); ....... ubuntu_application_ui_move_surface_to(surface_, geometry.x(), geometry.y()); ....... } 

This is an extremely truncated code, but the essence is clear - calls are made to the Ubuntu platform API , which we have implemented in the glibc version of libubuntu_application_api.so , which, in fact, is a bridge to the bionic version of libubuntu_application_api.so , which lies in the platform- api / android .

Jump?

Jumping with grep to the desired file, we get to platform-api / android / default / default_ubuntu_application_ui.cpp :
 //   ubuntu_application_ui_create_surface ubuntu::application::ui::Surface::Ptr surface = session->create_surface(props, ubuntu::application::ui::input::Listener::Ptr(new CallbackEventListener(cb, ctx))); //   ubuntu_application_ui_move_surface_to auto s = static_cast<Holder<ubuntu::application::ui::Surface::Ptr>*>(surface); s->value->move_to(x, y); 

It remains for us to open the matryoshka and find out how ubuntu :: application :: ui :: Session and, accordingly, ubuntu :: application :: ui :: Surface are implemented. And they are implemented in this file - ubuntu_application_api_for_hybris.cpp :
 namespace android { ....... struct Session : public ubuntu::application::ui::Session, public UbuntuSurface::Observer { ....... Session(.....) { ...... ubuntu::application::ui::Surface::Ptr create_surface( const ubuntu::application::ui::SurfaceProperties& props, const ubuntu::application::ui::input::Listener::Ptr& listener) { ....... // ,     .        UbuntuSurface UbuntuSurface* surface = new UbuntuSurface(client, client_channel, looper, props, listener,this); ....... // 100%   ,    UbuntuSurface return ubuntu::application::ui::Surface::Ptr(surface); ....... 

Rewind, find UbuntuSurface :
 struct UbuntuSurface : public ubuntu::application::ui::Surface { ....... UbuntuSurface(const sp<SurfaceComposerClient>& client, .......) : ubuntu::application::ui::Surface(listener) { //    -    Android API surface_control = client->createSurface( String8(props.title), props.width, props.height, PIXEL_FORMAT_RGBA_8888, props.flags & .......); surface = surface_control->getSurface(); ....... 

We get a certain object of type android :: SurfaceControl , which is the result of a call to android :: SurfaceComposerClient () -> createSurface () .
Through it pass all calls to android :: SurfaceComposerClient ( frameworks / native / libs / gui / Surface.cpp ), such as: resizing, moving, changing the order of layers and so on.

Going back through the chain, we understand what actually happens when we launch the next Qt application with the Ubuntu QPA platform.

Conclusion


At this point, I have to stop myself, because, in my opinion, the considered principle of interaction between Ubuntu Touch and Android is self-sufficient. Further arguments can already go in isolation from all of the above. Questions of interaction between qmlscene and ubuntuappmanager , the principle of input control using the services of SurfaceFlinger and InputDispatcher, and other issues from the corners of this simple topic remained unresolved . But that's another story.

A week later, the phone will arrive on Firefox OS, gutting it ...

Source: https://habr.com/ru/post/180505/


All Articles