Skip to main content

14.13 Android

In the previous section, we were still immersed in the world of PPPoE, figuring out how to wrap Ethernet frames into point-to-point channels. In this section, we'll zoom out and look at one of the largest "tenants" of Linux kernel networking in the mobile world—Android.

Android is a somewhat special tenant. It lives in the Linux kernel building, but it has remodeled much of the interior and even added its own locks to certain doors.

14.13.1 An Empire Above the Kernel

In recent years, Android has undeniably dominated the mobile OS landscape. Its foundation is the Linux kernel—or, more precisely, a heavily modified version maintained by Google engineers.

The vast majority of Android devices run on ARM chips (although a project called android-x86 once tried to port it to Intel CPUs, and the first-generation Google TV was also x86-based, but it eventually moved back to ARM). Android was originally founded in 2003 by Andy Rubin in California and was acquired by Google in 2005. In 2007, the Open Handset Alliance (OHA), a consortium of companies, brought it to the forefront.

Although it is open source, released under the Apache License, its development model is an entirely different beast compared to the Linux community.

Linux development happens out in the open—on public mailing lists, where anyone can submit patches, people argue (on the CC list), and code eventually gets merged. Android doesn't work this way. Most of its development happens behind Google's tightly closed doors. You can certainly submit patches to Gerrit, but whether they make it into the main branch is entirely up to Google. This "open source but not exactly open" model has always left a veil between the Android kernel and mainline Linux.

Despite this, Google's contributions to mainline Linux are massive. The Cgroup subsystem we mentioned earlier in this chapter was originally developed by Google engineers. On the networking front, there are two major patches: RPS (Receive Packet Steering) and RFS (Receive Flow Steering), submitted by Google's Tom Herbert and merged in the 2.6.35 kernel. These features make multi-core systems smarter when handling network packets—instead of letting interrupts fly around randomly, they distribute packets to specific CPUs based on the flow hash.

There's a simple truth behind this: when you have hundreds of CPU cores at your disposal, getting network packets to flow smoothly across them without contention is a massive engineering challenge. Google fed the experience it gained in its data centers back to the community.

As for the Android kernel itself, although a lot of code now resides in the Linux kernel's staging directory, fully merging it into mainline remains a work in progress. Historically, Google built quite a few of its own wheels, such as WakeLocks, Binder (a lightweight RPC-based IPC mechanism), Ashmem (Anonymous Shared Memory), and Low Memory Killer. These gave kernel maintainers quite a headache—WakeLocks, for instance, were outright rejected in 2010.

But things are slowly improving. Some features have been accepted or replaced by more generic mechanisms (like Autosleep). The non-profit organization Linaro has played a significant role in this, coordinating various vendors to push their changes upstream.

However, diving deep into the implementation details of the Android kernel and its mainlining process is beyond the scope of this book—that alone could fill another volume.

14.13.2 The User-Space Networking Dilemma

Since the kernel is still the kernel (more or less), what is there to talk about regarding Android networking?

Plenty. The biggest pitfalls of Android networking lie not in the kernel, but in user space.

Android relies heavily on the HAL (Hardware Abstraction Layer), and networking is no exception. This leads to a rather counterintuitive phenomenon: even if the driver in your kernel is perfectly written, if the framework above doesn't recognize it, you simply can't get online.

In early Android versions (up to 4.2), the framework had virtually zero support for Ethernet. If you compiled the driver into the kernel, the underlying TCP/IP stack would indeed work, and you could see the network was up via ADB (Android Debug Bridge), but the Android system itself was completely unaware of it.

This state persisted until after 4.0, when the android-x86 project forcefully added an Ethernet implementation (it was rather rough in design, but it worked). By 4.2, the official AOSP source finally supported Ethernet—but only in the most basic sense. It could detect cable plug/unplug events, and if there was a DHCP server on the other end, it could get an IP. But if you wanted to configure a static IP, set a proxy, or force all apps to use this interface... sorry, no dice. Or, more accurately, the door was locked.

If you really needed to configure networking like a proper Linux system (configuring interfaces, toggling between DHCP and static, setting proxies), you had to apply a bunch of patches or even modify the framework layer.

To make matters worse, Android's network management is quite "dictatorial." It typically only allows one interface to be active at a time. Even if you have eth0 and eth1, don't expect to use both ports simultaneously like a router. Android is a phone OS, not a router OS. While this restriction is frustrating, it aligns with its product positioning.

14.13.3 Four Typical Android Specializations

To keep you from getting completely lost when debugging Android networking, here are four key differences you must know.

1. The "Paranoid" Network

