guided research project -

46
Guided Research Project Analyzing the Variability Realization in Android Nicolas Fußberger Student Number: 384507 Advisor: Dr. Bo Zhang Department of Computer Science TU Kaiserslautern

Upload: others

Post on 11-Dec-2021

2 views

Category:

Documents


0 download

TRANSCRIPT

Guided Research Project

Analyzing the Variability Realization in Android

Nicolas Fußberger Student Number: 384507

Advisor: Dr. Bo Zhang

Department of Computer Science

TU Kaiserslautern

2

Table of Contents

1 Introduction .............................................................................................................. 4 1.1 Motivation ................................................................................................................... 4 1.2 Problem Statement ...................................................................................................... 4

1.2.1 Research Problems ............................................................................................................ 4 1.3 Research Method ......................................................................................................... 5

1.3.1 Research Questions ........................................................................................................... 5 1.3.2 Research Procedure ........................................................................................................... 6

1.4 Research Contribution .................................................................................................. 6 1.5 Research Scope ............................................................................................................ 6 1.6 Summary ..................................................................................................................... 6

2 Foundations .............................................................................................................. 7 2.1 State of the Art ............................................................................................................ 7

2.1.1 Terminology ....................................................................................................................... 7 2.1.1.1 Software Product Line ........................................................................................................... 7 2.1.1.2 Variability ............................................................................................................................... 7 2.1.1.3 Feature ................................................................................................................................... 8

2.1.2 Binding Time ...................................................................................................................... 9 2.1.3 Overview of Variability Realization Techniques .............................................................. 10

2.1.3.1 Conditional Compilation ...................................................................................................... 10 2.1.3.2 Conditional Execution .......................................................................................................... 11 2.1.3.3 Inheritance/Polymorphism .................................................................................................. 12 2.1.3.4 Module Replacement .......................................................................................................... 13 2.1.3.5 Aspect Orientation ............................................................................................................... 13 2.1.3.6 Frame Technology ............................................................................................................... 14

3 Android ................................................................................................................... 15 3.1 Android Architecture .................................................................................................. 16 3.2 Android from a Product Line Perspective .................................................................... 17 3.3 Conceptual Architecture of Android ............................................................................ 18

3.3.1 Overview .......................................................................................................................... 18 3.3.2 Android Configuration ..................................................................................................... 18

3.3.2.1 Product Configuration ......................................................................................................... 19 3.3.2.2 Board Configuration............................................................................................................. 20

3.3.3 Android Build System ...................................................................................................... 20 3.3.4 Source Code ..................................................................................................................... 21

4 Analysis ................................................................................................................... 22 4.1 Configurability of Android .......................................................................................... 22

4.1.1 Qualitative Analysis ......................................................................................................... 22 4.1.1.1 Configuration mechanisms .................................................................................................. 22

4.1.2 Quantitative Analysis ....................................................................................................... 23 4.1.2.1 Configuration Options ......................................................................................................... 23 4.1.2.2 Configuration Impact ........................................................................................................... 24

4.2 Variability Mechanisms in Configuration / Build System / Source Code ........................ 26 4.2.1 Qualitative Analysis ......................................................................................................... 26

4.2.1.1 Overview .............................................................................................................................. 26 4.2.1.2 Conditional Compilation ...................................................................................................... 26 4.2.1.3 Conditional Execution .......................................................................................................... 29 4.2.1.4 Module Replacement .......................................................................................................... 30 4.2.1.5 Inheritance ........................................................................................................................... 32

4.2.2 Quantitative Analysis ....................................................................................................... 35 4.2.2.1 Conditional Compilation in Source Code ............................................................................. 35 4.2.2.2 Conditional Execution in Build System ................................................................................ 42

3

5 Conclusion & Future Work ....................................................................................... 43

6 References .............................................................................................................. 45

4

1 Introduction 1.1 Motivation With ever increasing software complexity, new engineering methods are needed to meet quality demands and a short time-to-market. Especially the customization of products for different market segments, different end users and different use-cases has been a reason for increasing complexity. To delay these design decisions as much as possible, variability is often shifted from hardware to software. Thus, a new set of engineering methods is needed to develop multiple software variants while at the same time meeting quality and time-to-market goals. Software product line engineering has emerged as a vibrant research field to address these challenges. Software Product Lines are developed by systematically managing variability while taking advantage of commonality to achieve the necessary quality and time to market by strategic reuse. There are many successful cases in industry, where methods of software product line engineering have been successfully applied, most famously presented in the Product Line Hall of Fame1, often reporting spectacular results like a ROI of 10:1 or a productivity improvement of 360% (Cummins inc.). However, experience reports from industry are mostly at a very abstract level since they need to protect their business from the competition. Especially the source code of these product lines is not available to researchers. Therefore, researchers have turned to publicly available open source product lines. Most famously the Linux Kernel has been studied extensively by the product line community (e.g. [1][2][3]). However, when it comes to variability realization techniques, most studies focus only on the usage of conditional compilation in source code. The analysis of Preprocessor code can be easily automated, yields quantitative results and can be scaled to a large number of product lines [3]. There is little empirical work (quantitatively or qualitatively) on the usage of other known variability realization techniques like polymorphism or module replacement. Additionally, most studies focus on the source code and variability in build system or configuration is often neglected. We intent to address this gap with our research by investigating the variability realization in Android, a very well-known product line that has – to the best of our knowledge – not had any attention in product line research.

1.2 Problem Statement

1.2.1 Research Problems From these observations mentioned in the introduction we conclude the following research problems:

P1: There is little knowledge (quantitatively or qualitatively) on the usage of variability realization techniques in large product lines besides the usage of conditional compilation in source code.

P2: Existing analysis focus only on the source code and rarely investigate the usage of

variability implementation mechanisms in configuration and build system.

1 http://splc.net/fame.html

5

1.3 Research Method To address these research problems, we will study the variability realization of large open source product lines. We will not only focus on the usage of conditional compilation in source code but also investigate the usage of other well-known implementation techniques in source code, build system and configuration files. Since the variability realization is also highly related to the configuration mechanisms, we also study the configurability of Android. We chose the Android operating system since it is a very popular software ecosystem that has not been studied by the product line community2. Moreover, it is a very heterogeneous software system incorporating many different open source projects and is characterized by its decentralized development. Therefore, we expect to find not a consistent use of a single variability realization technique but the usage of many different techniques that are used together.

1.3.1 Research Questions We conclude the following research questions. While RQ2 addressed both P1 and P2, RQ1 can be considered a prerequisite. Since each variation point can be ultimately traced to some configuration option, is it necessary to understand how Android is being configured in order to understand how variability is realized. RQ1: How Is Android being configured for a specific device?

RQ1.1: What configuration options exist? RQ1.2: What configuration mechanisms are used? RQ1.3: What is the impact of the configuration?

RQ2: How is variability realized in Android?

RQ2.1: What variability mechanisms are used on different layers? RQ2.2: Is the variability Implementation localized to individual layers or distributed among multiple layers (crosscutting)? RQ2.3: What is the quantitative usage of different variability realization techniques?

These research questions are addressed in the following chapters:

Table 1: Mapping of research questions to chapters

Research Question Chapter

RQ1.1 4.1.2.1

RQ1.2 4.1.1.1

RQ1.3 4.1.2.2

RQ2.1 4.2.1

RQ2.2 4.2.1

RQ2.3 4.2.2

2 Android has been studied from an ecosystem perspective [24], but to the best of our knowledge there has been no analysis of the variability realization in the operating system itself.

6

1.3.2 Research Procedure Our research combines qualitative and quantitative data on the variability realization in Android. Qualitative data like the configuration mechanisms or examples for the usage of different variability realization techniques for different purposes are obtained by studying the official Android documentation, existing literature and manually searching through the Android OS source code. Based on these findings we developed tools to extract quantitative data on the impact of configuration mechanisms and the usage of different variability realization techniques. All tools have been developed in Python and are heavily based on Kati3, a tool developed by Google that can parse and partially evaluate Makefiles. Based on the Makefile abstract syntax tree created by Kati and specific variable names used in Androids Makefiles, we can then automatically detect different variability realization techniques. Additionally, the VITAL tool [4] is used for extracting the usage of preprocessor commands in C/C++ source code.

1.4 Research Contribution Our main contribution in this project is the generation of empirical data (qualitative and quantitative) on the variability realization in Android, a large open source software ecosystem that is currently an open research topic. This includes qualitative data on the usage of different variability realization techniques and different configuration mechanisms in Android. We will also present quantitative data on the possible configuration options, configuration impact and the usage of variability realization techniques. To generate this data, we present a new analysis method for extracting variability data from Make-based open source systems and implemented the necessary tool support to automate this analysis.

1.5 Research Scope There are different flavors of Android for smart watches, smartphones, televisions, cars, which can altogether be viewed as a product line. But in this report, we will look at Android OS for smartphones/tablets and treat this version of Android alone as a product line. Therefore, we will only focus on variability mechanisms bound at construction time since this is one of the main variability drivers for smartphones, although Android also includes mechanisms for runtime variability (e.g. through property files, apps, etc.). Similarly, in our quantitative analysis we will focus on variability mechanisms related to hardware variability (i.e. defined in Android board configuration).

1.6 Summary Chapter two introduces the terminology and foundational knowledge about product lines, variability and binding times. Additionally, the variability realization techniques commonly found in literature are presented. In chapter three the target system Android, its architecture in terms of configuration, build system and source code is explained. Based upon this, chapter four contains the analysis results concerning Android’s configurability and variability implementation. We present the most important configuration mechanisms and analyze their

3 https://github.com/google/kati

7

impact on the rest of the system. Additionally, variability realization techniques in Android are presented, both, in terms of typical examples and quantitative results on the usage of different techniques.

2 Foundations 2.1 State of the Art

2.1.1 Terminology

2.1.1.1 Software Product Line „A software product line is a set of software-intensive systems sharing a common, managed set of features that satisfy the specific needs of a particular market segment or mission and that are developed from a common set of core assets in a prescribed way“ [5]. Instead of developing each product from scratch, product line engineering is about taking advantage of commonality and carefully managing variability. New products can be assembled from already existing parts (core assets) tailored to individual costumers. Despite tailor-made products, the expected benefits from adopting a product line over single system development include reduced costs, improved quality and fast time to market [6]. It is clear that these advantages do not come for free. A product line requires a serious upfront investment for domain engineering processes like scoping or development of core assets. Figure 1 shows the expected costs for developing several systems from scratch compared to a product line approach. A small number of systems can still be developed with less effort using a single-system development approach since there is no investment into strategic reuse required. Only if many different (but similar) systems are developed such an upfront investment pays off in the long term.

Figure 1: Comparison of costs for single system development and product-line development [7]

2.1.1.2 Variability Variability is a very general concept within product line engineering and can refer to the variability within a software development process as well as its resulting artefacts like requirements specifications, architecture documents, source code or test cases [8]. For our

10 1. Introduction to Software Product Line Engineering

for software, the break-even point is already reached around three systems.4

A similar figure is shown in [Weiss and Lai 1999], where the break-even

point is located between three and four systems. The precise location of the

break-even point depends on various characteristics of the organisation and

the market it has envisaged, such as the customer base, the expertise, and the

range and kinds of products. The strategy that is used to initiate a product

line also influences the break-even point significantly [McGregor et al.

2002]. Chapter 20 elaborates on the initiation of product lines.

1.3.2 Enhancement of Quality

The artefacts in the platform are reviewed and tested in many products. They

have to prove their proper functioning in more than one kind of product. The

extensive quality assurance implies a significantly higher chance of detecting

faults and correcting them, thereby increasing the quality of all products.

1.3.3 Reduction of Time to Market

Often, a very critical success factor for a product is the time to market. For

single-product development, we assume it is roughly constant,5 mostly com-

prising the time to develop the product. For product line engineering, the

time to market indeed is initially higher, as the common artefacts have to be

built first. Yet, after having passed this hurdle, the time to market is consid-

4 [Clements and Northrop 2001]: The sidebar on p. 226, “It Takes Two”, provides a closer examination

of the break-even point for software product lines. 5 In practice, this number varies, but for showing the effect of single-system vs. product line engineering

this assumption is sufficiently accurate.

Lowerdevelopment cost

Accumulated

Costs

Number of

Different Systems

Single Systems

System Family

Break-Even

Point

Up-Front

Investment

Lower Costs

per System

approx. 3 Systems

(Software Engineering)

Fig. 1-2: Costs for developing n kinds of systems as single systems compared to product

line engineering

Improved quality through reuse

Shorterdevelopment cycles

8

purpose, we want to use a definition of variability that is more targeted towards variability implementation. Variability can be defined as “the ability to derive different products from a common set of artefacts” [6]. Anastasopoulos and Gacek [9] differentiate between multiple types variability which are shown in Table 2. In general, one can differentiate between positive and negative variability, where positive variability adds functionality and negative variability removes functionality. Other categories denote the optional inclusion of code/requirements/components, their replacement (alternative) or change in their functionality. Another variability type is concerned with a change in the platform or environment of a system. An example for such variability is the migration of a system from a Unix-based environment to a Windows-based environment.

Table 2: Variability Types [9]

Variability Type Meaning

