📜 ⬆️ ⬇️

How Android works, part 1


In this series of articles, I will talk about the internal Android device — about the boot process, about the contents of the file system, about Binder and Android Runtime, about what the applications consist of, how they are installed, run, work and interact, about the Android Framework, and how Android is secured.


Articles series:





Some facts


Android is the most popular operating system and application platform, with more than two billion active users. It employs completely different devices, from the “Internet of Things” and smart watches to TVs, laptops and cars, but most often Android is used on smartphones and tablets.


Android is a free and open source project. Most of the source code (which can be found at https://source.android.com ) is distributed under the free Apache 2.0 license.


Android Inc. was founded in 2003 and purchased in 2005 by Google. The Android public beta was released in 2007, and the first stable version is in 2008, since then major releases are released about once a year. The latest stable version of Android at the time of writing is 7.1.2 Nougat.



Android is Linux


There was a lot of controversy about this wording, so I’ll explain at once what I mean by this phrase: Android is based on the Linux kernel, but differs significantly from most other Linux systems.


Among the original Android development team was Robert Love, one of the most famous Linux kernel developers, and even now Google remains one of the most active contributors to the core, so it’s not surprising that Android is based on Linux.


As with other Linux systems, the Linux kernel provides such low-level things as memory management, data protection, support for multiprocessing and multithreading. But - with a few exceptions - you will not find other familiar components of GNU / Linux systems in Android: there is nothing from the GNU project, X.Org, or even systemd is not used. All of these components are replaced by analogs that are more suitable for use in conditions of limited memory, low processor speed and minimal power consumption — thus, Android looks more like a embedded Linux system than GNU / Linux.


Another reason that GNU software is not used by Android is the well-known “no GPL in userspace” policy:


We are sometimes asked why the Apache Software License 2.0 is the preferred license for Android. For userspace (that is, non-kernel) software, as well as other licenses like BSD, MIT, etc. over other licenses such as the LGPL.

Android is about freedom and choice. It is possible to make it possible. We want you to make it so. Using the LGPL libraries would often not do that.

The Linux kernel itself in Android is also slightly modified: several small components were added, including ashmem (anonymous shared memory), Binder driver (part of the large and important Binder framework, which I will discuss below), wakelocks (hibernation management), and low memory killer. Initially, they were patches to the kernel, but their code was quickly added back to the upstream core. However, you will not find them in “normal Linux”: most other distributions disable these components during assembly.


Android is not using the GNU C library ( glibc ) as the libc (standard C library), but its own minimalistic implementation called bionic , optimized for embedded (embedded) systems — it is much faster, smaller and less demanding on memory than glibc, which is overgrown with multiple layers of compatibility.


In Android, there is a command line shell (shell) and many standard for Unix-like systems of commands / programs. Embedded systems typically use the Busybox package, which implements the functionality of many commands in one executable file; Android uses an analogue called Toybox . As in the "normal" distributions of Linux (and unlike embedded systems), the main way to interact with the system is a graphical interface, not the command line. However, it is very easy to get to the command line - just run the terminal emulator application. By default, it is usually not installed, but it is easy, for example, to download from the Play Store ( Terminal Emulator for Android , Material Terminal , Termux ). In many "advanced" Android distributions, such as LineageOS (formerly CyanogenMod), the terminal emulator is preinstalled.


Terminal emulator on android


The second option is to connect to an Android device from a computer via the Android Debug Bridge (adb). This is very similar to connecting via SSH:


user@desktop-linux$ adb shell android$ uname Linux 

Of the other familiar components, Android uses the FreeType library (for displaying text), the OpenGL ES , EGL and Vulkan graphic APIs, as well as the lightweight SQLite DBMS.


In addition, earlier WebKit browser engine was used to implement WebView , but starting from version 7.0 , the installed Chrome application is used instead (or another; the list of applications allowed to act as a WebView provider is configured at the system compilation stage). Inside, Chrome also uses WebKit-based Blink engine, but unlike the system library, Chrome is updated via the Play Store — so all applications that use WebView automatically receive the latest improvements and fixes for vulnerabilities.


Android technology stack


It's all about apps


As you can easily see, using Android is fundamentally different from using “regular Linux” - you do not need to open and close applications, you just switch between them, as if all applications are always running. Indeed, one of the unique features of Android is that applications do not directly control the process in which they are running. Let's talk about this in more detail.


The basic unit in Unix-like systems is the process. Both low-level system services, and individual commands in the shell, and graphical applications are processes. In most cases, the process is a black box for the rest of the system — the other components of the system do not know and do not care about its condition. The process starts with a call to the main() function main() actually _start ), and then implements some of its own logic, interacting with the rest of the system through system calls and simple interprocess communication (IPC).


