📜 ⬆️ ⬇️

Overview of the Android core features

“And I… wash the carburetor!”
Joke

Introduction

In the kindergarten, we and like-minded people dissected grasshoppers in the hope of understanding their structure. The school was soldering the radio “Russia”. At the institute it was the turn of the cars, the nuts of which were repeatedly rearranged. Interests have changed, but the desire to “disassemble” sometimes wakes up, and today it is aimed at Android.
')
How many times have you been rescued by the availability of Android sources? I - no longer count. Android is an open project, but, unfortunately, we only have the opportunity to read; it’s almost impossible to edit the Android code without being a Google employee. Let's grumble over this moment and load the repository. How to do this is perfectly described on the official site .



Common architecture

The architecture of Android can be schematically depicted as follows:



The original scheme does not contain information about the features of the kernel and does not focus on Binder and system services. But Binder is the “glue” that binds all the components of the system.

As a rule, the books describe the top left blue rectangle, that is, the API that is available to the application developer. We are interested in everything below. Today we consider only the core.

Core

The kernel is the central part of any distribution called Linux. Despite the availability of a “ clean ” kernel, many developers (Ubuntu, Fedora, SuSe, etc.) add their patches to it before being included in the distribution. Android goes the same way, only at the cost of losing direct compatibility: it will not start on a “clean” core. Currently, there are intentions to include “androidisms” in the main version of the kernel; in 2011, Linus Torvalds gave 4-5 years to this process. Success has already been achieved in the framework of the inclusion of the wakelocks mechanism in the 3.5 kernel version.

Consider the “androidisms” in more detail.

Wakelocks

The history of this mechanism is epic, it draws on the collection of articles “The path of wakelock to Linux”: their discussion took about 2000 letters in the LKML mailing list .

Desktops and laptops have a well-established system of power modes (for x86 processors there are several of them): the computer works “at full speed” when something is done and goes into an energy-efficient mode when the system is idle. Leaving in the "sleep" mode occurs either after a rather long inactivity, or manually, for example, when closing the lid of the laptop.

On the phones, a different mechanism was required: the main state of the system is “hibernation”; it is only exited when necessary. Thus, the system can fall asleep, even if some application is active. In Android, the wakelock mechanism was implemented: if an application (or driver) performs something important that should reach its logical conclusion, it “captures” the wakelock, preventing the device from falling asleep.

Attempts to port the wakelock mechanism to the core have provoked resistance from many developers. Android programmers solved a specific problem, the solution of which was a certain mechanism. The conditions of the problem were very narrow. The target platform is ARM, so its features were used: ARM processors initially assume a frequent change in the “sleep” and “wakefulness” operating modes, unlike x86. In Android, applications communicate with the power management system through PowerManager , and what should Linux client applications do?

The Android developers didn’t even try to find a common solution “for the future”, which would then flow into the main core without problems, didn’t consult the Linux core community on this issue. Can you blame them for it? Despite all the problems and discussions, as mentioned above, an API with identical autosleep functionality appeared in the kernel.

Android application programmers rarely have to deal with wakelock, as the platform and drivers handle the obligations imposed on them, taking into account the "sleep" mode. However, the familiar PowerManager will help to intervene in this process. By the way, the author comes to mind only one scenario: to prevent the phone from falling asleep when starting the service from BroadcastReceiver, which is solved by the auxiliary class from Android Support Library WakefulBroadcastReceiver .

Low memory killer

In the standard Linux kernel there is an Out of Memory Killer , which, based on the badness parameter, determines the process to be killed:

badness_for_task = total_vm_for_task / (sqrt(cpu_time_in_seconds) *
sqrt(sqrt(cpu_time_in_minutes)))


Thus, the more the process consumes memory and the less it lives, the less lucky it will be.

All programmers who have read the documentation or have been interviewed, know that, first, the process can be “killed” and if there are free resources, secondly, the candidate for crowding out is selected according to other criteria: the presence of “live” Android component, visibility user and so on.

The mechanism is quite simple: each process is assigned a priority from -17 to 16, while the higher the priority, the higher the probability of killing the process, and, depending on the amount of free memory, the priority is chosen from which the processes will be completed. Priorities are described in ProcessList.java . Interestingly, the priority of the HOME_APP_ADJ home screen application is quite high, but I thought: why is it constantly restarted?