Positive Functionality is added

Negative Functionality is removed

Optional Code is included

Alternative Code is replaced

Function Functionality changes

Platform / Environment Platform or environment changes

To describe variability even further Pohl introduced the notion of variability subject and object [7]. A variability subject is “a variable item of the real world or a variable property of such an item” and describes what can vary. A variability object is “a particular instance of a variability subject” and describes how an item varies. An example for a variability subject is the cooking time of a steak where possible instances (variability objects) are rare, medium rare or well done. The manifestation of variability subjects in artefacts such as requirements, architecture or code is referred to as a variation point. Jacobson et al. first introduced this term as “one or more locations at which the variation will occur” [10]. Analogously, the “representation of a variability object within domain artefacts” [7] is called a variant. Consider the color of Apple’s iPhone. Possible variability objects include green, blue, black, yellow, etc. However, Apple only offers silver, gold, rose gold and black as possible variants for this variability object. Therefore, variants might only be a subset of possible variability objects.

2.1.1.3 Feature The term feature describes “a characteristics or end-user-visible behavior of a software system” [6] and is typically used in the context of a product line to distinguish multiple products. A product can be thought of as a set of features, a so-called feature selection. This concept allows to easily communicate product characteristics to all stakeholders while also enabling to manage variability and commonality in all software lifecycles. Anastasopoulos and Gacek [9] define multiple feature types (cf. Table 3) that constrain feature selection. A mandatory feature must be present in all products, while an optional feature may only exist in a subset of products. Constraints between features include alternative features (i.e. only one of several features can be selected) and mutually inclusive and exclusive features.

9

Table 3: Feature Types [9]

Feature Type Meaning

Mandatory The feature must be always included.

Optional The feature is an independent complement that may be included or not.

Alternative The feature replaces another feature when included.

Mutually Inclusive In order for the feature to be included, specific other feature(s) must be included as well and

vice versa.

Mutually Exclusive In order for the feature to be included specific other feature(s) must be left out and vice versa.

Features and their relations are typically specified in a feature model whose graphical representation is called a feature diagram. An example for a feature diagram of a graph library is depicted in Figure 2. The diagram is a tree whose nodes denote features and different notations for parent-child connections encode different feature types. Additionally, constraints for feature selection may be added as propositional logic. In Figure 2, an empty circle on top of a feature denotes on optional feature, while a filled circle denotes a mandatory feature. An empty circle at the bottom of a node defines a one-out-of-many relationship, so that only one of the child nodes may be selected for any product. A filled circle defines a some-out-of-many relationship and any number of children may be selected for a product. Mutually inclusive and exclusive feature types are defined via constraints. For example, the MSG algorithm can only be selected if the graph is undirected and weighted

(MSGUndirected Weighted).

Figure 2: A feature diagram for a graph library [6]

2.1.2 Binding Time When deriving a product from a common set of assets, at one point the decision needs to be made how to resolve variability added through variation points (i.e. which features to include or exclude in a product). The time at which this decision is made is called the binding time. Depending on the domain and on the business objective for a product line, it can make sense to make this decision sooner or later. For example, in embedded systems where resources are often scarce variability is bound rather sooner than later so that the code for a single product can be optimized and needs as little memory as possible. In other domains like the smartphone industry where additional functionality can be added by users by installing additional apps, such variability needs to be present at runtime.

10

Svahnberg et al. [8] define the following phases of a system’s lifecycle where variability may be bound: Product architecture derivation, compilation, linking and runtime. During product architecture derivation, variation points in the product line architecture need to be set to a particular variant to derive the architecture for a particular product. Examples for variation points may be optional components that correspond to an optional feature or selecting a particular specialization of a general component (e.g. depending on the target hardware platform). The most common technique for bounding variability at compile time is the C/C++ preprocessor which scans the source code and adds or removes certain parts. During linking, the object files created by the compiler are built into an executable. This step depends on the chosen programming language and technologies that are used. During runtime, it may be possible to extend the system by adding new variants (e.g. downloading apps on the smartphone) or to choose among a set of predefined variants (e.g. by changing the settings in an application).

2.1.3 Overview of Variability Realization Techniques In this section, we will give a brief overview of variability realization techniques commonly found in literature. We will only consider mechanisms that bind variability as early as compile-time. In particular we will not cover product architecture derivation as an earlier binding time [8]. We will look specifically at variability mechanisms in source code, although some mechanisms can also be used on other artefacts like requirements specification or architecture documentation.

Variability Mechanism Binding Time Implementation Granularity

Conditional Compilation Compilation Preprocessor Any

Conditional Execution Runtime Conditional Statements

Limited, mostly Statements

Inheritance/Polymorphism Compilation/Runtime Language Constructs

Any

Module Replacement Compilation/Linking Build System Any

Aspect Orientation Compilation Specific tools for code weaving

Limited, mostly statements

Frame Technology Compilation Specific tools Any

Cloning Compilation Copy&Paste, Branches

Any

Figure 3: Overview of variability mechanisms commonly found in literature. Data adapted from [11].

2.1.3.1 Conditional Compilation

Description Conditional compilation is a widely used variability mechanism in open source software as well as in industrial software [12]. It allows programmers to conditionally add or remove code before compilation based on a configuration. This variability is bound at construction time which allows for highly optimized and memory efficient code. This mechanism can be used to conditionally compile single statements, classes, modules – there is no limit for application in terms of granularity.

11

Implementation Conditional compilation is typically implemented using a preprocessor, most famously the C/C++ preprocessor. Using #ifdef directives, any part of the code can be conditionally compiled based on some #define definitions.

