Mobile OS & Software — Security Architecture

Mobile App Sandboxing: Security Model Comparison Between Android and iOS

By Lena Torres, RF & Connectivity EngineerSeptember 8, 202612 min read

Every tap on a mobile app crosses an invisible boundary. Whether you are granting camera access to a video call or allowing a payment wallet to use NFC, the operating system is deciding — at the kernel level — what that app is allowed to touch. That boundary is the sandbox, and understanding how Android and iOS construct it is critical for anyone building radios, sensor hubs, or connectivity stacks that share the same SoC as untrusted third-party code.

As RF engineers, we tend to think about isolation in terms of spectral masks and RF front-end desense. App sandboxing is the digital equivalent: stringent containment that prevents one app’s misbehavior or compromise from leaking into the modem, the sensor hub IMU pipeline, or another app’s keychain. This article dissects both models from the kernel up, compares their enforcement mechanisms, and maps what has changed in the 2024–2026 platform releases.

1. What Sandboxing Actually Guarantees

A sandbox is not a firewall or an antivirus filter. It is a confinement primitive that enforces three properties:

  • Isolation: Each app receives its own filesystem namespace, private data directory, and process identity. No direct access to another app’s memory or files.
  • Least Privilege: By default, an app can do almost nothing sensitive — no camera, no location, no raw sockets. Capabilities must be explicitly granted.
  • Mediation: All access to privileged resources is brokered through a trusted OS service that checks policy before granting the operation.

Both platforms achieve this, but their primitives differ radically. Android builds on Linux discretionary access control (DAC) plus mandatory access control (MAC) via SELinux. iOS builds on the XNU kernel’s TrustedBSD framework and a policy engine called Seatbelt (Sandbox.kext), coupled with cryptographically enforced entitlements.

Engineering mental model: Think of Android’s sandbox as a multi-tenant apartment building where each tenant has a unique UID-key and SELinux acts as the building security policy. iOS is more like a clean room — every app starts with an empty allowlist and must present a signed entitlement badge for every door it wants to open.

2. Android: UID Isolation + SELinux + Permissions

2.1 Linux UID per App

Historically, Android’s most elegant trick was repurposing Linux user IDs. At install time, the PackageManager assigns each app a unique UID in the range 10000–19999 (AID_APP_START). That UID owns /data/data/com.example.app with mode 700. No other UID can read it. When an app spawns, Zygote forks and calls setuid() to drop into that identity. Even if an app exploits a vulnerability to gain code execution, it is still trapped in its UID.

Mobile Connectivity Standards Comparison
Feature 5G NR Wi-Fi 7 Bluetooth 5.4
Peak Throughput20 Gbps46 Gbps2 Mbps
Latency1–4 ms1–2 ms3–6 ms
RangeSeveral km30–50 m100–400 m
Power DrawHighMediumUltra-low
Primary UseCellular dataLocal networkingPeripherals/IoT

Android 10+ extended this with App Sandboxing via SDK sandbox and isolated processes. WebView renderers, for example, run in isolatedProcess=true services with UID 99000+ and no network or filesystem access at all, communicating only via Binder IPC.

2.2 SELinux in Enforcing Mode (SEAndroid)

DAC alone is insufficient because a root exploit could bypass UID checks. Since Android 5.0, SELinux enforces MAC. Every process carries a context like u:r:untrusted_app:s0:c512,c768. The policy — compiled into /vendor/etc/selinux/ — defines type enforcement rules that even root cannot override. An untrusted_app domain cannot, for instance, open /dev/kmem, raw /sys nodes, or interact with hal_camera_default except through the CameraService broker.

# Example SEAndroid policy snippet for untrusted_app
type untrusted_app, domain;
allow untrusted_app app_data_file:file { read write };
allow untrusted_app cameraserver_service:service_manager find;
# explicitly denied: no raw socket, no /proc/net
neverallow untrusted_app self:capability sys_admin;
neverallow untrusted_app kernel:system module_load;

This explains why modern Android exploits must chain a kernel vulnerability and a SELinux bypass — the MAC layer contains the blast radius.

2.3 Permission Model: Install-time to Runtime

Permissions are the user-visible layer above the sandbox. Normal permissions (INTERNET) are auto-granted. Dangerous permissions (LOCATION, CAMERA, READ_MEDIA_IMAGES) require runtime consent via the PermissionController. Since Android 11, permissions are one-time and auto-revoked for unused apps, and Android 14 introduced partial media access and the photo picker to avoid granting broad storage access.

<!-- AndroidManifest.xml declarative request -->
<manifest package="com.example.fitapp">
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 <uses-permission android:name="android.permission.CAMERA" />
</manifest>

Importantly, permissions are checked by the system service, not the kernel. When your app calls LocationManager.requestLocationUpdates(), the system_server validates checkPermission() before delivering fixes from the GNSS HAL. This brokered architecture is analogous to how we isolate GPU access via Metal/Vulkan — user code never touches registers directly.

3. iOS: Seatbelt, Containers, and Entitlements