The mOomAdj and mOomMinFreeLow / mOomMinFreeHigh arrays just set the “when to clean up” rules:

 private final int[] mOomAdj = new int[] {FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ}; private final long[] mOomMinFreeHigh = new long[] {49152, 61440, 73728,86016, 98304, 122880}; 


Thus, the home screen application is pushed out with the remaining 73728 KB of free memory on the phone with a 1280x800 screen and 700 MB of RAM.
ProcessList passes the corresponding values ​​to the kernel , as can be seen in its updateOomLevels method.

Priorities for processes are set by the Activity Manager Service , one of the many system services that can be communicated through Activity Manager .

Binder

Binder , along with other solutions (Files, Sigmals, Sockets, Pipes, Semaphores, Shared Memory, etc.), solves the problem of interprocess communication. The legs of this solution are growing from the project OpenBinder , the developers of which at one time went to the Android team.

Bionic (the libc implementation) does not use System V IPC , since in the Androids environment standard tools will lead to resource leaks.

Features:
  1. Flow control (we all remember that a service that supports AIDL must work in a multi-threaded environment). The maximum number of threads is 15 ( ProcessState.c , open_driver method), so you should not block large quantities of binder flows without unnecessary necessity.
  2. The mechanism of informing about the death of the process that holds the Binder object “ Link to Death ”. For example, through it, the Window Manager learns about the death of an application and deletes its associated windows. Also, the LocationManager, at the death of all its listeners, stops polling the GPS receiver. Lowmemorykiller satisfied. :)
  3. 2 call modes: blocking and non-blocking (oneway). In the first case, the calling thread is blocked and waits for the method to be processed in the thread of the handler process. Programmers simply call methods through the dot, the platform takes over the interaction of threads.
  4. Transmit UID and PID for security. Through them, the system services determine whether the calling process has the right to perform the requested actions.
  5. For Java programmers, proxy and Stub creation tools for converting calls to Java methods into binder transactions.


Consider how it works on the example of LocationManager.



When we want to get GPS information, the following happens:
  1. Our application calls the appropriate method on the LocationManager.
  2. LocationManager delegates the call to a proxy object that converts Java methods and objects to a binder transaction (the proxy object of the LocationManager is mService).
  3. The transaction is sent to the kernel driver, which redirects its LocationManagerService, inherited from .LocationManager.Stub.
  4. . LocationManager. Stub does the opposite: it expands the transaction into a call to a Java method.
  5. . LocationManagerService processes the request (using, for example, the GPS driver).
  6. The stub object packs the response into a transaction, and the process goes in the opposite direction.
  7. The driver sends the answer back.
  8. The proxy object unpacks the result of a method call into Java objects.


As you can see, quite a large logic is hidden behind the call to the system services methods.

Ashmem

Anonymous Shared Memory (ashmem) is a shared memory mechanism. In Linux, as a rule, this mechanism is implemented through POSIX SHM . Android developers found it not sufficiently protected, which could play into the hands of malware. Ashmem's features are a link counter, if zeroed out, the shared memory can be freed (for example, the memory is released when all processes that use it are completed) and the reduction of the shared region due to lack of memory in the system.

A prime example of using ashmem is the zygote process, which loads the starting version of Dalvik VM with loaded base classes and resources, and the rest of the applications simply reference this memory.

Binder has a transaction size limit of 1MB (otherwise a TransactionTooLargeException exception will be thrown). If we need to transfer a large amount of data from one process to another, we can just use Ashmem: create a MemoryFile and transfer the file descriptor to another process.

Logger

Normal distributions generally use two logging systems: the kernel log, accessible via the dmesg command, and the system logs, usually located in the / var / log directory.

The Android system includes several cyclic buffers for storing user program messages (which prolongs the life of memory cards, since read-write cycles are not wasted) and does not have additional delays from working with sockets, which are used in standard syslog .



The diagram shows the general Android logging system. The logging driver provides access to each buffer via / dev / log / *. Applications have access to them not directly, but through the liblog library. The classes Log , Slog and EventLog communicate with the library liblog. The adb logcat command shows the contents of the “main” buffer.

Conclusion

In this article, we briefly reviewed some of the features of Android as a Linux system. Behind the brackets are some other parts (pmem, RAM console, etc.), as well as such important aspects of the platform as a whole, like System Service, the system startup process and others. If this topic is interesting, in the following articles we will consider them.

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


All Articles