Figure 4 shows two examples for the usage of the C/C++ preprocessor. The left example demonstrates the conditional inclusion of files based on the target operating system (OS is Windows if __WINDOWS__ is defined through #define __WINDOWS__) and the right example shows the addition of a heat sensor based on if the target product supports such a sensor (if HAS_HEAT_SENSOR is defined through #define HAS_HEAT_SENSOR). Although preprocessors are mostly used for code, they work on any text-based artefact and can also be used for requirements documents, architecture documentation, etc.

Advantages & Disadvantages Conditional compilation is very easy to apply using available preprocessors. These are not limited to code but can be applied to any text-based document since they are oblivious to the syntax. However, for a long term benefit their usage requires some discipline. Since the preprocessor is also used for other things beside variability implementation (e.g. constant definitions), its usage as a variability mechanism should be clearly separable by employing a prefix like HAS_PREFIX. This also allows further automated analysis regarding code erosion and dead code detection [13]. The developer should also care for simple logical expressions inside #ifdef statements and avoid highly nested #ifdef code to keep the code maintainable [14].

2.1.3.2 Conditional Execution

Description Conditional execution uses standard conditional statements to implement variability. In contrast to conditional compilation these statements are executed at runtime and can thus be used to implement runtime variability. Since these statements are part of the programming language, they need to obey its syntax rules and can only be used to implement variable blocks of statements.

Implementation The build-in if-else or switch constructs of programming languages are used to implement conditional execution. In order to differentiate variability implementation from regular usage

1. #ifdef __WINDOWS__ 2. #include <winsock2.h> 3. #else 4. #include <sys/socket.h> 5. #endif

Figure 4: Example for conditional compilation using the C/C++ preprocessor. (Left) To include a file based on the operating system. (Right) To add functionality for a heat sensor.

1. #ifdef HAS_HEAT_SENSOR 2. Sensor sensor = new HeatSensor(); 3. Observer.register(sensor); 4. #endif

12

of conditional statements, it is recommended that a specific variable naming pattern (e.g. a common prefix) is chosen.

Advantages & Disadvantages Conditional execution is very easy to use and required no further tools since conditional statements are part of every higher programming language. Since conditional statements are also used for other things, there is risk of mixing the implementation of variability with the rest of the code. A naming pattern of variables can solve this issue. A disadvantage of this approach is the mixing of commonality and variability inside a single file. For example, a new variant cannot be added without first understanding and then altering the existing code. Like with conditional compilation there is also the risk of creating highly nested or complex conditional expressions that increase maintainability over time.

2.1.3.3 Inheritance/Polymorphism

Description There are multiple different variability mechanisms related to inheritance or polymorphism ranging from binding at compile time to binding at runtime. Mechanisms commonly found in literature include subtype polymorphism, parametric polymorphism (often called static polymorphism) and overloading [11][15] as well as multiple inheritance, mixin-based inheritance, object-based inheritance [9] and ad-hoc polymorphism and casting [15].

Implementation The implementation of inheritance-based variability mechanisms typically depends on the specific type of mechanisms and the programming language. Not all mechanisms can be implemented in all object-oriented languages. For example, Java does not support multiple inheritance (in contrast to C++). More information on how to implement subtype polymorphism and parametric polymorphism in C++ can be found in [16] and [17].

Advantages & Disadvantages A common advantage of inheritance-based mechanisms is the separation of commonality and variability. For example, an abstract class called Sensor implements the common behavior across all sensors, while each specific sensor is implemented in a subclass. This technique is often used in frameworks to allow developers to define extensions to the existing functionality [11]. On the other hand, this approach of implementing variability and commonality in different files does not scale well. With increasing variability, the amount of subclasses increases as well, creating a complex inheritance tree. Runtime binding always adds a performance penalty, and may also result in runtime errors (null pointers) [11].

13

2.1.3.4 Module Replacement

Description Module replacement is used for selecting between files or subsystem often stored in sibling directories. Depending on the implementation technique this selection can take place during compilation or linking.

Implementation Module replacement is commonly implemented in C++, where an (possibly conditionally) included header file selects the right file to include or the header files have identical names among sibling directories, in which case the selection can be done using the –I flag to select an include directory (compile time) or –L flag to select a path to a linked library.

Advantages & Disadvantages Like inheritance-based mechanisms, module replacement allows the separation of variants but has no runtime overhead since variability is bound during compilation or linking. However, variation points are not easily visible in code (in case the headers have identical names) and may also involve the build system (e.g. to specify –I or –L flags). This problem can be addressed by tool support. Another problem is the handling of defaults (e.g. in case of optional variability) since a module needs to be selected at all time. A dummy module may solve this problem [11].

2.1.3.5 Aspect Orientation

Description Aspect orientation is a technique to address the problem of crosscutting concerns that result in code duplication, scattering or tangling. It uses so-called aspects to localize the code related to these concerns into one code unit. “An aspect is a programming construct that encapsulates the implementation of a crosscutting concern“ [6]. An aspect is then weaved into the rest of the program at specific locations (called joint points) using a process called aspect weaving.

Implementation The implementation typically requires special tool support (e.g. AspectJ) since code weaving is not supported in most programming languages.

Advantages & Disadvantages Similar to module replacement, this technique allows the separation of commonality and variability. Additionally, it provides the means to localize implementation of features which would otherwise be scattered across the code and result in duplicated code. However, this technique can only be used with additional tool support since code weaving is not supported in most programming languages. In contrast to techniques like inheritance or conditional execution which use constructs that every programmer is familiar with, this technique will most likely require a learning phase before it can be applied.

14

2.1.3.6 Frame Technology

Description Frame Technology is a variability mechanism developed by Paul G. Basset [18] that allows to separate those modules that change frequently from those that change less frequently. For example, the implementation of common functionality across all products may not change very often, whereas new features or changes to existing features may be more frequent. If commonality and variability reside in the same file, each change has the potential to break the common code although only the variable code needs to be changed. Using Frame Technology “arbitrary text parts can be managed as variabilities, even syntactically incomplete code such as partial loops or isolated return statements” [19].

Implementation Similar to aspect orientation, the implementation of Frame Technology requires special tool support. A detailed example can be found in [19].

Advantages & Disadvantages Like module replacement or inheritance, this technique allows the separation of common and variable code but unlike other mechanisms, it does not need to respect the programming language syntax when doing so. This technique is not well known and not widely used [11]. It requires special tool support and additional training.

15

3 Android Android is the most successful mobile operating system of all time, currently owning a market share of 84.1% (according to Gartner, May 2016). It is not only the major operating system for smartphones, but also runs on tablets, wearables like smartwatches, televisions and automotive multimedia systems. Clearly, such a platform needs to carefully manage variability. After all, these different kinds of devices greatly differ in terms of their provided functionality, user interface, hardware, etc. In this report, we will focus solely on Android (version 6) for smartphones and tablets, since this is the primary usage of Android.

Figure 5: Android 6 devices and their HW/SW characteristics

Figure 5 depicts the specification for different smartphones and tablets, all running the current Android operating system version six. The obvious variability among these devices is their underlying hardware. They use different CPUs, GPUs, different display technologies with different dimensions, different sensors and cameras. This includes optional variability (like different sensors) and alternative variability (like CPU or GPU). In fact, the underlying hardware is the only way, that a regular smartphone can stand out from the competition. Since the Android app store is open to all Android smartphones, they can all download the same applications. Therefore, any pure software variability can be easily replicated by third party apps. For example, the LG Nexus 5 is the only device whose camera has built-in smile detection. However, a quick search in the Google play store instantly reveals third party apps that enable all other devices to achieve the same functionality.

Asus Nexus 7 cellular (2013)(“asus deb”)

Asus Nexus 7 wifi (2013)(“asus flo”)

HTC Nexus 9(“htc flounder”)

Motorola Nexus 6(“moto shamu”)

Network Technology

GSM / HSPA / LTE No cellular GSM / CDMA / HSPA / LTE

DisplayLED-backlit7.0 inches1200 x 1920 pixels

LED-backlit7.0 inches1200 x 1920 pixels

IPS LCD 8.9 inches1536 x 2048 pixels

AMOLED 5.96 inches1440 x 2560 pixels

No cellular

ChipsetQualcomm Snapdragon S4Pro

Nvidia Tegra K1 Qualcomm Snapdragon 805Qualcomm Snapdragon S4Pro

CPU Quad-core 1.5 Ghz Krait Dual-core 2.3 Ghz Denver Quad-core 2.7 Ghz Krait 450Quad-core 1.5 Ghz Krait

GPU Adreno 320 Adreno 320 Kepler DX1 Adreno 420

Primary camera5 MPGeo-tagging, touch focus, face detection

5 MPGeo-tagging, touch focus, face detection

8 MPGeo-tagging, touch focus, face detection

13 MPGeo-tagging, touch focus, face detection, panorama, HDR

SensorsAccelerometer, gyro, proximity, compass

Accelerometer, gyro, proximity, compass

Accelerometer, gyro, compass

Accelerometer, gyro, proximity, compass, barometer

LG Nexus 5(“ lge hammerhead”)

GSM / CDMA / HSPA / LTE

True HD IPS+4.95 inches1080 x 1920 pixels

Qualcomm MSM8974Snapdragon 805

Quad-core 2.3 Ghz Krait 400

Adreno 330

8 MPGeo-tagging, touch focus, face/smile detection, panorama, HDR

Accelerometer, gyro, proximity, compass, barometer

16

3.1 Android Architecture A look at the Android architecture gives a first clue on how hardware variability is being supported within Android. The topmost layer consists of the application framework containing the standard Android APIs used by app developers to access basic functionality. The Binder IPC proxies enable the inter-process communication between the application framework and the system services which are used by the application framework to access the underlying hardware. The hardware abstraction layer exposes a consistent interface to higher layers while allowing the underlying hardware implementation to change. This is a common variability mechanism at the architectural level that enables the support for cameras, sensors, etc. without having to change any of the upper layers. Android is built on top of the Linux kernel whose device drivers control the hardware. The Android architecture already gives some insight into how this operating system supports hardware variability:

1. Android makes use of HALs that hide all hardware related variability from upper layers by providing a consistent interface. This is the location were hardware vendors can add their custom implementations.

2. Android is built on top of the Linux kernel, which provides its own sophisticated mechanisms for handling variability that have been studied in detail by the product line community. For example, the Linux kernel can be compiled for many different hardware architectures (like x86 or ARM) which allows it to run on tablets, smartphones, TVs, desktop pcs, etc. As we will later see, the Linux kernel is actually not part of the Android source code, but is included as a precompiled binary. Therefore, a lot of the variability is already bound and does not need to be considered by Android.

Figure 6: Overview of the Android architecture [21]

17

3.2 Android from a Product Line Perspective

Table 4: Android source code structure [20]

Directory Content

Abi Minimal C++ Run-Time Type information support

Bionic Android’s custom C library

Bootable OTA, recovery mechanism and reference bootloader

Build Build System

Cts Compatibility Test Suits

Dalvik Dalvik VM

Development Development tools

Device Device-specific files and components

Docs Content of http://source.android.com

External External projects imported into the AOSP

Frameworks Core components such as system services

Hardware HAL and hardware support libraries

Libcore Apache harmony

Libnativehelper Helper functions for use with JNI

Ndk Native development Kit

Out Build output will be placed here

Packages Stock Android apps, providers and IMEs

Pdk Platform Development Kit

Prebuilt Prebuilt binaries, including toolchains

Sdk Software Development Kit

System Embedded Linux platform that houses Android

Tools Various IDE tools

From an external perspective Android can clearly be seen as a Software Product Line. Figure 5 showed some sample products containing commonality and variability that are all running the same Android version. As it turns out, Android also conforms to the model of a Product Line from an internal perspective. Table 4 shows the folder structure of the Android source code, where core assets and application specific assets are clearly separated. The “Device” folder contains product specific assets like custom apps, layout files and other configuration files. The folders highlighted in blue can be considered as core assets. These are Android specific modules, but also other open-source projects that have been integrated into the Android platform. They may contain variability that is resolved during the build process based on the selected configuration. A specific product is derived by building Android with a selected product configuration. The resulting files are placed in the “Out” folder which does not contain any build-time variability any more.

Core Assets Application specific code

18

3.3 Conceptual Architecture of Android

3.3.1 Overview

Conceptually, we decompose Android into three layers (cf. Figure 7). The configuration layer comprises Android’s configuration files that define its configuration space and act as input for product derivation. Android defines different kinds of configurations at different layers of abstraction, namely the product and board configuration. The product derivation process is implemented by Androids build system that resolves variability based on the selected configuration. This can have an effect on the overall build process (global Makefiles) or on individual modules (module Makefiles). The source code is organized in modules that can be hierarchically grouped. Each module contains an Android.mk file that defines its compilation process.

3.3.2 Android Configuration

Table 5: Android Configuration Layers [21]

Layer Example Description

Product myProduct, myProduct_eu

The product layer defines the feature specification of a shipping product such as the modules to build, locales supported, and the configuration for various locales. In other words, this is the name of the overall product. […]

Board/Device Trout, goldfish The device/board layer represents the physical

layer of plastic on the device (i.e. the industrial design of the device). For example, North American devices probably include QWERTY keyboards whereas devices sold in France probably include AZERTY keyboards. […]

Arch Arm, x86, mips, arm64 The architecture layer describes the processor configuration and ABI (Application Binary Interface) running on the board.

Android specifies three different layers of configuration with decreasing abstraction level. We will focus on the first two, the product and board configuration since the architecture configuration is part of the Linux kernel.

Figure 7: Conceptual Architecture of Android. Refinement level 1 (left) and 2 (right)

19

Configuration files in Android are regular Makefiles, although they only make use of a subset of the Make language. They assign values to a set of predefined constants that will be used during the build process to decide which modules to build, how to resolve their variability, etc. Note that there is no tool support for creating a configuration. The recommended approach is to copy an existing configuration and adjust it as necessary.

3.3.2.1 Product Configuration The product configuration is the configuration on the highest level of abstraction. Although it is documented as a “feature specification”, we will see that the abstraction to the level of features is inherently missing. Other product lines like the Linux kernel allow the user to select which features should be included in the current build and then maps this information to a lower level configuration containing which modules to compile, which dependencies to resolve and which parameters to choose. Android on the other hand does not provide any such mapping and puts the burden on the configurator to select modules and to set parameters in such a way that the desired features are included in the current build. Figure 9 shows an example product configuration from a Nexus phone. The configuration mechanisms in the product configuration will be explained in detail in section Product Configuration on page 22.

# Inherit from the common Open Source product configuration $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) PRODUCT_NAME := aosp_shamu PRODUCT_DEVICE := shamu PRODUCT_MODEL := AOSP on Shamu PRODUCT_MANUFACTURER := motorola PRODUCT_PACKAGES += \ Launcher3

PRODUCT_COPY_FILES += \ device/moto/shamu/init.shamu.rc:root/init.shamu.rc PRODUCT_PROPERTY_OVERRIDES += persist.ims.disableDebugLogs=1 PRODUCT_LOCALES += en_GB de_DE es_ES fr_CA

Figure 8: Product configuration excerpt from device/moto/shamu (device.mk)

20

3.3.2.2 Board Configuration According to the Android specification, the board configuration represents the “physical layer of plastic on the device”. This configuration certainly defines more technical characteristics of the device on a lower level of abstraction than the product configuration. Examples are the processor architecture, cpu or wlan device. However, in contrast to the product configuration, the variable names of the board configuration and also their possible values are not defined by the Android specification which makes the configuration a tedious and error prone task. The constants defined in this configuration are referenced by global Makefiles as well as local Makefiles of individual modules and often appear in conditional statements. Therefore, there exists an implicit set of possible values for each constant. Since there is also no tool support, the only option is to manually trace a constant through the build files in order to know their possible values and impact.

3.3.3 Android Build System

Figure 10: Schematic overview of the Android build system

Androids build system is based on GNU Make. But unlike most Make-based build systems, it does not use it recursively (i.e. build each subsystem independently by a recursive Make call). Instead, it relies on file naming conventions and includes all files with a certain name throughout the source code to build a gigantic Makefile during built time. A schematic overview of the build system is depicted in Figure 10. The build system is decomposed in the build configuration (using the “lunch” tool), globally defined Makefiles can control the overall build process (residing in build/core/) and module Makefiles (named “Android.mk”) that define how an individual module is being compiled.

Figure 9: Board configuration excerpt from device/moto/shamu (BoardConfig.mk)

TARGET_CPU_ABI := armeabi-v7a TARGET_CPU_ABI2 := armeabi TARGET_ARCH := arm TARGET_ARCH_VARIANT := armv7-a-neon TARGET_CPU_VARIANT := krait ENABLE_CPUSETS := true TARGET_NO_BOOTLOADER := true BOARD_KERNEL_BASE := 0x00000000 BOARD_KERNEL_PAGESIZE := 2048 BOARD_KERNEL_TAGS_OFFSET := 0x01E00000 BOARD_RAMDISK_OFFSET := 0x02000000 MAX_VIRTUAL_DISPLAY_DIMENSION := 2048 BOARD_EGL_CFG := device/moto/shamu/egl.cfg BOARD_USES_ALSA_AUDIO := true WPA_SUPPLICANT_VERSION := VER_0_8_X BOARD_WLAN_DEVICE := bcmdhd BOARD_WPA_SUPPLICANT_DRIVER := NL80211 BOARD_WPA_SUPPLICANT_PRIVATE_LIB :=

lib_driver_cmd_$(BOARD_WLAN_DEVICE) BOARD_HOSTAPD_DRIVER := NL80211 […..]

21

The Android build process starts by selecting a product configuration to be built using the “lunch” tool. The build process is then initiated by running Make, which executes the globally defined Makefiles in the build/core directory. These Makefiles include (the Make “include” operation is similar to a preprocessor #include) the configuration files of the previously selected configuration and make use of their definitions to resolve variability and adjust the build process accordingly. The Android source code is decomposed into modules. Each module has a file called “Android.mk” that defines how each module should be compiled. These files might also include conditional statements referencing the variables from the product and/or board configuration. The global Makefiles will include all files named “Android.mk” and resolve their variability by evaluating their conditional statements and execute their compilation commands. In total Android 6 contains 2834 modules, each containing a file called Android.mk Figure 11 shows a sample Android.mk file. These files make use of a small subset of the Make language and assign values to a set of predefined constants similar to the product configuration files. After setting the path to the current directory (line 1) and clearing all previously set variables (line 2, this is necessary since these Makefiles are not executed in isolation but included by a global Makefile), the list of source files of the current module is defined (line 3) together with the name of the module (line 4) and any compiler flags that should be added when building the compiler command (line 5). The compiler command is then constructed according to the predefined rules for compiling a static library (line 6).

3.3.4 Source Code

Figure 12: Android source code [22]

The source code of Android is organized into 2834 modules which can be hierarchically nested. According to the analysis conducted by Zhang [22] the majority of Android is implemented in C/C++ code (~120.000 C/C++ files) followed by Java (~50.000 files). With 4475 Makefiles, the build system in Android is highly distributed with each module having their own Makefiles. Overall, it is clear that the Android source code is highly heterogeneous by allowing the use of a variety of different programming languages like Java, C, C++, Python, Go, Javascript, etc.

1. LOCAL_PATH := $(call my-dir) 2. include $(CLEAR_VARS) 3. LOCAL_SRC_FILES := healthd_board_default.cpp 4. LOCAL_MODULE := libhealthd.default 5. LOCAL_CFLAGS := -Werror 6. include $(BUILD_STATIC_LIBRARY)

Figure 11: A simple Android.mk file II. EXAMPLE SYSTEM: ANDROID

In this paper, we selected the Android operation system (i.e., Android 5 and 6) as the example system for analyzing the build dependency structure and further comparison. Considering the complexity of the Android system, we have conducted a preliminary investigation of the Android code base, which actually contains different programming languages. As shown in Fig. 2, the code base of Android 6 mainly contains source files in C/C++, Java, Python, Go, Bash, and JavaScript. There are also other files like makefiles, libraries, packages, configuration files, webpages, and so on. The analysis of the Android build dependency structure should consider all these programming languages and related source files.

Fig. 2. Number of Source Files in Android 6.

III. BUILD DEPENDENCY ANALYSIS

This section introduce the build dependency analysis approach and the case study on the Android 5 and 6. Fig. 3 shows an overview of the Android build dependency analysis. First, build logging and build parsing are conducted to extract the build dependency structures of Android 5 and 6 respectively. Then the extracted build dependencies and related artefacts are compared. These steps are elaborated in the following subsections.

Fig. 3. Overview of the Android Build Dependency Analysis.

A. Build Logging

The goal in this first step is to monitor the build process including all executed build commands (also called the build recipes), their execution time, and their invocation relationships. After investigating different existing techniques and evaluating their applicability, we eventually developed a logging approach

and managed to capture all this information by instrumenting the build process accordingly. As the build process is usually conducted by the GNU Make tool (also in building Android systems), the solution idea is to specify the execution shell of the Make tool as a dedicated Bash shell script, which is actually a wrapper of the Linux Bash. As the Make tool will pass each build command to the Bash script as an argument, the script can not only capture and execute the command but also capture the execution time of the command. As a build command may invoke other commands (via the same shell script), the invocation flow is also captured recursively. Finally, the dynamic build jobs are captured during the build process in a tree structure and saved in an XML log file.

In the Android study, the build logging was conducted as a dynamic analysis during the building of Android 5 and 6 respectively, which resulted in two separate XML files documenting a huge amount of build jobs. It turned out that the build process of Android 6 contains 199466 build jobs. While the captured commands involves all used compilers and linkers (e.g., gcc, g++, clang, clang++), the most executed commands during the Android build process were actually system commands (e.g., echo, mkdir, rm, etc.) that do not produce any build artefacts. The logging results enable further extraction of dependencies between artefacts as well as corresponding measurement, visualization, and comparison, which are described in the following sub-sections.

B. Build Parsing

Given the captured build jobs from build logging, the next step is to extracted build dependencies. Each dependency represents a build job and contains the information of the build command name, input and output artefacts, build duration, and the related makefile. The parsing step is implemented in Python scripts and includes several sub-steps as below.

1) Parsing build commands: The command name as well

as input and output artefacts are extracted by parsing the

executed build command string in the build log. To this end, a

command parser is implemented for parsing these build

commands.

2) Parsing build duration: The duration of each build