3.1 Container Directory Randomization

On iOS, every app is installed to /private/var/containers/Bundle/Application/<UUID> and its data lives in /private/var/mobile/Containers/Data/Application/<UUID>. The UUID is randomized per install, preventing hard-coded path attacks. iOS does not use Linux UIDs for isolation; instead, every third-party app runs as the same mobile user (uid 501) but is caged by Seatbelt.

3.2 Seatbelt / Sandbox.kext

Seatbelt is a TrustedBSD extension that evaluates a Scheme-like profile for each process at exec time. The default profile container denies nearly everything: filesystem writes outside the container, Mach IPC to non-whitelisted services, raw network access, IOKit user clients, and sysctls. Apple’s profiles are compiled from .sb files and cannot be modified on-device.

; iOS container profile (simplified)
(version 1)
(deny default)
(allow file-read* (subpath "/System/Library"))
(allow file* (subpath "/private/var/mobile/Containers/Data/Application/ABCD-1234"))
(deny network* (regex #"^/private/var/run/.*"))
(allow mach-lookup (global-name "com.apple.frontboard.systemappservices"))

Even if an app escapes its container via a bug, the sandbox profile still blocks the syscall at the mac_system_check hook. In RF terms, this is a hardware-like filter in the kernel path, not an application check that can be skipped.

3.3 Entitlements as Capabilities

Where Android uses permission prompts, iOS uses entitlements — key-value pairs signed into the app’s provisioning profile and verified by amfid at launch. Without com.apple.developer.healthkit, HealthKit APIs return errors even if the user would consent. Without com.apple.security.network.client, BSD sockets fail with EPERM.

<!-- Entitlements.plist -->
<dict>
 <key>com.apple.developer.devicecheck.appattest-environment</key>
 <string>production</string>
 <key>com.apple.security.app-sandbox</key>
 <true/>
</dict>

Access to sensitive data still requires user consent (TCC — Transparency, Consent, and Control), stored in /private/var/mobile/Library/TCC/TCC.db. Since iOS 17, TCC has required a usage string and now enforces per-file photo access via the Picker, mirroring Android’s partial access but with stricter binary enforcement.

This tight coupling between code signing and runtime is why Swift vs Kotlin performance comparisons often miss the security overhead: iOS apps pay a small launch-time verification cost for stronger confinement.

Android vs iOS Sandboxing Architecture Block diagram comparing Android UID plus SELinux sandbox versus iOS Seatbelt sandbox. ANDROID SANDBOX iOS SANDBOX App Process — UID 10123 (untrusted_app) App Private Dir: /data/data/com.app (700) SELinux MAC: u:r:untrusted_app:s0 Binder IPC → System Services (broker) Linux Kernel — DAC + LSM Hooks PermissionController mediates CAMERA / LOCATION Policy in /vendor/etc/selinux/plat_sepolicy App Process — mobile (501) + Container UUID Container: .../Containers/Data/Application/<UUID> Seatbelt Profile: container.sb (deny default) Mach IPC → trustd / backboardd (broker) XNU Kernel — TrustedBSD + Sandbox.kext Entitlements + TCC.db mediate access Provisioning Profile signed by Apple
Figure 1 — High-level confinement stack. Android isolates by Linux UID and contains with SELinux MAC; iOS isolates by randomized container path and contains with a default-deny Seatbelt policy gated by signed entitlements.

4. Head-to-Head

DimensionAndroid 14–15iOS 17–18
Isolation PrimitiveLinux UID per app + private data dir, isolatedProcess for renderersRandomized container UUID per install, all apps as mobile user, Seatbelt confinement
Kernel EnforcementSELinux (SEAndroid) MAC, seccomp-bpf filters, AppArmor on some OEMsTrustedBSD + Sandbox.kext (Seatbelt), per-process profile evaluation
Default PolicyAllowed unless SELinux denies; untrusted_app domain narrowly scopedDeny-by-default; only explicit allows in profile pass
Capability GrantManifest permissions + runtime prompts; managed by PermissionControllerSigned entitlements + TCC prompts; verified by amfid/trustd
Inter-App SharingContentProvider, Binder, FileProvider; permission-protected exportsApp Groups, File Provider, URL Schemes, XPC; entitlement-gated
Exploit ContainmentSELinux neverallow + scoped storage + hardware-backed keystore isolationSeatbelt + Pointer Authentication + Page Protection Layer; no sideloaded policy changes
Developer FlexibilityHigh: NDK, sideloading, custom SELinux domains for OEMsLow: no JIT outside entitlement, no dynamic code loading, stricter ATS

From a connectivity standpoint, the difference matters for modem-adjacent code. On a Snapdragon 8 Gen 4 class SoC, the baseband and application processors are isolated via IOMMU, but an app that gains access to android.permission.READ_PRIVILEGED_PHONE_STATE still cannot talk to the modem directly — the Telephony stack brokers RIL commands and SELinux blocks /dev/socket/rild for untrusted apps. On iOS, the equivalent entitlement com.apple.CommCenter.fine-grained simply does not exist for third parties, so baseband AT interfaces are unreachable even with a jailbreak-level sandbox escape unless the profile itself is bypassed.

5. What’s New in 2024–2026

Android 14–15 Hardening

Google moved to strengthen the brokered model. Scoped Storage is now fully enforced, READ_MEDIA_IMAGES/VIDEO replaced broad storage permissions, and the new Photo Picker returns only user-selected assets via a separate picker process. Android 14 also blocked dynamic code loading from writable files (W^X enforcement for app data) and tightened intent mutability to reduce confused-deputy attacks.

Most relevant for RF work: Android 15 introduced Private Space — a secondary user profile with its own keystore and SELinux category (c1024+), effectively a sandbox within a sandbox. It also expanded SDK Runtime (formerly SDK Sandbox) to run ad SDKs in a separate UID with no access to the host app’s data, communicating via mediated APIs.

iOS 17–18 Hardening

Apple’s focus has been blast-radius reduction. Lockdown Mode now applies stricter Seatbelt profiles to WebKit and Messages, and Sensitive Content Warning runs in an isolated XPC service with no network entitlement. iOS 18 extended Required Reason APIs — file timestamp and boot time APIs now require a declared reason and are audited at App Review.

The biggest shift is Alternative App Distribution in the EU, which initially raised concerns that sideloading would weaken sandboxing. Apple’s answer: sideloaded apps still receive the same Seatbelt profile and TCC enforcement; the difference is notarization rather than App Review. The kernel does not distinguish App Store from alternative marketplace apps at the MAC layer.

6. Practical Guidance for Connectivity-Focused

If you are building an app that touches Bluetooth, UWB, Wi-Fi Aware, or GNSS raw measurements, design for the broker:

  • Request minimally, explain clearly. Both platforms now show permission history in Settings. A vague NEARBY_DEVICES request without a feature narrative triggers higher denial rates and, on iOS, App Review rejection.
  • Prefer pickers over permissions. Use Android Photo Picker and iOS PHPicker instead of full library access. You avoid handling large file descriptors and reduce SELinux/TCC audit surface.
  • Assume your process is ephemeral. iOS may terminate background apps aggressively; Android may place them in restricted standby buckets. Persist state outside memory and re-request entitlements on relaunch.
  • Isolate sensitive operations. Move crypto into StrongBox / Secure Enclave via Keystore/Keychain, not app memory. Route network traffic through Network Security Config (Android) or App Transport Security (iOS) rather than raw sockets.
  • Test with the strictest policy. Enable SELinux enforcing + adb shell appops restrictions on Android, and enable Lockdown Mode + TCC reset (tccutil reset All) on iOS during QA.

The underlying lesson from both stacks is the same one we apply to RF coexistence: never trust the adjacent channel to stay quiet. Assume the neighboring app is compromised and verify that your own data remains contained even then.

Conclusion

Android and iOS both deliver robust sandboxing, but they reflect different philosophies. Android, rooted in Linux, gives developers more visibility and flexibility — you can inspect SELinux contexts, attach seccomp filters, and even run isolated processes — at the cost of relying on user judgment for permission grants. iOS, rooted in BSD MAC, trades flexibility for a simpler guarantee: nothing happens without a signed entitlement and a Seatbelt allow rule, which makes large-scale malware far harder but also limits legitimate low-level access.

For the next generation of always-sensing, always-connected devices, the trend is convergence: brokered, picker-driven access, per-app encryption categories, and hardware-rooted attestation. Whether you ship on AOSP or iOS, building with the sandbox rather than against it is the only sustainable path to both compliance and user trust.

Frequently Asked Questions

Is Android or iOS sandboxing more secure?

Both are highly secure but enforce isolation differently. iOS uses a stricter, centrally managed Seatbelt policy with no inter-app data sharing by default, reducing attack surface. Android offers more flexibility through UID isolation and SELinux but relies on user-granted permissions, increasing risk if users approve malicious requests. For most threat models, iOS provides stronger out-of-the-box containment.

Can a sandboxed app access another app's data?

No, under normal conditions. On Android each app runs under its own Linux UID with a private data directory and SELinux type enforcement. On iOS each app lives in its own container with a randomized path. Controlled sharing only occurs via brokered APIs like Android ContentProviders or iOS App Groups, both mediated and permission-checked by the OS.

What role does SELinux play in Android sandboxing?

Since Android 5.0, SELinux runs in enforcing mode as SEAndroid. It adds mandatory access control on top of DAC. Every app domain (untrusted_app) is confined by a strict policy that restricts which system services, device nodes, and files it can access, even if a process is compromised or gains UID 0 via an exploit.

Do iOS entitlements break the sandbox?

No — entitlements extend the sandbox in a controlled way. They are cryptographic capabilities signed into the provisioning profile by Apple. An app can only use resources like iCloud, Push Notifications, or Network Extensions if Apple has granted that entitlement. Without it, the Seatbelt sandbox denies access at the kernel level.

References & Standards: Bluetooth SIG · 3GPP specifications