Since Android is also Unix-like, all this is true for it, but while low-level parts - at the Unix level - operate on the concept of a process, at a higher level - the Android Framework level - the main unit is the application . The application is not a black box: it consists of individual components well known to the rest of the system.


Android applications have no main() function, no single entry point. In general, Android abstracts as much as possible the concept of an application launched from both the user and the developer. Of course, the application process needs to be started and stopped, but Android does it automatically (I will talk more about this in the next articles). The developer is invited to implement several separate components, each of which has its own life cycle.


In Android, however, we’ve been a function () function, because we’ve been running. It’s not always a problem. it can be used even if it isn’t currently running.

To implement such a system, applications need to be able to communicate with each other and with system services — in other words, you need a very advanced and fast IPC mechanism.


This mechanism is Binder.


Binder


Binder is a platform for fast, convenient and object-oriented interprocess communication.


Binder development began at Be Inc. (for BeOS), then it was ported to Linux and opened. The main developer of Binder, Dianne Hackborn, has been and remains one of the main developers of Android. During the development of Android, Binder has been completely rewritten.


Binder does not work on top of System V IPC (which is not even supported in bionic), but uses its own small kernel module, which userspace interacts with through user calls (mainly ioctl ) on the “virtual device” /dev/binder . From the userspace side, low-level work with Binder, including interaction with /dev/binder and marshalling / unmarshalling data, is implemented in the libbinder library.


The low-level parts of the binder operate in terms of objects that can be sent between processes. This uses reference counting to automatically release unused shared resources and notify of the completion of a remote process (link-to-death) to release resources within the process.


The high-level parts of a binder work in terms of interfaces, services, and proxies. The description of the interface provided by the program to other programs is written in a special AIDL (Android Interface Definition Language) language, which looks very similar to the interface declaration in Java. This description automatically generates a real Java interface, which can then be used by clients and the service itself. In addition, two special classes are automatically generated for the .aidl : Proxy (for use by the client) and Stub (for the service side) that implement this interface.


For Java code in the client process, the proxy object looks like a normal Java object that implements our interface, and this code can simply call its methods. At the same time, the generated implementation of the proxy object automatically serializes the passed arguments, communicates with the service process via libbinder, deserializes the result of the call passed back and returns it from the Java method.


Stub works the other way around: it accepts incoming calls via libbinder, deserializes the arguments, calls the abstract implementation of the method, serializes the return value, and passes it to the client process. Accordingly, to implement a service, it is enough for a programmer to implement abstract methods in the class inherited from Stub.


Such a Binder implementation at the Java level allows most code to use a proxy object, without even thinking about the fact that its functionality is implemented in another process. To ensure full transparency, Binder supports nested and recursive interprocess calls. Moreover, the use of Binder on the client’s side looks exactly the same, regardless of whether the implementation of the used service is located in the same or in a separate process.


In order for different processes to “find” each other’s services, Android has a special ServiceManager service that stores, registers and issues tokens of all other services.


Binder is widely used in Android to implement system services (for example, the package manager and the clipboard), but the details of this are hidden from the application developer by the high-level classes in the Android Framework, such as Activity , Intent, and Context . Applications can also use Binder to provide each other with their own services — for example, the Google Play Services application does not have its own graphical user interface at all, but provides developers of other applications with the opportunity to use Google Play services.


More details about Binder can be found on these links:



In the next article, I’ll talk about some of the ideas on which the high-level parts of Android are built, some of its predecessors, and basic security mechanisms.


')

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


All Articles