dependency indicates the time used for building the output

artefact in the corresponding build job, which is extracted from

the build log.

3) Tracing header fi les: In many cases the executed build

command string only contains a header directory, while the

used header files are not specified. Fortunately, in the C/C++

build jobs (which is the majority in the Android build process)

such header file information can be extracted from the

dependency files (.d files specified by the -MF option in the

C/C++ build commands).

4) Makefi le mapping: Executed build commands are

written in makefiles. In order to complete the build dependency

knowledge, the related makefiles are identified based on

makefile parsing and mapped to the build dependencies.

5) Parsing dependency chains: As depicted in Fig. 1, the

build artefacts are likely to be inter-related (i.e., an artefact is

built as input of another build job). In this case, the build

22

4 Analysis 4.1 Configurability of Android

4.1.1 Qualitative Analysis

4.1.1.1 Configuration mechanisms

Product Configuration Figure 13 depicts an excerpt from the product configuration of a Nexus phone (codename shamu). Notice that the configuration is not created from scratch, but inherited from another configuration (line 1-2). It starts by initializing basic information like the product name, device name, model and manufacturer (line 3-6). The constant PRODUCT_PACKAGES lets you define a list of modules to be build. Using this mechanism, you can also add your own modules (defined in the product-specific device folder) to extend Android. Each module throughout the Android source code contains a module specification in the form of a special Makefile that defines among other things the name of the module. Notice that there is neither a global definition of all modules nor is there any definition of dependencies between modules. PRODUCT_COPY_FILES defines a list of files that are copied during the build process to another location (syntax source:target, line 8). This is a customization mechanism commonly used to define your own layout, look & feel, runtime configurations or other resource based information. Extracting variability from code and putting it in a resource file like an xml or configuration file is good practice since it lets you alter your product without recompilation or touching the code. Using this mechanism, you can for example define your own layout files and copy them during the build process to override the default layout. Not all variability is resolved during build-time. Android makes extensive use of property files which are read at runtime. Values for properties can be set using the PRODUCT_PROPERTY_OVERRIDES constant (line 9). PRODUCT_LOCALES allows to define which languages should be preinstalled (line 10).

# Inherit from the common Open Source product configuration $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) PRODUCT_NAME := aosp_shamu PRODUCT_DEVICE := shamu PRODUCT_MODEL := AOSP on Shamu PRODUCT_MANUFACTURER := motorola PRODUCT_PACKAGES += \ Launcher3

PRODUCT_COPY_FILES += \ device/moto/shamu/init.shamu.rc:root/init.shamu.rc PRODUCT_PROPERTY_OVERRIDES += persist.ims.disableDebugLogs=1 PRODUCT_LOCALES += en_GB de_DE es_ES fr_CA

1 2 3 4 5 6 7 8 9 10

Figure 13: Product configuration excerpt from device/moto/shamu (device.mk)

23

Board Configuration As explained in section Board Configuration on page 20, the Board Configuration has no defined configuration mechanisms. Neither the possible configuration constants, nor their possible values or their semantics are defined in any way.

4.1.2 Quantitative Analysis

4.1.2.1 Configuration Options To get an impression on what is actually configurable within Android, we analyzed how many configuration options exist. Specifically, we studied the board configurations available in Android 6 since most of the variability implementation is hardware-related. The data extraction was performed by building a simple Makefile parser that is able to parse Androids board configurations and extract the (constant, value) pairs. In this step we ignored any conditional statements (which rarely occur), but did include the constants in their body in our result. By parsing all available board configurations and merging the results we know how many different values where used for each constant. Although we cannot claim to get a complete result with this approach, it is a first approximation and the best we can do with the data that is publicly available. The resulting data in visualized in Figure 14 in form of a histogram. We analyzed all available 26 board configuration files of Android 6 and obtained 158 unique constants. The y-axis shows the amount of configuration constants and the x-axis depicts the frequency of the amount of different values used. This result clearly shows that only a small fraction of all configuration constants actually has more than one possible value among all available configuration files. 113 constants only have a single value (71%) while only 20 configuration constants (13%) have 5 or more values and can be regarded as real variability. The natural questions that follows is: What do the configuration constants define that are highly variable? To party answer this question we also included Figure 15 displaying the name of each configuration constant. Obviously, extensive domain knowledge is required to fully answer this questions but we can make some observations from the constant names.

Figure 14: Possible configuration options for board configuration constants. The results were obtained by analyzing all

available 26 board configuration for Android 6 release 1.

24

Figure 15: The amount of distinct values obtained for the configuration constants in Android 6 board configuration files. The

figure only shows those configuration constants that have at least two different values among all configuration files.

From the names of configuration constants in Figure 15 we can draw the following conclusions: Fist, several of those constants that are highly variable depict partition sizes or block sizes with numbers (*_PARTITION_SIZE, *_BLOCK_SIZE). Second, some of these configuration constants are used for implementing module replacement and therefore specify include directories (e.g. BOARD_SEPOLICY_DIRS or *_BDROID_BUILDCFG_INCLUDE_DIR). Third, many constants refer to the CPU or architecture of the target device (TARGET_ARCH_VARIANT, TARGET_CPU_ABI, TARGET_ARCH, TARGET_2ND_ARCH_VARIANT). Thus, this seems to be one of the major source of variability among the different board configurations. Looking at the naming pattern of constants, it seems like at one point a naming pattern was chosen (e.g. prefix TARGET_* for specifying properties of the target device or BOARD_* for encoding that this constant is defined in the board configuration). But through the evolution of the system the configuration files got cluttered with new constants that did not follow this rule (e.g. OVERRIDE_RS_DRIVER or MAX_EGL_CACHE_SIZE) which makes any automated analysis were difficult.

4.1.2.2 Configuration Impact The logical next question to ask is:

1. Which of these configuration constants has the highest impact? 2. Where do these configuration constants have an impact? I.e. is variability resolved in

global Makefiles or locally in individual modules? To answer these questions, we searched all of Androids Makefiles for the names of the board configuration constants. Obviously, a true analysis of Makefiles (including scanning and parsing) is not feasible within this project. Therefore, we rely on a purely textual search here making use of regular expressions based search programs like grep. In this analysis, we differentiate between three locations within Android. First, the device folder which contains (besides the configuration) vendor-specific modules and apps. Second, the build folder which contains global build files that direct the overall build process and define reusable constants and functions. Any other Makefile must belong to ordinary Android modules which form our third category. By making this distinction, we can infer whether constants have a global impact (if they are referenced mostly in global build files) or whether

25

the impact is distributed among multiple modules (if they are mostly referenced in local module-specific build files). The result of our analysis is depicted in Figure 16 in a stacked area plot. The total amount of area colored for a specific constant is the total amount of references of this constant. This number is then broken down into the individual categories DEVICE, BUILD and MODULE by using different colors.

Figure 16: Textual References of board configuration constants in Androids build files. The figure distinguishes between build files in the device-folder (DEVICE) which belong to vendor-specific modules, build files in the build-folder (BUILD) and files in

individual Android modules (MODULE). Only the 30 most references constants are shown.

The target architecture (TARGET_ARCH) is the most referenced board configuration constant with almost 400 references. It is being referenced in device-specific Makefiles, global Makefiles but mostly in the Makefiles of individual Modules. Followed by the target board platform (TARGET_BOARD_PLATFORM) which is referenced about 200 times. Interestingly, this constant is only referenced in device-specific modules (DEVICE) and local Android modules (MODULE) and not in global Makefiles. With about 100 references, the third most referenced constant is the second target architecture (TARGET_2ND_ARCH) which has been introduced for 64-bit Android builds and is referenced mostly in global Makefiles. From this analysis, we can draw the following conclusions:

1. Only a small set of constants have a large amount of references throughout the Android build system while the great majority only have very few references. The amount of references follows a power law distribution, a phenomenon that also describes the number of links on the internet, file sizes, etc.

2. The target architecture and board platform are by far the constants with the highest impact, mostly on individual modules.

3. The majority of references are in device-specific or regular Android modules (~70% of all references). Therefore, the architecture and board platform variability in Android has be to resolved in individual modules.

26

4.2 Variability Mechanisms in Configuration / Build System / Source Code

4.2.1 Qualitative Analysis

4.2.1.1 Overview

Conditional Compilation

Conditional Execution

Module Replacement

Inheritance

Configuration X X

Build System X X X

Source Code X X X X

4.2.1.2 Conditional Compilation

Build System Typically, conditional compilation uses a preprocessor to insert or remove code parts before the compilation process. Android does not use a preprocessor for Makefiles. However, they employ conditional execution to control which source files will be compiled. Figure 17 depicts an example for the use of conditional compilation in Makefiles. The variable TARGET_ARCH (line 1) refers to the target architecture which is defined in Androids board configuration. Based on the target architecture, this Makefile assigns which source files gets compiled (by assignment to the LOCAL_SRC_FILES variable). Therefore, Android employs conditional compilation to implement file-level variability within its modules.

Mechanism

Layer

1. ifeq ($(TARGET_ARCH),arm)

2. LOCAL_SRC_FILES += \

3. src/asm/pvmp3_polyphase_filter_window_gcc.s \

4. src/asm/pvmp3_mdct_18_gcc.s \

5. src/asm/pvmp3_dct_9_gcc.s \

6. src/asm/pvmp3_dct_16_gcc.s

7. else

8. LOCAL_SRC_FILES += \

9. src/pvmp3_polyphase_filter_window.cpp \

10. src/pvmp3_mdct_18.cpp \

11. src/pvmp3_dct_9.cpp \

12. src/pvmp3_dct_16.cpp

13. endif

Figure 17: Example for the use of conditional compilation in Makefiles on a file level. Example taken from frameworks/av/media/libstagefright/codecs/mp3dec/Android.mk

27

Source Code

Example 1 Conditional compilation is probably the most used technique for implementing small grained variability in C and C++ code. It uses the preprocessor to insert or remove parts of the source code before compilation. What is remarkable about Android is that the usage of conditional compilation in the source code is not localized to the source code level but crosscutting through build system and configuration. You would expect to find a header file with the definition of preprocessor flags, but Android uses a different technique. Since the configuration layer is implemented in Makefiles, the source code cannot directly access it to derive values for preprocessor constants. Therefore, the build system acts as a translation layer to translate between the configuration and the implementation technique in the source code. Figure 18 depicts an example for such a case. In the board configuration, a constant called BOARD_CHARGER_ENABLE_SUSPEND is defined and set to true which controls whether the charger is allowed to go into a suspend state. In order to implement this variability using conditional compilation in the source code, the preprocessor flag

ifeq ($(strip $(BOARD_CHARGER_DISABLE_INIT_BLANK)),true) LOCAL_CFLAGS += -DCHARGER_DISABLE_INIT_BLANK

endif ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true) LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND

endif

System/Core/healthd/Android.mk

BOARD_CHARGER_DISABLE_INIT_BLANK:=true

# let charger mode enter suspend

BOARD_CHARGER_ENABLE_SUSPEND := true

Device/htc/flounder/BoardConfig.mk Configuration

Build System

#ifdef CHARGER_ENABLE_SUSPEND

static int request_suspend(bool enable) { if (enable) return autosuspend_enable(); else

return autosuspend_disable(); } #else

static int request_suspend(bool /*enable*/) { return 0; } #endif

System/core/healthd/healthd_mode_charger.cpp Source Code

Figure 18: Example for the use of conditional compilation in source code. Example taken from System/core/healthd/healthd_mode_charger.cpp

28

