Most people will use Android devices on a daily basis, even if they don't realise it! Android's use in smartphones is well-known (Wikipedia suggests Android holds around 70% of the smartphone market share), but Android may also be found in cars, TVs, entertainment systems, and other devices besides.
Given how widespread Android use is in many consumer devices, it has started to become a de-facto standard for Embedded Linux more widely.
Increasingly, if you're looking to develop an embedded product, the chipset vendor may well provide chip (or board) support in the form of an customised release of Android rather than a more traditional Embedded Linux Board Support Package.
As such, even if you're not trying to develop a mobile phone, you may end up using Android because that's what your chipset vendor supports. If you're used to developing for an Embedded Linux system, you may find Android a little... different.
In this article, we'll take a look at what Android looks like from a general embedded development perspective.
Embedded Linux, but not as we know it
Of course, Android is Linux, it runs the Linux kernel. But at the same time, it's not really. Some of the familiar staples of an Embedded Linux system are either replaced or absent.
To begin with, getting the source is quite different. The Android Open Source Project (AOSP) lives in a repository managed by the repo tool, which doesn't appear to be widely used outside of the Android project. It's essentially a way to manage lots of git trees as a cohesive whole.
And when I say "lots of git trees", I really do mean it. While this isn't just an Android-specific issue (many Embedded Linux systems involve a lot of source), it's worth noting that the Android repo tree is large: north of 100Gb. That's a lot of source code to physically store on a development machine, not to mention navigate around and work with in general.
Once you have source, the build is probably going to be unfamiliar if you're used to tools like OpenEmbedded, buildroot, or distro tooling (e.g. for Debian, etc). Android uses a combination of Gradle and ninja, with a little bit of make glue. Broadly speaking, ninja acts as a low-level coordination tool for running build commands, and Gradle ties everything together. And make is seemingly being phased out in favour of gradle/ninja, but still exists in the AOSP tree at the time of writing.
In contrast to more traditional Embedded Linux stack, the Android C library is not glibc or even one of the "minimal" C libraries such as musl. Android comes with its own C/C++ library, Bionic. Whether or not that represents a problem depends a little: we've had to code around Bionic missing some POSIX support when porting code, for example, as well as debug tricky crashes around C++ linking and exception handling.
The Android runtime OS is very much not an Embedded Linux system. It has its own init system, its own IPC mechanism, Binder, and its own (quite different) architecture compared to more traditional Embedded Linux. Later versions of Android also enforce selinux use, and it's not always easy to disable it.
Android is not (necessarily) made for you
Having made a start with Android, you may find you run into issues around Android's design and expectations. Android is of course designed primarily for smartphones. While this is great if you're working on a smartphone, it may not be so great if you're not.
To begin with, Android contains a sophisticated middleware framework which supports apps. It has developed over time to support lots of different apps which may run on a smartphone, but still -- you have to figure out how to work with it, and you may find it's not ideal for what you want. If you are trying to do something the Android runtime system doesn't expect, you may find different components trying to fight you.
At a lower level, the Android build has checks and balances in place to ensure that the lower-level system components play nicely for smartphone use. In particular it validates kernel options during the build: if you want to configure the kernel differently you may end up running into this.
Finally, because people interested in Android development are primarily app developers, that's what what most of the documentation, tutorials, and guides you'll find online focus on. It can be hard to find information outside of that focus.
Living with Android
Android is a well-established project
Android is around a couple of decades old at this point, and is well beyond the churn and teething problems inherent with younger projects.
The cadences of Android development are well established, allowing for effective planning.
There is plenty of good documentation available from the Android project itself. While the scope of the documentation is large enough to be overwhelming at times, that's easily better than no documentation at all.
Development tooling, albeit sometimes unfamiliar, is comprehensive and generally very good:
- Android Debug Bridge (adb) makes it easy to get shell access over USB or Ethernet;
- system updates are already supported and integrated, allowing for easy update of apps, or even whole-system updates;
- the runtime has great support via. dumpsys for capturing crash logs and information;
- performance monitoring tools are included out of the box;
- tracking of open-source licenses is a well-integrated part of the build system.
Although Android may take some time to get to grips with, it does enjoy the predictability and stability of a long-running project.
Dealing with change
Change is of course part and parcel of any software development. Libraries update, projects move forward, and you have to deal with that.
Android is no different; and if and when you find yourself needing to port code from one version of Android to another you can expect to deal with changes.
The Android build system, as we touched on earlier, is moving to use Gradle, which may mean you need to update your build integration for the components you've added to the build.
The Android Java framework changes from one release to the next. You may need to update your application if some API you depend upon has changed significantly. The upside here is that the Java application-level API is designed to deal with change, and there are good mechanisms in place to allow you to handle it.
Of course, Android system internals also change from release to release.
Unfortunately, if you've ended up having to modify these internals to support your usecase, you may have a harder time porting those aspects of your work.
Unlike the Java API, Android internals make no provision for change management, so if an update breaks something you've coded to, then tough luck.
Brave new world
In a lot of ways, coming from a more traditional Embedded Linux background where the software stack is well-known, and the developer is generally in full control over everything, Android can feel like an uphill battle. There are lots of new tools to get to grips with, and the software architecture looks nothing like the architecture you're used to.
It's tempting, therefore, to view Android as a regression compared with traditional Embedded Linux, especially if you're not using it entirely of your own volition.
But to do so would be to ignore some of the very real benefits Android can offer.
If your application fits reasonably nicely into a smartphone-esque application (and it may well!) then the Android Java framework is a rich and mature software stack to develop upon.
Moreover, that stack is being continuously improved by one of the largest software companies in the world, in a software ecosystem which is developed, at least in part, to make it easy for app authors to build, debug, and optimise their applications.
If the Android architecture is a reasonable fit for your device, these are difficult benefits to ignore.
If you find yourself attracted by Android's benefits, or even having to port to it, why not drop us a line and see whether we can help you get your project up and running?