The Android architecture or Android stack is composed of a number of components organized into layers.
Android is an open-source operating system based on Linux kernel on which applications run on an application framework that controls the activities supported by the libraries and Dalvik virtual machine which compiles and converts all java class files into a single file. The application layer in an android operating system is the top layer that includes utilities like SMS, contacts, phone, browser, camera, media player, cleaner, etc. All of these are developed in Java programming language.
The Linux Kernel
The Android operating system is a multi-user Linux system in which each application is a different user. The foundation of the Android platform is the Linux kernel. Android Linux is a variant of the standard GNU Linux operating system kernel. It also is used to shield a higher level layers in Android, from a whole variety of hardware diversity. Different CPUs, different types of memory, different types of sensors, different types of transceivers and different implementations of all those devices as well. Using a Linux kernel allows Android to take advantage of key security features and allows device manufacturers to develop hardware drivers for a well-known kernel.
Each process has its own virtual machine (VM), so an application’s code runs in isolation from other applications. By default, every application runs in its own Linux process. Android starts the process when any of the application’s components need to be executed, then shuts down the process when it’s no longer needed or when the system must recover memory for other applications.
Android Linux in its kernel is written in C and ships separately form the rest of the Android stack. It contains a number of optimizations intended to meet the needs of mobile devices and apps. Android Linux is also used to mediate access to and share the various hardware resources. Things like CPUs or network communication are shared by different applications either at the services layer, in the system as Android’s frameworks. Or up in the applications themselves that you might write or you might get off the shelf. For example, the Android Runtime (ART) relies on the Linux kernel for underlying functionalities such as threading and low-level memory management.
Android Linux Kernel is a forked version of the GNU Linux Kernel, so they’re not identical. Android Linux also extends GNU Linux in some very important ways. For example, it has a shared memory driver that’s used to conserve memory that’s shared between processes. It has a special power management module that manages power and a mobile device effectively. It’s got a special interprocess communication driver called the Bider driver that’s used to communicate between processes very efficiently.
In particular, there are certain device drivers, there are certain applications that won’t run precisely the same
in Android Linux versus GNU Linux. However, the Android Linux kernel should offer a familiar and robust capabilities for mobile applications that system programmers will be comfortable with and end users have to come know and expect to provide trustworthy communication in a mobile network environment.
Middleware infrastructure
Middleware infrastructure, comprise of layers of software written in C/C++, that reside atop the underlying operating system, but below the applications and application frameworks. Android Middleware infrastructure provides reusable capabilities that extend hardware-centric OS kernel and its protocol mechanisms. Key elements in Android’s Middleware infrastructure, are its hardware abstraction layer, its runtime environment, and its support for native libraries. The key application components in Android, include activities, services, broadcast receivers, content providers and intents.
Hardware Abstraction Layer (HAL)
The Hardware Abstraction layer sits on the top of the Linux Kernel, which is used to shield the upper layers of the Android stack for low-level hardware details and diversity and underlying elements. The hardware abstraction layer (HAL) provides standard interfaces that expose device hardware capabilities to the higher-level Java API framework.
Things like the Wi-Fi driver, Bluetooth, or GPS, or the radio interface are all handled by the hardware abstraction layer. The HAL consists of multiple library modules, each of which implements an interface for a specific type of hardware component, such as the camera or bluetooth module. When a framework API makes a call to access device hardware, the Android system loads the library module for that hardware component.
Another purpose of this layer is to shield the original equipment manufacturers from the virality of the GNU Public License.
Android Runtime
Another important layer of Android middleware elements, is called the Android runtime, which itself contains two types. One part, is known as a managed execution environment, that’s used to efficiently run Java-based apps and some of Android’s system services. Examples of this, in early versions of Android, would include the Dalvik Virtual Machine.
Characteristics of Dalvik virtual machine is as follows; Smart phone application run in a instance of a Dalvik virtual machine. There may exist various instances of Dalvik virtual machine instance on single machine, each instance runs in the form of chunks or separate Linux process.Dalvik virtual machine depend on the original OS Linux kernel for the execution, isolation memory manage process and threads supporting.Dalvik virtual machine is register based process
More recently, Dalvik has been replaced by the Android Run Time or ART, which uses a so called, ahead of time compilation model, instead of a just in time compiler, and a virtual machine model. This managed execution environment, be it ART or Dalvik, is optimized for the constraints of mobile devices.
For devices running Android version 5.0 (API level 21) or higher, each app runs in its own process and with its own instance of the Android Runtime (ART). ART is written to run multiple virtual machines on low-memory devices by executing DEX files, a bytecode format designed specially for Android that’s optimized for minimal memory footprint. Build tools, such as d8, compile Java sources into DEX bytecode, which can run on the Android platform.
Core Libraries
The second part is Core Java class libraries and core Android class libraries.
Android’s core libraries provide key components including activities, services, broadcast receivers, content providers and intents, which glue all the other components together. Android App Components are essential building blocks of mobile apps running in the Android environment, that provide various hooks via which Android can affect the lifecycle of these apps.
Intents
One of the key components, which is used to glue together many of the other components, is called intents. Which are essentially messages, that describe an action to perform or an event, that’s already occurred. An intent is a message that one component uses to interact with or request functionality from other components. Intents are essentially the glue that helps to integrate Android apps. App development is therefore simplified since existing components can be reused as black boxes
An intent is implemented as a so-called passive data structure. A passive data structure just consists of fields and field accessor and mutator methods. An intent contains a number of elements including the name, action, data, category, extras, and flags. Some of these elements are of interest to app components that receive the intent such as the action, data, and extras. Other elements in an intent are of interest to Android’s intent framework itself. Examples of these elements are flags, category, and name elements.
The Data element defines the data and action it should operate upon. For example, if the action is ACTION_CALL, the data field would be tel: URI. Extra elements provide additional information that’s delivered along with an intent, in addition to the action and the data. Internally, is extras restored as a so-called Bundle and a bundle is essentially a key-value or an attribute-value pair.
A category provides Android with information about the type of component that can handle an intent. In particular, it provides this information to the Android Activity Manager Service which is part of the intents framework. The CATEGORY_DEFAULT allows an activity to be started by an implicit intent when no specific category is assigned to it. CATEGORY_LAUNCHER allows the activity to be an initial Activity of a task and is listed in the top-level app launcher. CATEGORY_BROWSABLE allows the activity to be safely evoked by a browser, display data reference by URL. And CATEGORY_HOME allows the activity to be displayed on the home screen.
Flags are used to specify how Android itself should handle the intent. Once again, it’s the Android Activity Manager Service that actually does the heavy lifting here. So if you set the flag of an intent to say Intent FLAG_ACTIVITY_NO_HISTORY, the new activity is not kept in the history stack. So as soon as the user navigates away from it the activity is finished.
The data in the fields in intent can be passed from one component to another. This approach is similar to
communicating between objects by passing arguments to a method call. Android automatically handles the cases where components live in different processes.
A named, or so-called explicit intent, is sent to an instance of a designated class. Explicit intents are typically
used to interact with components that reside in the same application. Explicit intents are particularly
important when starting to or binding to services. If the name is omitted in intent, however, then other elements in the intent are used to identify the target components. These include the action, data, and category elements in intent which indicate the actions, the form, the data to act on, and so on.
Apps can use intents to describe two types of things. The first thing they can do is to find an operation to perform. For example, the ACTION_VIEW intent can be used to display data to the user. Likewise, the ACTION_CALL intent can be used to perform a call to someone specified by the data, such as a phone number. Android delivers the intent to the Activity or Service that can best handle it.
And intent can also be used to describe an event that’s already occurred. For example, ACTION_BATTERY_LOW intent indicates there’s a low battery condition on an Android device. ACTION_AIRPLANE_MODE_CHANGED intent Indicates the user has switched the phone into or out of airplane mode. Once again, Android delivers the intent to the broadcast receiver or receivers that can best handle it.
An action field abstractly describes an operation performed or an event that occurred. Action is similar to a method name associated with a set of arguments. An action is designated by a Java string. Actions can target both activities and receivers. Some of the action names are predefined by Android such as ACTION_CALL, ACTION_EDIT, ACTION_BATTERY_LOW, and ACTION_HEADSET_PLUG. Of course, action names could also be defined by users. Such as ACTION_DOWNLOAD_IMAGE.
Instead of defining action in isolation define an intent protocol that components can handle. This protocol defines an intent’s action, input, and output. For example, ACTION_VIEW is an action that’s predefined by the intent class. The action that this action takes is displaying data to a user, the input is the URI from which to retrieve
the data and there’s no output at all. ACTION_PICK conversely is used to pick an item from data, the input is a URI containing a directory of data, from which to pick an item, such as a list of images. And the output that comes back is the URI of item that was picked.
A component invokes a method to send an intent to 0 or more components. The startActivity() and startActivityForResult () methods can be used to launch an activity with an action intent. Sendbroadcast() and
various methods related to send broadcast are used to send event intents to a BroadcastReceiver. There can be 0 or more of these receivers depending on various conditions. And finally is a pair of methods, startService or bindService that can be used to launch and communicate with a service via an intent. Unlike activities or receivers where there may be multiple potential recipients of the intent, intent can only be sent to one service.
Methods pass the intents asynchronously, they don’t block the caller while the various components carry out the operations in processing related to the intent. One way to connect components is to have them reside in the same app. Another configuration can be where some of the components run in different applications. They’re provided by different applications. Different application developers could build them at different points in time. And Android and the Android component framework is responsible for gluing them all together at the right time, as the apps are on. Once again, these connections between the activities and services occur via late runtime binding, rather than at compile time. Components can run in the same process, Or they can run in different processes. This mapping of components processes can be configured declaratively in the so-called AndroidManifest.xml file.
There are various upsides and downsides to configuring components into the same process or in different processes. In general, there are trade-offs between robustness and performance. In particular, if you put all of
the components into the same process, that may help to improve performance because the communication cost of talking between the various components will be reduced. Since you don’t have to send intents across process boundaries. Conversely, if you put things in the same process, all the components, then if something goes wrong with one of the components, that may affect the other components in the process.
In general, runtime discovery of components is very flexible and very powerful. For example this type of model enables apps to have loosely-coupled components. In other words, the components don’t have to know about each other when they’re developed. And they can be extended and integrated dynamically by using Android’s component framework.
However, it’s important to keep in mind that late binding can also lead to some unpleasant surprises. For example, there’s a problem that’s commonly understood on Android as activity hijacking, and this occurs when a malicious activity Is launched in place of the intended activity. And because Android provides dynamic binding of intents to components, like activities, it’s possible for a bad activity to sneak in and take the place of one that was intended.
An implicit intent is delivered to a component only if a filter matches. A map is an overview of the intent filtering and intent resolution mechanisms provided by Android. Intent filters describe which types of intents a component can handle. These intent filters glue together loosely coupled components that are accessed by intents. It’s the Activity Manager’s job to use intent filters to match the intent fields against the filtering criteria. As you can see, these filters are defined in the Android manifest file
Intent filter fields reflect the action, data, and category fields of an intent, An implicit intent is tested against the filter in all three areas. All must pass in order for the intent to be delivered to the component. The extras in flags however play no part in resolving which component receives an intent.
There are two types of intent handler components that Android supports. Statically configured and dynamically configured. Statically configured intent handler components are specified declaratively in the AndroidManifest.xml file. It’s also possible, however, to dynamically configure intent handler components by programmatically
specifying them in Java code.
The Activity Manager Service runs in the system server process in Android, and filters events and dispatches them to the intent handlers. Despite its name however, the Activity Manager Service handles other types of components as well such as broadcast receivers and services. And implicit intent may match the intent filters of multiple activities. If there is more then one match to an intent Android displays its user dialog that enable users to do several things. First they can select the app they want to use to handle the intent. They can also indicate that
app become the default choice for future intent resolutions that match or they could select this app to be used
just this one time in which case Android will re-prompt the user in the future next time conflicts occur.
AndroidManifest.xml file
Android needs certain information to execute an app. This information includes a variety of different things. For example it needs to know the name of the app Java package. It also needs to know information about the app components, classes and capabilities. For example, which components are exported from this app to other apps, what are the component names? Which processes will be use to host the components? What intents are handled
by the components? Android also need to know what permissions the app has to have as well as the permissions other apps need to interact with the components in this app. Note that later versions of Android have changed so that users grant permissions to apps while the app is running rather than when they install the app. Android also needs to know the minimum and target API levels of Android that the app requires. Traditionally, this information was provided In the AndroidManifest.xml file.
However if you use Gradle, this information can be provided instead by the Gradle build file rather than putting it in the AndroidManifest.xml. There’s a whole variety of other things that need to be provided to Android from some central place. The AndroidManifest.xml file provides that central place to convey all these types of information.
The application element is a container that includes tags for specifying the app components. For example activities implement part of the app’s visual user interface. All activities must be represented by activity elements in manifest file. Services implement long-duration background operations. All services must be represented by service elements in a manifest file. Broadcast receivers can be used by apps to receive broadcast intents even when other app
components aren’t running. The use of a manifest file is actually optional for receivers. You can use them, or not. Content providers supply structured access to data managed by an app. All app providers must be defined in
a <provider> element in the manifest file.
Activities
Activities are components that can be started by intent, and they provide a screen within which users can interact, in order to do something, like prompt the user for input, accept user input, accept button clicks, and text, and display the results to users.
Broadcast receivers or components, play the role of event handlers. They respond to broadcast announcements,
which might be within an app or between apps, as well as from the system, layer parts of the Android up to the apps themselves. There are also components known as services. These are not user-facing, in other words, they run in the background, and they’re used to perform long-running operations, and or to access remote resources, such as those connected across a network.
And finally, the last type of app component is called content providers, and they are used to manage access to structure data. And they provide data security mechanisms, which make it easier to get the data in files, and other resources securely and efficiently, on an Android device.
Like the Java platform, the Android platform strives to provide a balance between run time performance and
developer productivity. In particular, Android’s core libraries, as well as the Java libraries implemented on Android, are often defined using wrapper facades.
Wrapper facades provide a Java binding to the native C/C++ libraries. These libraries include things like Libc, which is what used to access the underlying services in the Linux Kernel, as well as various database, libraries, multimedia streaming libraries, 2D and 3D graphics libraries, libraries for handling secure communication, and many other native libraries as well. All of which can be accessed via Java code.
A summary of some key core Android libraries available to the Android developer is as follows −
- android.app − Provides access to the application model and is the cornerstone of all Android applications.
- android.content − Facilitates content access, publishing and messaging between applications and application components.
- android.database − Used to access data published by content providers and includes SQLite database management classes.
- android.opengl − A Java interface to the OpenGL ES 3D graphics rendering API.
- android.os − Provides applications with access to standard operating system services including messages, system services and inter-process communication.
- android.text − Used to render and manipulate text on a device display.
- android.view − The fundamental building blocks of application user interfaces.
- android.widget − A rich collection of pre-built user interface components such as buttons, labels, list views, layout managers, radio buttons etc.
- android.webkit − A set of classes intended to allow web-browsing capabilities to be built into applications.
Java Threads
Android’s core library also provides many other user interface and persistence components. There are also a number of capabilities in the core libraries, handling Java threading, concurrency and synchronization.
Java threads, are the smallest unit of execution for sequences of programmed instructions. Each process can have multiple threads that run concurrently and, of course, each app in Android runs in its own process, so that’s means each app can have multiple threads. Each thread contains a call stack, that’s used to keep track of the method state and data, as methods are called, and as they return from being called. Android implements Java threads, using various mechanisms in various layers of it’s stack.
Java threads must be given some code, when they start to run. There are a number of different ways to do this. One way to do this is to implement the Runnable interface. Android encapsulates the bulk of the Java thread classes, within its concurrency frameworks. Such as the Hammer framework and AsyncTask framework.
Starting a Java thread, takes a non-trivial amount of time and system resources. Java threads are written in Java. But then they use some of the services provided by the Dalvik or ART execution environments, which in turn, use various services provided by Libc and the underlying Android Linux kernel, in order to be able to take sequence of instructions. And have them actually run on separate cores in the hardware.
Java Application Framework Layer
The Application framework layer in Android contains various system services that provide apps with the capabilities and information they need in order to do their work. There are several things that are provided by Android’s Application framework layer. One thing it does is it exposes the hardware and Linux operating system kernel up to apps that do it in a way that’s Java-centric and object-oriented rather than being programmed in C and C++. Which have more accidental complexities. They run continuously during the system operation. The control flow is driven by various events and calback.
The application framework is an integrated set of components that provide a reusable architecture for a family of related apps. Frameworks, internally, use an event-driven programming model that makes it easier to plug application code into the reusable portions of the framework. They’re important because they help to enhance systematic reuse by providing canonical structure and functionality to apps, so the apps don’t have to rewrite that
functionality and structure from scratch. Typically, an app registers callbacks for specific types of events that
can occur within a framework.
For example, in the context of Android, Android activities, broadcast receivers, services and so on are used to register automatically for different kinds of life cycle events. Such as when the app starts or when it’s about to be paused or resumed and so on and so forth. A callback is essentially an object that’s passed as a parameter to a method provided by the framework.
The framework transparently monitors event sources, such as user input buffers, network connections, files, and so on, or directories for the activity of interest that the app is registered for. When something happens that’s of interest to app components, the framework is responsible for calling back the object registered as part of the callback. The processing then occurs within the context of a thread that is borrowed from the framework for the duration of a callback. When the app callback is done, the control returns back to the framework, where it waits for the next event to occur. This process of having the framework be responsible for managing the bulk of the control
flow in the application leads to what’s often called, inversion of control.
Each application in the android operating system is running separately from other and has memory spaces reserved for them making each one data inaccessible to others. For communication among applications, message parsing is used.
API
The entire feature-set of the Android OS is available to you through APIs written in the Java language. These APIs form the building blocks you need to create Android apps by simplifying the reuse of core, modular system components, and services, which include the following:
- A rich and extensible View System you can use to build an app’s UI, including lists, grids, text boxes, buttons, and even an embeddable web browser
- A Resource Manager, providing access to non-code resources such as localized strings, graphics, and layout files
- A Notification Manager that enables all apps to display custom alerts in the status bar
- An Activity Manager that manages the lifecycle of apps and provides a common navigation back stack
Content Providers that enable apps to access data from other apps, such as the Contacts app, or to share their own data - Developers have full access to the same framework APIs that Android system apps use.
These system services run continuously throughout the operation of the system. Doing things like managing calls, managing window interactions with the user, managing access to various resources, keeping track of where the phone is located, providing notifications to the notification status area, managing the lifecycles of activities, broadcast receivers and services and so on. And due to the inversion of control that’s inherent in an application framework, these system services manage the control flow via various event handling operations and callbacks, either the system code or the application code itself. One of the key system services that are part of the application framework layer is called the activity manager service.
System Apps
On top of all these different layers of middleware and operating system services are, of course, the apps that we use every day. And this includes things like the browser, calendar, camera, photo album app, calculator, clock, alarm, email, etc.
Android comes with a set of core apps for email, SMS messaging, calendars, internet browsing, contacts, and more. The system apps function both as apps for users and to provide key capabilities that developers can access from their own app. For example, if your app would like to deliver an SMS message, you don’t need to build that functionality yourself—you can instead invoke whichever SMS app is already installed to deliver a message to the recipient you specify.
References and Resources also include:
https://www.researchgate.net/publication/319617606_Evolution_of_Android_Operating_System_A_Review
http://www.dre.vanderbilt.edu/~schmidt/cs891f/2017-PDFs/L4-pt3-core-and-native-libraries.pdf