CHARGER_ENABLE_SUSPEND is conditionally added through the build system by defining a compiler flag (the flag “-D” is equivalent to #define in a source file). The preprocessor then chooses the right implementation for the method request_suspend based on if the preprocessor constant CHARGER_ENABLE_SUSPEND is defined. Therefore, Android employs conditional compilation to implement small grained variability based on the configuration files by conditionally introducing preprocessor flags via the module Makefiles.

Example 2

Figure 19 depicts an example for the usage of conditional compilation to implement (module-) local variability. While in the previous example variability has been imposed by Android’s configuration, in this example the module is being configured locally independent of Android’s configuration. The file Android.config defines a high level configuration where each feature can be turned on or off by assigning “y” or “n” to its variable. In contrast to Android’s variability management where the abstraction to the level of features is inherently missing and any semantics of configuration constants needs to be reverse-engineered, this module allows a high level configuration where each feature is explicitly documented. The dependencies between features and the mapping to the feature implementation is implemented in the Android.mk file. For example, the feature CONFIG_P2P is selected in the Android.config file for the current configuration. The Makefile Android.mk conditionally adds source files for compilation based on the feature selection. This example shows that individual modules like wpa_supplicant_8 (Figure 19) can have a more systematic way of handling variability than the overall Android codebase. This is due to the fact that Android uses a lot of external open source software in its codebase. The example shown is clearly itself a product line that has been integrated into the Android code base as a core asset.

1. include $(LOCAL_PATH)/android.config

2. ifdef CONFIG_P2P

3. OBJS += src/p2p/p2p_invitation.c

4. OBJS += src/p2p/p2p_dev_disc.c

5. OBJS += src/p2p/p2p_group.c

6. OBJS += src/ap/p2p_hostapd.c

7. OBJS += src/utils/bitfield.c

8. L_CFLAGS += -DCONFIG_P2P

9. NEED_GAS=y

10. NEED_OFFCHANNEL=y

11. CONFIG_WPS=y

12. CONFIG_AP=y

13. ifdef CONFIG_P2P_STRICT

14. L_CFLAGS += -DCONFIG_P2P_STRICT

15. endif 16. endif

[…] # P2P (Wi-Fi Direct) # This can be used to enable P2P support in # wpa_supplicant. See README-P2P for # more information on P2P operations. CONFIG_P2P=y

[…]

<<includes>>

Android.mk

Android.config

Figure 19: Example for the use of conditional compilation in the source code to implement (module-) local variability. Example taken from external/wpa_supplicant_8/wpa_supplicant/

29

4.2.1.3 Conditional Execution

Configuration

Example 1 Figure 20 depicts an example for the usage of conditional execution in Androids configuration file (specifically the board configuration). This example depicts the usage of dex-preoptimization, which is an optimization that enables a fast boot time for Android OS and Android apps at the cost of additional memory usage. This feature is only available if Android is built on a Linux machine and the target build variant is set to “user” (which is a release build, an alternative build variant would be “debug”). This configuration constraint is implemented in the board configuration using the conditional execution mechanism of Makefiles. Therefore, Android uses conditional execution at the configuration level to implement dependencies between configuration items (TARGET_BUILD_VARIANT and WITH_DEXPREOPT) and between the build environment (HOST_OS).

Example 2 The second example depicts a different usage of conditional execution in Androids configuration layer. Androids configuration is not implemented in a single configuration file, but Android divides its configuration into two layers, namely the product configuration and board configuration. The example in Figure 21 shows how Android uses conditional execution to implement dependencies among these layers. There exists a variant of the LG Nexus 5 device (codename “Hammerhead”) that has a fingerprint sensor. Android handles this variability by defining a specialized configuration for this device which inherits from the configuration of the basic device and adds the fingerprint functionality. However, both devices share the same board configuration. Therefore, any fingerprint related configuration constants in the board configuration need to be tied to the fingerprint product configuration. Android implements this constraint using conditional execution by comparing the name of the chosen target product (Figure 21, line 1). Therefore, Android uses conditional execution in its configuration files to implement dependencies between its different layers of configuration (i.e. board and product configuration).

Figure 20: Example for conditional execution in Androids configuration files to implement variability among configuration constants and the build environment. Example taken from the board configuration of a Nexus device (Device/moto/shamu/BoardConfig.mk)

1. # Enable dex-preoptimization to speed up first boot sequence

2. ifeq ($(HOST_OS),linux) 3. ifeq ($(TARGET_BUILD_VARIANT),user) 4. ifeq ($(WITH_DEXPREOPT),) 5. WITH_DEXPREOPT := true

6. endif 7. endif 8. endif

1. ifneq ($(filter hammerhead_fp aosp_hammerhead_fp,$(TARGET_PRODUCT)),) 2. BOARD_HAS_FINGERPRINT_FPC := true

3. Endif

Figure 21: Example for conditional execution in Androids configuration files to connect different configuration layers. Example taken from Device/Ige/hammerhead/BoardConfig.mk

30

Build System We have already seen multiple examples of conditional execution in Android’s build system in previous sections. For example, Figure 18 shows how conditional execution is used to connect the configuration with the variability implementation technique (in this example conditional compilation) by introducing preprocessor constants based on the value of the configuration constant BOARD_CHARGER_DISABLE_INIT_BLANK.

Source Code Figure 22 shows an example for the usage of conditional execution in source code for implementing variability. The function isOffloadSupported (line 4) refers to a feature called hardware-offloaded audio processing. This term describes the possibility of performing the main audio processing tasks outside the computer’s main CPU. Whether this feature is turned on or off is defined in a property file which is loaded at runtime and accessed using the key “audio.offload.disable” (line 9). The value of this entry is used in conditional statements (line 10) and affects the functions behavior. While we have seen the implementation of build-time variability (i.e. variability which gets bound during the build process) using conditional compilation a lot, Android makes use of conditional execution for implementing runtime variability via property/policy files in source code.

4.2.1.4 Module Replacement

Build System While module replacement is mostly associated with include directories in C/C++ files it can also be used in the build system for conditionally including Makefiles. Figure 23 shows an example for such a case in Android 6. The content of the top-level Android.mk Makefile for the module GPS is shown. GPS has several submodules called msm8960, msm8974, msm8084 that implement different GPS sensors for different board platforms. The compilation process

1. // This function checks for the parameters which can be offloaded. 2. // This can be enhanced depending on the capability of the DSP and policy 3. // of the system. 4. bool AudioPolicyManagerBase::isOffloadSupported(const audio_offload_info_t& offloadInfo) 5. { 6. […] 7. // Check if offload has been disabled 8. char propValue[PROPERTY_VALUE_MAX]; 9. if (property_get("audio.offload.disable", propValue, "0")) { 10. if (atoi(propValue) != 0) { 11. ALOGV("offload disabled by audio.offload.disable=%s", propValue ); 12. return false; 13. […] 14. }

Figure 22: Example for the usage of conditional execution in source code to implement variability with respect to a configuration/policy file

31

is being directed to the correct GPS submodule based on the chosen target board platform (conditional statements in line 3,6,9) by including all Makefiles of the submodule (include statement in line 5,8,11). The variable TARGET_BOARD_PLATFORM has been defined in the board configuration and describes the chosen board platform. Interestingly, there are further constraints that might prevent the GPS module from building any of its submodules. Namely, the variable BOARD_VENDOR_QCOM_GPS_LOC_API_HARDWARE

must not be empty and the variable BOARD_VENDOR_QCOM_LOC_PDF_FEATURE_SET must be set to true. These constraints are not visible at the configuration level where they are defined and make the configuration process for a new device an error prone and tedious task.

Source Code The first thing to notice about the module replacement example in Figure 24 is that this variability implementation mechanisms cuts across all layers similar to the example for conditional compilation in Figure 18. The example is concerned with Androids Bluetooth module (system/bt) which uses a configuration file in form of a c-code header file that differs among different products. This variability is resolved by means of module replacement.

1. ifneq ($(BOARD_VENDOR_QCOM_GPS_LOC_API_HARDWARE),) 2. ifeq ($(BOARD_VENDOR_QCOM_LOC_PDK_FEATURE_SET),true) 3. ifneq ($(filter msm8960 apq8064 ,$(TARGET_BOARD_PLATFORM)),) 4. #For msm8960/apq8064 targets 5. include $(call all-named-subdir-makefiles,msm8960) 6. else ifneq ($(filter msm8974 ,$(TARGET_BOARD_PLATFORM)),) 7. #For msm8974 target 8. include $(call all-named-subdir-makefiles,msm8974) 9. else ifneq ($(filter msm8084 ,$(TARGET_BOARD_PLATFORM)),) 10. #For msm8084 target 11. include $(call all-named-subdir-makefiles,msm8084) 12. endif #TARGET_BOARD_PLATFORM 13. endif #BOARD_VENDOR_QCOM_LOC_PDK_FEATURE_SET 14. endif #BOARD_VENDOR_QCOM_GPS_LOC_API_HARDWARE

Figure 23: Example for the usage of module replacement in Makefiles for selecting submodules for compilation. Example taken from Hardware/qcom/gps/Android.mk

ifeq ($(TARGET_PRODUCT),bt_shamu) BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/moto/shamu/bluetooth_extra else BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR := device/moto/shamu/bluetooth endif

# Setup bdroid local make variables for handling configuration

ifneq ($(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR),) bdroid_C_INCLUDES := $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR) bdroid_CFLAGS += -DHAS_BDROID_BUILDCFG

endif

#ifdef HAS_BDROID_BUILDCFG

#include "bdroid_buildcfg.h"

#endif

Configuration

Build System

Source Code

Device/moto/shamu/BoardConfig.mk

System/bt/Android.mk

System/bt/hci/include/bt_hci_bdroid.h

Figure 24: Example for the usage of module replacement in source code for implementing variability wrt. the chosen target product.

32

The include-directory is conditionally defined at the configuration layer (variable BOARD_BLUETHOOTH_BDROID_BUILDCFG_INCLUDE_DIR) based on the chosen target product. The content of this variable is then specified as an include directory in the build system by assigning it to bdroid_C_INCLUDES. This will later generate a compiler command using the –I flag for defining an include directory. Additionally, a preprocessor constant HAS_BDROID_BUILDCFG is introduced through the build system that indicated whether an include-directory has been defined at all. This way, module replacement can also be used to implement optional variability opposed to just alternative variability. Based on this variable, the source code includes the header file “bdroid_buildcfg.h” which resides in the include-directory introduced via the build system. Interestingly, while in the previous example in Figure 23 we highlighted the inappropriate implementation of constraints on the module level which make configuration a difficult task, in this example the issue is resolved by shifting the variability to the configuration level where it belongs.

4.2.1.5 Inheritance

Configuration

Androids configuration layer makes great use of inheritance to reduce the complexity of configuration files. Figure 25 depicts the inheritance tree of Androids product configuration that are part of the Android 6 source code. The leaf nodes at the bottom represent final configuration files whereas all other nodes represent intermediate configuration files. Most of these intermediate files are provided by Android to support vendors in configuring Android. However, vendors also make use of this inheritance mechanism for their own purpose and use it to encode commonality and separate variability. For example, the two nodes highlighted in red represent the product configuration for HTC Nexus 9 as a 32-bit

frameworks/native/build/tablet-dalvik-heap.mk

build/target/board/generic_arm64/device.mk

build/target/product/aosp_arm64.mk

AOSP on ARM arm64 Emulatorgeneric_arm64

build/target/product/sdk_phone_arm64.mk

Android SDK built for arm64generic_arm64

device/generic/qemu/ranchu_arm64.mk

AOSP on qemu arm64 emulatorgeneric_arm64

build/target/product/full.mk

AOSP on ARM Emulatorgeneric

build/target/product/aosp_arm.mk

AOSP on ARM Emulatorgeneric

build/target/product/aosp_base_telephony.mk

build/target/product/full_mips.mk

AOSP on MIPS Emulatorgeneric_mips

build/target/product/full_mips64.mk

build/target/product/full_x86.mk

AOSP on IA Emulatorgeneric_x86

build/target/product/full_x86_64.mk

AOSP on IA x86_64 Emulatorgeneric_x86_64

device/lge/hammerhead/full_hammerhead.mk

AOSP on HammerHeadhammerhead

device/moto/shamu/aosp_shamu.mk

AOSP on Shamushamu

build/target/product/core_64_bit.mk

build/target/product/sdk_phone_mips64.mk

Android SDK built for mips64generic_mips64

build/target/product/sdk_phone_x86_64.mk

Android SDK built for x86_64generic_x86_64

device/htc/flounder/product.mk

build/target/product/full_base.mk

build/target/product/aosp_base.mk

build/target/product/full_base_telephony.mk

device/asus/deb/aosp_deb.mk

AOSP on Debdeb

device/asus/flo/aosp_flo.mk

AOSP on Floflo

device/htc/flounder/aosp_flounder.mk

AOSP on Flounderflounder

device/htc/flounder/aosp_flounder_64_only.mk

AOSP on Flounder 64-bit onlyflounder

build/target/product/aosp_mips.mk

AOSP on MIPS Emulatorgeneric_mips

build/target/product/aosp_mips64.mk

AOSP on MIPS64 Emulatorgeneric_mips64

build/target/product/aosp_x86.mk

AOSP on IA Emulatorgeneric_x86