If you write a native Linux network program and run it on Android, you might be surprised to find—it gets rejected.

On standard Linux, any program can call socket() to open a socket and send data. But on Android, not everyone is qualified to play with the network.

Google added a security mechanism called "Paranoid Network." This filters processes by GID (Group ID). If a process doesn't belong to a specific group, the kernel will outright reject its network operations.

This is why, when writing a standard Android app, you must add the line <uses-permission android:name="android.permission.INTERNET" /> in AndroidManifest.xml. This line isn't just for the Java virtual machine to read—it ultimately gets passed down to the underlying kernel to check your GID.

Because this mechanism is so unconventional, getting it merged into mainline Linux has been very difficult.

2. Bluedroid Replaces BlueZ

In the earlier Bluetooth section, we covered Linux's BlueZ protocol stack. But after Android 4.2, Google decided to drop BlueZ and switch to Bluedroid.

This is a Bluetooth stack based on Broadcom's code. The reasons for the switch were varied, including performance, power consumption, and code maintenance. For developers, this means the Bluetooth implementation you see on Android is quite different from what you'd see on a Linux laptop.

Low Energy Bluetooth (BLE) support was added in Android 4.3 (API Level 18). Before that, AOSP had no native BLE support, leaving vendors to implement their own APIs.

3. Netfilter Extension: xt_qtaguid

Data usage tracking is a crucial feature for mobile OS—"How much data have I used this month?" "Which app is consuming the most data?"

To achieve this, Google added a module to the kernel's netfilter subsystem called xt_qtaguid. Its purpose is to allow user-space applications to tag their own sockets.

With this tag, the kernel knows which app a packet belongs to when processing it. This allows for precise, per-app statistics. This change also involved modifications to the underlying Netfilter, and Google attempted to push these patches to the LKML, though the path to merging was quite bumpy.

4. User-Space NFC Implementation

In the earlier section on IEEE 802.15.4 and NFC, we mentioned NFC's layering. On Android, the NFC architecture made a specific choice: most of the protocol stack logic resides in user space.

The actual implementation runs in user space, communicating with the underlying Broadcom or OEM-provided drivers via the HAL. This design differs from traditional Linux device drivers, which typically stuff all logic into the kernel.

While there are countless books online teaching you how to write Android apps, resources covering Android's internal implementation (especially low-level and kernel interactions) are incredibly scarce. If you truly want to dive deep into this area, here are some hardcore resources:

Books:

  • Embedded Android: Porting, Extending, and Customizing by Karim Yaghmour (O'Reilly Media, 2013). A classic guide on porting Android to new devices.

Slides:

  • Android System Development by Maxime Ripard, Alexandre Belloni (free-electrons.com): Over 400 slides packed with solid content.
  • Android Platform Anatomy by Benjamin Zores: Dissects the Android platform architecture.
  • Jelly Bean Device Porting by Benjamin Zores: A practical walkthrough of porting Jelly Bean.

Conferences and Websites:

  • Android Builders Summit (ABS): An annual event—if you can't attend, watch the videos and slides.
  • XDA Developers Conference: A gathering for geeks, featuring many in-depth hacking shares.
  • Android internals forum: gmane.comp.handhelds.android.platform, though a bit dated, the historical discussions are highly valuable.

Finally, remember that all Android source code is at android.googlesource.com. However, because the project is so massive, it's split into hundreds of git repositories. Google uses a Python tool called repo to manage this pile of repos—if you plan to download the source and build it yourself, learn to use repo first.


Chapter Echoes

With this, our journey through this chapter truly comes to an end.

Looking back, we've taken some massive strides in this chapter:

  • We started with Namespaces, establishing a perspective of isolation.
  • We put a rein on resources using Cgroups.
  • We ventured into the wireless worlds of Bluetooth, 802.15.4, and NFC to look at those quirky protocols.
  • We dug into the kernel's Notifier Chains and PCI subsystems.
  • Finally, we looked at complex cases like PPPoE and Android, where underlying technologies are packaged into upper-level platforms.

Remember what we said at the beginning of this chapter? The Linux networking subsystem is a massive machine. Every subsection in this chapter—whether it's multi-core RPS or Android's HAL—is essentially answering one question: As this machine grows more complex, more multi-core, and more mobile, how do we harness it?

Now we've reached the last page. Linux networking is a vast ocean, and new features are constantly flowing into the mainline kernel. We hope this book hasn't just taught you a few APIs or code snippets, but has given you a map. The next time you're staring at ethtool output or a kernel crash log, you'll know where to look and what questions to ask.

Happy debugging!