build/target/product/aosp_x86_64.mk

AOSP on IA x86_64 Emulatorgeneric_x86_64

build/target/product/embedded.mk

build/target/product/base.mk

build/target/product/core_minimal.mk

build/target/product/core_base.mk

build/target/product/core.mk

generic

device/generic/armv7-a-neon/mini_common.mk

build/target/product/generic_no_telephony.mk

build/target/product/sdk_base.mk

device/google/atv/products/atv_base.mk

build/target/product/runtime_libart.mk

build/target/board/generic/device.mk

build/target/product/generic.mk

generic

build/target/product/generic_mips.mk

generic_mips

build/target/product/generic_x86.mk

generic_x86

build/target/product/locales_full.mk

external/svox/pico/lang/all_pico_languages.mk

frameworks/base/data/sounds/AllAudio.mk

build/target/product/telephony.mk

build/target/board/generic_mips/device.mk

build/target/board/generic_mips64/device.mk

build/target/board/generic_x86/device.mk

build/target/board/generic_x86_64/device.mk

external/google-fonts/carrois-gothic-sc/fonts.mk

external/google-fonts/coming-soon/fonts.mk

external/google-fonts/cutive-mono/fonts.mk

external/google-fonts/dancing-script/fonts.mk

external/hyphenation-patterns/patterns.mk

external/noto-fonts/fonts.mk

external/roboto-fonts/fonts.mk

frameworks/base/data/fonts/fonts.mk

frameworks/base/data/keyboards/keyboards.mk

build/target/product/languages_full.mk

build/target/product/sdk_phone_armv7.mk

generic

build/target/product/sdk.mk

generic

device/sample/products/sample_addon.mk

generic

build/target/product/sdk_arm64.mk

Android SDK built for arm64generic_arm64

build/target/product/sdk_phone_mips.mk

Android SDK for Mipsgeneric_mips

build/target/product/sdk_phone_x86.mk

Android SDK built for x86generic_x86

build/target/product/sdk_mips.mk

Android SDK for Mipsgeneric_mips

build/target/product/sdk_x86.mk

Android SDK built for x86generic_x86

build/target/product/sdk_x86_64.mk

Android SDK built for x86_64generic_x86_64

device/asus/deb/device.mk

device/asus/flo/device-common.mk

device/asus/flo/device.mk

device/asus/flo/full_flo.mk

AOSP on Floflo

frameworks/native/build/tablet-7in-xhdpi-2048-dalvik-heap.mk

hardware/qcom/msm8960/msm8960.mk

device/asus/fugu/full_fugu.mk

fugufugu

device/asus/fugu/aosp_fugu.mk

fugufugu

frameworks/native/build/tablet-10in-xhdpi-2048-dalvik-heap.mk

device/asus/fugu/device.mk

device/htc/flounder/device.mk

hardware/broadcom/wlan/bcmdhd/firmware/bcm4354/device-bcm.mk

device/generic/arm64/mini_arm64.mk

Mini for arm64arm64

device/generic/armv7-a-neon/mini_armv7a_neon.mk

Mini for armv7-a-neonarmv7-a-neon

device/generic/mips/mini_mips.mk

Mini for mipsmips

device/generic/x86/mini_x86.mk

Mini for x86x86

device/generic/x86_64/mini_x86_64.mk

Mini for x86_64x86_64

device/generic/mini-emulator-arm64/mini_emulator_arm64.mk

mini-emulator-armv7-a-neonmini-emulator-armv7-a-neon

device/generic/mini-emulator-armv7-a-neon/m_e_arm.mk

mini-emulator-armv7-a-neonmini-emulator-armv7-a-neon

frameworks/base/data/sounds/AudioPackage5.mk

device/generic/mini-emulator-armv7-a-neon/mini_emulator_common.mk

device/generic/mini-emulator-mips/mini_emulator_mips.mk

mini-emulator-mipsmini-emulator-mips

device/generic/mini-emulator-x86/mini_emulator_x86.mk

mini-emulator-x86mini-emulator-x86

device/generic/mini-emulator-x86_64/mini_emulator_x86_64.mk

mini-emulator-x86_64mini-emulator-x86_64

device/generic/qemu/qemu_base.mk

device/generic/qemu/qemu_arm.mk

Minimal Android for QEMU/Armgeneric

device/generic/qemu/qemu_arm64.mk

Minimal Android for QEMU/ARM64generic_arm64

device/generic/qemu/qemu_mips.mk

Minimal Android for QEMU/MIPSgeneric_mips

device/generic/qemu/qemu_mips64.mk

Minimal Android for QEMU/MIPS64generic_mips64

device/generic/qemu/qemu_x86.mk

Minimal Android for QEMU/x86generic_x86

device/generic/qemu/qemu_x86_64.mk

Minimal Android for QEMU/x86_64generic_x86_64

device/htc/flounder/aosp_flounder32.mk

32-bit AOSP on Flounderflounder32

device/htc/flounder/aosp_flounder64.mk

AOSP on Flounderflounder

device/htc/flounder/device-lte.mk

device/htc/flounder/product_64_only.mk

build/target/product/verity.mk

device/moto/shamu/device.mk

device/lge/hammerhead/aosp_hammerhead.mk

AOSP on HammerHeadhammerhead

device/lge/hammerhead/aosp_hammerhead_fp.mk

AOSP on HammerHeadhammerhead

device/lge/hammerhead/car_hammerhead.mk

Car Hammerheadhammerhead

frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk

device/lge/hammerhead/device.mk

hardware/broadcom/wlan/bcmdhd/firmware/bcm4339/device-bcm.mk

hardware/qcom/msm8x74/msm8x74.mk

device/moto/shamu/bt_shamu.mk

BT Shamushamu

hardware/broadcom/wlan/bcmdhd/firmware/bcm4356/device-bcm.mk

hardware/qcom/msm8x84/msm8x84.mk

external/svox/pico/lang/PicoLangDeDeInSystem.mk

external/svox/pico/lang/PicoLangEnGBInSystem.mk

external/svox/pico/lang/PicoLangEnUsInSystem.mk

external/svox/pico/lang/PicoLangEsEsInSystem.mk

external/svox/pico/lang/PicoLangFrFrInSystem.mk

external/svox/pico/lang/PicoLangItItInSystem.mk

frameworks/native/build/tablet-dalvik-heap.mk

build/target/board/generic_arm64/device.mk

build/target/product/aosp_arm64.mk

AOSP on ARM arm64 Emulatorgeneric_arm64

build/target/product/sdk_phone_arm64.mk

Android SDK built for arm64generic_arm64

device/generic/qemu/ranchu_arm64.mk

AOSP on qemu arm64 emulatorgeneric_arm64

build/target/product/full.mk

AOSP on ARM Emulatorgeneric

build/target/product/aosp_arm.mk

AOSP on ARM Emulatorgeneric

build/target/product/aosp_base_telephony.mk

build/target/product/full_mips.mk

AOSP on MIPS Emulatorgeneric_mips

build/target/product/full_mips64.mk

build/target/product/full_x86.mk

AOSP on IA Emulatorgeneric_x86

build/target/product/full_x86_64.mk

AOSP on IA x86_64 Emulatorgeneric_x86_64

device/lge/hammerhead/full_hammerhead.mk

AOSP on HammerHeadhammerhead

device/moto/shamu/aosp_shamu.mk

AOSP on Shamushamu

build/target/product/core_64_bit.mk

build/target/product/sdk_phone_mips64.mk

Android SDK built for mips64generic_mips64

build/target/product/sdk_phone_x86_64.mk

Android SDK built for x86_64generic_x86_64

device/htc/flounder/product.mk

build/target/product/full_base.mk

build/target/product/aosp_base.mk

build/target/product/full_base_telephony.mk

device/asus/deb/aosp_deb.mk

AOSP on Debdeb

device/asus/flo/aosp_flo.mk

AOSP on Floflo

device/htc/flounder/aosp_flounder.mk

AOSP on Flounderflounder

device/htc/flounder/aosp_flounder_64_only.mk

AOSP on Flounder 64-bit onlyflounder

build/target/product/aosp_mips.mk

AOSP on MIPS Emulatorgeneric_mips

build/target/product/aosp_mips64.mk

AOSP on MIPS64 Emulatorgeneric_mips64

build/target/product/aosp_x86.mk

AOSP on IA Emulatorgeneric_x86

build/target/product/aosp_x86_64.mk

AOSP on IA x86_64 Emulatorgeneric_x86_64

build/target/product/embedded.mk

build/target/product/base.mk

build/target/product/core_minimal.mk

build/target/product/core_base.mk

build/target/product/core.mk

generic

device/generic/armv7-a-neon/mini_common.mk

build/target/product/generic_no_telephony.mk

build/target/product/sdk_base.mk

device/google/atv/products/atv_base.mk

build/target/product/runtime_libart.mk

build/target/board/generic/device.mk

build/target/product/generic.mk

generic

build/target/product/generic_mips.mk

generic_mips

build/target/product/generic_x86.mk

generic_x86

build/target/product/locales_full.mk

external/svox/pico/lang/all_pico_languages.mk

frameworks/base/data/sounds/AllAudio.mk

build/target/product/telephony.mk

build/target/board/generic_mips/device.mk

build/target/board/generic_mips64/device.mk

build/target/board/generic_x86/device.mk

build/target/board/generic_x86_64/device.mk

external/google-fonts/carrois-gothic-sc/fonts.mk

external/google-fonts/coming-soon/fonts.mk

external/google-fonts/cutive-mono/fonts.mk

external/google-fonts/dancing-script/fonts.mk

external/hyphenation-patterns/patterns.mk

external/noto-fonts/fonts.mk

external/roboto-fonts/fonts.mk

frameworks/base/data/fonts/fonts.mk

frameworks/base/data/keyboards/keyboards.mk

build/target/product/languages_full.mk

build/target/product/sdk_phone_armv7.mk

generic

build/target/product/sdk.mk

generic

device/sample/products/sample_addon.mk

generic

build/target/product/sdk_arm64.mk

Android SDK built for arm64generic_arm64

build/target/product/sdk_phone_mips.mk

Android SDK for Mipsgeneric_mips

build/target/product/sdk_phone_x86.mk

Android SDK built for x86generic_x86

build/target/product/sdk_mips.mk

Android SDK for Mipsgeneric_mips

build/target/product/sdk_x86.mk

Android SDK built for x86generic_x86

build/target/product/sdk_x86_64.mk

Android SDK built for x86_64generic_x86_64

device/asus/deb/device.mk

device/asus/flo/device-common.mk

device/asus/flo/device.mk

device/asus/flo/full_flo.mk

AOSP on Floflo

frameworks/native/build/tablet-7in-xhdpi-2048-dalvik-heap.mk

hardware/qcom/msm8960/msm8960.mk

device/asus/fugu/full_fugu.mk

fugufugu

device/asus/fugu/aosp_fugu.mk

fugufugu

frameworks/native/build/tablet-10in-xhdpi-2048-dalvik-heap.mk

device/asus/fugu/device.mk

device/htc/flounder/device.mk

hardware/broadcom/wlan/bcmdhd/firmware/bcm4354/device-bcm.mk

device/generic/arm64/mini_arm64.mk

Mini for arm64arm64

device/generic/armv7-a-neon/mini_armv7a_neon.mk

Mini for armv7-a-neonarmv7-a-neon

device/generic/mips/mini_mips.mk

Mini for mipsmips

device/generic/x86/mini_x86.mk

Mini for x86x86

device/generic/x86_64/mini_x86_64.mk

Mini for x86_64x86_64

device/generic/mini-emulator-arm64/mini_emulator_arm64.mk

mini-emulator-armv7-a-neonmini-emulator-armv7-a-neon

device/generic/mini-emulator-armv7-a-neon/m_e_arm.mk

mini-emulator-armv7-a-neonmini-emulator-armv7-a-neon

frameworks/base/data/sounds/AudioPackage5.mk

device/generic/mini-emulator-armv7-a-neon/mini_emulator_common.mk

device/generic/mini-emulator-mips/mini_emulator_mips.mk

mini-emulator-mipsmini-emulator-mips

device/generic/mini-emulator-x86/mini_emulator_x86.mk

mini-emulator-x86mini-emulator-x86

device/generic/mini-emulator-x86_64/mini_emulator_x86_64.mk

mini-emulator-x86_64mini-emulator-x86_64

device/generic/qemu/qemu_base.mk

device/generic/qemu/qemu_arm.mk

Minimal Android for QEMU/Armgeneric

device/generic/qemu/qemu_arm64.mk

Minimal Android for QEMU/ARM64generic_arm64

device/generic/qemu/qemu_mips.mk

Minimal Android for QEMU/MIPSgeneric_mips

device/generic/qemu/qemu_mips64.mk

Minimal Android for QEMU/MIPS64generic_mips64

device/generic/qemu/qemu_x86.mk

Minimal Android for QEMU/x86generic_x86

device/generic/qemu/qemu_x86_64.mk

Minimal Android for QEMU/x86_64generic_x86_64

device/htc/flounder/aosp_flounder32.mk

32-bit AOSP on Flounderflounder32

device/htc/flounder/aosp_flounder64.mk

AOSP on Flounderflounder

device/htc/flounder/device-lte.mk

device/htc/flounder/product_64_only.mk

build/target/product/verity.mk

device/moto/shamu/device.mk

device/lge/hammerhead/aosp_hammerhead.mk

AOSP on HammerHeadhammerhead

device/lge/hammerhead/aosp_hammerhead_fp.mk

AOSP on HammerHeadhammerhead

device/lge/hammerhead/car_hammerhead.mk

Car Hammerheadhammerhead

frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk

device/lge/hammerhead/device.mk

hardware/broadcom/wlan/bcmdhd/firmware/bcm4339/device-bcm.mk

hardware/qcom/msm8x74/msm8x74.mk

device/moto/shamu/bt_shamu.mk

BT Shamushamu

hardware/broadcom/wlan/bcmdhd/firmware/bcm4356/device-bcm.mk

hardware/qcom/msm8x84/msm8x84.mk

external/svox/pico/lang/PicoLangDeDeInSystem.mk

external/svox/pico/lang/PicoLangEnGBInSystem.mk

external/svox/pico/lang/PicoLangEnUsInSystem.mk

external/svox/pico/lang/PicoLangEsEsInSystem.mk

external/svox/pico/lang/PicoLangFrFrInSystem.mk

external/svox/pico/lang/PicoLangItItInSystem.mk

Figure 25: Inheritance tree of product configurations of Android 6. The leaf nodes represent the final configurations for a device. The configurations inherit from their parent nodes (incoming edges). The red circle highlights the configuration for

HTC Nexus 9 32- and 64-bit versions.

33

version and a 64-bit version, both inheriting from their parent node that encapsulates their commonality. In the following we will present some concrete examples from different product configuration files.

Example 1 This example refers to the variant of the Nexus 5 device (codename “Hammerhead”) that contains a fingerprint sensor. The implementation of this variability in Android’s board configuration has already been presented as an example for conditional execution in Figure 21. In this example, we see how this variability is resolved in the product configuration using inheritance. For managing this variability, Android uses a common product configuration for Nexus 5 devices that contains their commonality. For implementing variability, a new product configuration is created that inherits from this configuration and adds any additional information. The product configuration of the Nexus 5 variant with fingerprint sensor is depicted in Figure 26. In this variant, the module implementing the fingerprint sensor capability is added for compilation (line 1). The common settings are inherited from the parent product configuration (line 3) and the product name is set accordingly (“_fp” refers to “fingerprint”). Therefore, Android make use of inheritance at the configuration level to separate variability and commonality.

Example 2 Figure 27 depicts the product configuration of a basic Nexus 5 device and demonstrates how inheritance at the configuration level can also be used for simplifying the configuration files. First, the configuration inherits from a base configuration provided by Android (aosp_base_telephony, line 1) that adds basic functionality common to all devices and has its own large inheritance tree. Next, some basic properties of the device are defined (line 3-8)

1. PRODUCT_PACKAGES := fingerprint.hammerhead 2. PRODUCT_LOCALES := en_US 3. $(call inherit-product, device/lge/hammerhead/aosp_hammerhead.mk) 4. PRODUCT_NAME := aosp_hammerhead_fp 5. PRODUCT_DEVICE := hammerhead

1. # Inherit from the common Open Source product configuration 2. $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) 3. PRODUCT_NAME := full_hammerhead 4. PRODUCT_DEVICE := hammerhead 5. PRODUCT_BRAND := Android 6. PRODUCT_MODEL := AOSP on HammerHead 7. PRODUCT_MANUFACTURER := LGE 8. PRODUCT_RESTRICT_VENDOR_FILES := true 9. $(call inherit-product, device/lge/hammerhead/device.mk) 10. $(call inherit-product-if-exists, vendor/lge/hammerhead/device-vendor.mk)

Figure 26: Example for the use of inheritance in Android’s configuration for separating commonality and variability. The function call highlighted in red depicts the inheritance mechanism. Example taken from Device/Ige/hammerhead/aosp_hammerhead_fp.mk

Figure 27: Example for the use of inheritance in Androids configuration to simplify configuration files. Example taken from Device/Ige/hammerhead/full_hammerhead.mk

34

and the detailed configuration is inherited from a separate file (device.mk, line 9). Finally, some vendor specific configuration settings might be added if such file exists (line 10). This example showed how inheritance among configuration files can be used to simplify individual configuration files by implementing the principle of separation of concerns.

Source Code Figure 28 depicts an example for the usage of inheritance in source code. Similar to the usage of inheritance in configuration files (cf. Figure 26) the purpose is to encapsulate commonality in a common base class and separate variability in individual subclasses. The example shows the implementation of different sensors of different manufacturers in Android. The commonality among these sensors is captured in a common base class SensorBase. Each sensor inherits from this class to implement its specific features. Hence, variability and commonality are separated using inheritance.

Figure 28: Example for the usage of inheritance in the source code to separate commonality and variability

35

Summary

Table 6: Summary of the results obtained by qualitative investigation of variability realization techniques in Android on different layers. * These mechanisms are not localized to a single layer but are crosscutting through all layers

Conditional Compilation *

Conditional Execution

Module Replacement *

Inheritance

Configuration Implement dependencies among configuration constants and build environment and between different layers of configuration

Simplification of configuration files & separation of commonality and variability

Build System Implement file-level variability

Connect configuration to variability implementation technique

Implement variability at the granularity of submodules

Source Code Implementing small-grained statement/function level variability

Implementation of runtime variability

Implementation of variability at the granularity of files

Separation of commonality and variability

4.2.2 Quantitative Analysis

4.2.2.1 Conditional Compilation in Source Code In the chapter on qualitative analysis of variability realization techniques we have already presented examples on how variability is realized using conditional compilation. In this chapter, we will gather quantitative data on the usage of conditional compilation in source code to answer the following questions:

Q1: To what extend is conditional compilation used in Androids source

code?

Q2: To what extend is it used to implement variability?

Q3: What is the distribution of variation points among the Android code?

Q4: Which constants have the highest impact?

The process for analyzing the usage of conditional compilation is depicted in Figure 29. In this analysis, we focus on conditional compilation in source code (C/C++ files) which is typically implemented using the C/C++ Preprocessor. The analysis is split into two parts: (1) analyzing Preprocessor usage and (2) tracing Preprocessor constants to their definition. For (1), we extract the variation points introduced through conditional compilation using the VITAL tool [4] and then calculate the file scattering per constant to reason about the extent to which conditional compilation is used (Q1). But since the preprocessor is also used for other purposes like constant or macro definition, not all of these variation points correspond to the implementation of real variability. Since Android’s configuration files are actually Makefiles,

Mechanism

Layer

36

Figure 29: Process for analyzing the usage of conditional compilation in Androids source code. We use the VITAL tool to

extract variation points in source code introduced by the preprocessor and trace these constants to their definition in code or Makefiles.

their configuration constants can only be referenced in other Makefiles. This means, that any Preprocessor constant that is defined based on Androids configuration, must only be introduced via Makefiles. As a result, we consider only those variation points as real variability, whose preprocessor constants have been introduced via Makefiles (Q2). This motivates part (2) of our analysis where we trace each Preprocessor constant found in source code, to its original definition (i.e. in source code or Makefiles). For identifying definitions in source code, we use a simple string-based search through all source code files and for Makefiles, we use the Kati tool to look for Preprocessor definitions introduced via the –D compiler flag. When filtering out those constants which are defined in source code, we can reason again about the distribution of variation points and the impact of individual constants by calculating the scattering degree (Q3) (Q4). In the following, each analysis step is explained in more detail.

Extracting Preprocessor Variation Points As a first step, we need to extract the usage of conditional preprocessor directives like #ifdef, #ifndef. We use the VITAL tool [4] to automatically extract all variation points in Android 6 introduced through the preprocessor. As a result, we found 334744 variation points (excluding header include guards ending with _H or _H_) containing 42531 unique preprocessor constants introduced through conditional compilation. On average, every preprocessor

37

constant results in 7,8 variation points. Assuming the names of all preprocessor constants are unique throughout the Android source code, we can calculate the file scattering as simply the number of files that have at least one variation point with this constant. The results are visualized in Figure 30. The distribution of the scattering degree shows the same phenomenon we identified in section 4.1.2.2 when we studied the configuration impact of individual configuration constants, namely the heavy-tailed distribution. There are only four preprocessor constants (0,0094%) scattered across more than 1000 files, 24 scattered across 1000-500 files (0,056%) and 42293 constants scattered across less than 100 files (99,44%). For comparison, there are in total 151296 C/C++ files (including header files) in Android 6.

Figure 30: The file scattering of preprocessor constants that were extracted using the VITAL tool. Only the 100 most

scattered constants are shown.

Figure 31 offers a different visualization of the same data using a so-called Treemap that shows the scattering degree of each constant encoded as the size of rectangles. This kind of visualization allows for a better comparison of the scattering degree of Preprocessor constants.

Figure 31: The file scattering of preprocessor constants that were extracted using the VITAL tool. Each box corresponds to a preprocessor constant and the size of each box visualized its scattering degree. Only the 100 most scattered constants are

shown.

38

Tracing Preprocessor Constants to their Definition

Figure 32: Summary of part (2) our analysis of the usage of conditional compilation. We trace variation points in source code

to the definition of their Preprocessor constants which can be either defined in source code or Makefiles.

Figure 32 summarizes part (2) of our analysis. Now that we have extracted all variation points in C/C++ code introduced through the Preprocessor our goal is to identify to what extent these variation points actually implement variability. For this purpose, we trace each preprocessor constant to its definition, which can be either in C/C++ code or Makefiles. In C/C++ code, Preprocessor constants are defined via #define. In Makefiles, Android introduces special variable names that contain the compiler flags used for building a compiler command. One of these flags (-D) can be used to introduce Preprocessor constants. Since we know that the Android configuration files are really Makefiles and their parameters can only be accessed via Makefiles, we assume that any Preprocessor constant that is used for implementing variability must have a connection to Androids configuration and can thus only be introduced via Makefiles. For finding the definition of Preprocessor constants in code, we make use of the UNIX programs grep4 and awk5 embedded in a Python script that allow us to search all C/C++ files highly parallelized. For finding the definition of Preprocessor constants in Makefiles we use a slightly modified version of Kati6 which is a tool developed by Google that parses and evaluates Makefiles to convert them to the Ninja build system currently used by Android (master-branch). We modified Kati to produce a log entry of each Makefile statement and its evaluation. Since many conditional statements depend on configuration parameters, operating system, build system environment, etc. not all paths are evaluated by Kati. For example, if the expression of a conditional statement if(expression) is evaluated to true, the else part will not be evaluated. We modified Kati to also include those statements into its log that would not be considered by default and evaluate them when possible. This enables us to consider all the Makefiles statements, mostly independent of the chosen configuration. The limitations of this approach are discussed at the end of this section.

4 http://www.gnu.org/software/grep/manual/grep.html 5 https://www.gnu.org/software/gawk/manual/gawk.html 6 https://github.com/google/kati

39

Figure 33: The results of tracing each preprocessor constant to its definition in source code or Makefiles. We differentiate

between the categories Code (i.e. definition in Code), Makefile (i.e. definition in Makefile), Code+Makefile (i.e. definition in Code and Makefile) and Not Found (i.e. definition neither in code nor Makefile)

The results for tracing Preprocessor constants to their definition is depicted in Figure 33. We differentiate between four different categories: the definition was found in code, the definition was found in a Makefile, the definition was found in code and Makefiles or the definition was found neither in code nor Makefiles. Unsurprisingly, the majority of definitions (64,6%) of preprocessor constants were found in code which confirms our assumption that most of the variation points using preprocessors are not related to variability. A lot more surprising is the fact that for 33,9% of the constants, there is no definition in code or Makefiles. For the remaining 1,5% (i.e. 655 constants) we found that 1,1% are only defined in Makefiles and 0,5% are defined in Makefile and code. The latter may be the result of our general assumption that the name of Preprocessor constants is always unique. The fact that many Preprocessor constants are not defined at all, may be explained by the fact that Android includes many open source projects that are configured for their use within the Android source code. As a result, only those Preprocessor constants are defined that enable the functionality required by Android. The purpose of the previous step was to identify those constants that are used for implementing variability. As stated before, we assume that only those constants introduced through Makefiles (i.e. the categories Makefile and Code+Makefile from above) satisfy this property. Now that we have identified those constants we can revisit the scattering degree. Figure 34 shows the file scattering only for those Preprocessor constants that are defined via Makefiles.

Figure 34: File scattering of Preprocessor constants that are defined in Makefiles.

40

When comparing Figure 34 showing the file scattering of Preprocessor constants introduced via Makefiles and Figure 30 showing the file scattering of all Preprocessors constants, we notice that those constants introduced via Makefiles are a lot less scattered. First of all, the constant with the maximum scattering degree (introduced via Makefiles) is scattered across 1608 files whereas the most scattered constant in our previous analysis was scattered across almost 10000 files. Secondly, the average scattering degree is also slightly smaller with 4,8 for constants introduced via Makefiles and 5,1 for all Preprocessor constants.

Figure 35: Treemap showing the scattering degree of those Preprocessor constants introduced via Makefiles.

Now that we have extracted the Preprocessor constants introduced via Makefiles, it is time to revisit our assumption that only those constants are used for variability implementation. When studying the names of constants (cf. Figure 35) we see that at least the most scattered constants do not seem to be related to features. Rather they are used for encoding technical variability like whether to compile with a debug option (e.g. DEBUG, NDEBUG), how memory is organized (e.g. __LITTLE_ENDIAN, BITS_PER_LONG) or whether to run the static code analysis tool lint. However, we can identify some naming patterns for the less scattered constants. In a systematic usage of conditional compilation for implementing variability one would expect a consistent prefix for Preprocessor constants so they can be distinguished from other constants. For example, an industrial product line from Danfass uses the prefix HAS_FEATURE [23]. And indeed, also for Android we see 160 constants beginning with HAVE_ (e.g. HAVE_MMAP, HAVE_JPEG, HAVE_IPV6) and 28 with the prefix USE_ (e.g. USE_OPENSSL, USE_SIMULATOR, USE_CPUSETS). Overall 28,7% of the constants correspond to this naming pattern. But as we have seen in the examples for the usage of conditional compilation in Android (cf. Figure 18) this must not be the case and cannot be assumed for our analysis.

41

Limitations of the Analysis Approach As Make is a Turing complete language, its analysis is never without shortcomings. Existing techniques based on static analysis tend to be tailored to a specific system and cannot be applied for other systems [3]. Our Kati-based approach for extracting data from Makefiles solves this issue since it is independent of a particular system and can therefore be applied to any Make-based system. On the other hand, Kati is not a static analysis tool. Its purpose is to translate build rules from Make into the Ninja build system. For this reason, it only considers a certain path through Makefiles and ignores those execution paths were conditional statements evaluate to false. In particular, it makes use of a specific configuration and does not explore the complete configuration space. We addressed this issue by forcing Kati to look at as many execution paths as possible. This increases the amount of data we can extract from Makefiles dramatically, but also brings some side-effects.

Figure 36 illustrates the major shortcomings of our Kati-based solution. LOCAL_CFLAGS is the variable name in Android that is used to store compilation flags (such as –DXY, to add preprocessor constants). On the left, we illustrate the problem of having a different value for LOCAL_CFLAGS due to a variable reference that is conditionally set. Although we extract the knowledge that FLAG_NAME can have two different values based on the value of the constants _UNIX_, this knowledge is not incorporated into our analysis yet. Therefore, any variable reference of FLAG_NAME will only yield the value of the last executed assignment, in this case the Else-case. The right side of Figure 36 illustrates the dependence of configuration constants (let us assume BOARD_CONFIG_PATH is defined in the board configuration of an Android device). Since a configuration needs to be chosen before running Kati, all configuration constants are set to a specific value. The variable reference $(BOARD_CONFIG_PATH) will yield the value of BOARD_CONFIG_PATH that was set in the chosen configuration. In the case that a configuration constant is referenced in a LOCAL_CFLAGS assignment (i.e. it is introduced as a preprocessor constant), Kati will only use the value of the configuration constant that is defined in the chosen Android configuration and not explore the complete possible configuration space.

LOCAL_CFLAGS += -D$(BOARD_CONFIG_PATH)

If _UNIX_ then FLAG_NAME = FLAG_A Else FLAG_NAME = FLAG_B LOCAL_CFLAGS += -D$(FLAG_NAME)

Figure 36: Disadvantages of Kati-based analysis of Makefiles. Illustrating the problem of the variable LOCAL_CFLAGS being influenced by multiple execution paths (left) and its possible dependence on configuration constants

42

Conclusion We presented an analysis method to extract data on the usage of conditional compilation in make-based systems. We first analyzed the usage of the C/C++ preprocessor to implement variation points using the VITAL tool and found that the distribution of Preprocessor constants among source code files follows a heavy-tailed distribution with only a small fraction being highly scattered. In part two of our analysis we traced the Preprocessor constants used in variation points to their original definition to identify which of these constants are introduced via Makefiles. Since the configuration in Android is written in the form of Makefiles, only those constants might have a connection to Androids configuration and therefore may implement real variability. As a result, we found that the same heavy-tailed distribution for the file scattering persists for those constants introduced via Makefiles, although the maximum scattering degree is much lower as well as the average scattering degree. Finally, even for those constants introduced via Makefiles, there was no heavily used naming pattern and it is therefore impossible to further constrain the amount of constants that relate to feature variability, i.e. whose variation points are actually used to implement variability. As future work, we intend to look at the variation points in Makefiles and focus on those Preprocessor constants that are introduced conditionally, based on some configuration constant. This way we can establish links between Androids configuration and the implementation mechanism conditional compilation in source code and therefore distinguish feature variability from merely technical variability.

4.2.2.2 Conditional Execution in Build System

Figure 37: The usage of conditional execution in the Android build system. We differentiate between local Makefiles (blue) which belong to individual modules and global Makefiles (orange) which are executed globally independent of individual

modules. Different bars denote the usage of different kinds of variables (i.e. board configuration constants, product configuration constants, etc.) and their height indicate the number of variation points. The bar label denotes the number of

variables that belong to each variable class in brackets.

43

Androids build system is defined in Makefiles which allow the usage of conditional statements much like a programming language. It supports regular if-statements like ifeq, ifneq but also statements like ifdef, ifndef in the style of the C/C++ preprocessor. We will consider all of these statements as the implementation of conditional execution since they change the control flow. Note that even though conditional execution has been presented as a technique to implement runtime variability (cf. section 2.1.3) its usage in Makefiles clearly refers to construction time of the overall system since Makefiles are only executed at construction time. We analyzed the usage of conditional execution in Android’s build system by parsing a log of Make statements created by our modified Kati tool, like we did in the previous analysis. The results are shown in a bar chart in Figure 37. We distinguish between local Makefiles (blue bar), which belong to individual modules and global Makefiles (orange bar) which reside in Androids build folder and have an impact on the overall build process. Looking at the variables used in these conditional expressions, we can identify several different classes of variables. We differentiate between variation points containing board configuration constants, product configuration constants, constants defining the build environment (e.g. operating system) and everything else. We can clearly identify board configuration constants based on the results of our previous analysis in section 4.1.2.1. Additionally, we also considered constants with the prefix “BOARD_” as board configuration constants. Product configuration constants are identified with the prefix “PRODUCT_”. The usage of the constants HOST_OS and TARGET_OS define the build environment class whose variation points depend on the operating system. Every other variation point falls in the Others class. From the results in Figure 37 we can make the following observations: Fist, the majority of variation points reside in local Makefiles which means that binding to a particular variant is done mostly locally for each module and not globally. Second, conditional execution is only used for resolving variability introduced through the board configuration and not variability introduced through the product configuration. The board configuration constants are used in almost 500 variation points, whereas the product configuration constants are used in close to zero variation points. Therefore, variability introduced through the product configuration must be resolved using other mechanisms and possibly at other binding times. Third, the build environment, i.e. the operating system is another major source of variability implemented using conditional execution in 372 variation points. Thus, in a system that is being developed in distributed way by people all over the world with different hardware and software, build environment variability seems to be one major kind of variability.

5 Conclusion & Future Work In this research project, we have explored the realization of variability in Android – a famous open source software product line. We started in chapter 2 by introducing the necessary terminology and covered foundational knowledge about product lines, variability and binding times. We presented variability mechanisms commonly found in literature and weighted their advantages and disadvantages. In the following chapter, we started to introduce Android, its architecture, configuration and build process. Before going into the analysis on the usage of variability mechanisms we covered the configurability of Android and highlighted its shortcomings. We found a lack of systematic variability management in the sense that there is no definition of possible configuration constants, possible values, dependencies or trace to

44

any higher level feature model. Nevertheless, we could reverse engineer some of the properties of Android’s configuration. Next, we presented the results of our qualitative research on the variability mechanisms used in Android. We found that many of the mechanisms found in literature are also used in Android, however sometimes for a different purpose. The last part of our project was the quantitative research on the usage of variability realization techniques. We presented an analysis process and its results for Android for the usage of conditional compilation in source code and conditional execution in Android’s build system. In future work, we intend to apply this method to other mechanisms as well such as module replacement.

45

6 References [1] S. Nadi and R. Holt, “Mining Kbuild to detect variability anomalies in Linux,” Proc. Eur.

Conf. Softw. Maint. Reengineering, CSMR, pp. 107–116, 2012. [2] L. Passos, J. Guo, L. Teixeira, K. Czarnecki, A. Wasowski, and P. Borba, “Coevolution of

variability models and related artifacts: A case study from the Linux kernel,” ACM Int. Conf. Proceeding Ser., pp. 91–100, 2013.

[3] C. Dietrich, R. Tartler, W. Schröder-Preikschat, and D. Lohmann, “A Robust Approach for Variability Extraction from the Linux Build System,” in Proceedings of the 16th International Software Product Line Conference - Volume 1, 2012, pp. 21–30.

[4] B. Zhang and M. Becker, “Variability Code Analysis Using the VITAL Tool,” in Proceedings of the 6th International Workshop on Feature-Oriented Software Development, 2014, pp. 17–22.

[5] P. C. Clements and L. Northrop, Software Product Lines: Practices and Patterns. Addison-Wesley, 2001.

[6] S. Apel, D. Batory, C. Kstner, and G. Saake, Feature-Oriented Software Product Lines: Concepts and Implementation. Springer Publishing Company, Incorporated, 2013.

[7] K. Pohl, G. Böckle, and F. J. van der Linden, Software Product Line Engineering: Foundations, Principles and Techniques. Secaucus, NJ, USA: Springer-Verlag New York, Inc., 2005.

[8] M. Svahnberg, J. van Gurp, and J. Bosch, “A Taxonomy of Variability Realization Techniques: Research Articles,” Softw. Pr. Exper., vol. 35, no. 8, pp. 705–754, Jul. 2005.

[9] C. Gacek and M. Anastasopoules, “Implementing Product Line Variabilities,” in Proceedings of the 2001 Symposium on Software Reusability: Putting Software Reuse in Context, 2001, pp. 109–117.

[10] I. Jacobson, M. Griss, and P. Jonsson, Software Reuse: Architecture, Process and Organization for Business Success. New York, NY, USA: ACM Press/Addison-Wesley Publishing Co., 1997.

[11] B. Zhang, S. Duszynski, and M. Becker, “Variability Mechanisms and Lessons Learned in Practice,” in Proceedings of the 1st International Workshop on Variability and Complexity in Software Design, 2016, pp. 14–20.

[12] J. Liebig, S. Apel, C. Lengauer, C. Kästner, and M. Schulze, “An analysis of the variability in forty preprocessor-based software product lines,” 2010 ACM/IEEE 32nd Int. Conf. Softw. Eng., vol. 1, pp. 105–114, 2010.

[13] B. Zhang, M. Becker, T. Patzke, K. Sierszecki, and J. E. Savolainen, “Variability Evolution _ and Erosion in Industrial Product Lines: A Case Study,” Proc. 17th Int. Softw. Prod. Line Conf., pp. 168–177, 2013.

[14] D. Le, E. Walkingshaw, and M. Erwig, “#ifdef confirmed harmful: Promoting understandable software variation,” in 2011 IEEE Symposium on Visual Languages and Human-Centric Computing (VL/HCC), 2011, pp. 143–150.

[15] D. Muthig and T. Patzke, “Generic Implementation of Product Line Components,” Revis. Pap. from Int. Conf. NetObjectDays Objects, Components, Archit. Serv. Appl. a Networked World, pp. 313–329, 2003.

[16] D. Abrahams and A. Gurtovoy, C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond (C++ in Depth Series). Addison-Wesley Professional, 2004.

[17] A. Alexandrescu, Modern C++ Design: Generic Programming and Design Patterns Applied. Boston, MA, USA: Addison-Wesley Longman Publishing Co., Inc., 2001.

46

[18] P. G. Bassett, Framing Software Reuse: Lessons from the Real World. Upper Saddle River, NJ, USA: Prentice-Hall, Inc., 1997.

[19] T. Patzke, “A Method for Reducing Arbitrary Complexity in Reusable Embedded Systems Code - The Frame Technology Idiom.”

[20] E. Sugawara and H. Nikaido, “Embedded Android,” Antimicrob. Agents Chemother., vol. 58, no. 12, pp. 7250–7, Dec. 2014.

[21] “Android Documentation.” [Online]. Available: https://source.android.com/devices/index.html. [Accessed: 13-Dec-2016].

[22] B. Zhang, V. Tenev, and M. Becker, “Android build dependency analysis,” in 2016 IEEE 24th International Conference on Program Comprehension (ICPC), 2016, pp. 1–4.

[23] B. Zhang, M. Becker, T. Patzke, K. Sierszecki, and J. E. Savolainen, “Variability Evolution and Erosion in Industrial Product Lines: A Case Study,” in Proceedings of the 17th International Software Product Line Conference, 2013, pp. 168–177.

[24] T. Berger, R.-H. Pfeiffer, R. Tartler, S. Dienst, K. Czarnecki, A. Wąsowski, and S. She, “Variability mechanisms in software ecosystems,” Inf. Softw. Technol., vol. 56, no. 11, pp. 1520–1535, 2014.