Android treats every app as a separate user on the device. Each app gets its own user ID and its own private data directory. That design is called sandboxing. It means that one app cannot read the private files of another app by default. It also means that a fault in one app is less likely to harm another app.
Sandboxing works through several layers at once. The Linux kernel enforces user separation. Android permissions control access to sensitive features such as location and microphone. SELinux policies add another layer of rules that limit what a process can do even when it runs as the same user. Together those layers form a strong boundary around each app.
This guide explains how sandboxing works and how to test it with your own apps. Testing your own apps is the safe way to learn. Do not try to break out of the sandbox of an app that you do not own. The goal is to understand the protection so you can build apps that respect it and work well inside it.
User IDs and private data directories
When Android installs an app it assigns a unique Linux user ID to that app. The app process runs under that user ID. Files in the app private directory belong to that user ID and other apps cannot read them. That simple rule protects notes and tokens and settings from other apps on the same device.
Shared storage works differently. Files on shared storage may be visible to other apps depending on permissions and scoped storage rules. Developers should not store sensitive data on shared storage. Use the private directory for tokens and personal data. Use shared storage only for files that the user intends to share such as photos that they export.
Test the boundary with two apps that you own. Write a file to the private directory of the first app. Try to read that file from the second app using a normal file path. The read should fail. That failure is the sandbox working as intended. A success would signal a serious problem in your test setup or in your understanding of the path.
Permissions and the principle of least privilege
Permissions are the second major layer. An app must declare the permissions that it needs and the user must grant sensitive permissions. A permission for location does not give access to contacts. A permission for camera does not give access to files. Each permission is separate and the app should request only what it truly needs.
The principle of least privilege means asking for the minimum set of permissions for a feature. A photo app needs camera and media access for the photos that the user picks. It does not need location for every photo unless location tagging is a real feature. Fewer permissions mean a smaller risk when a bug or a library misbehaves.
Test permission behaviour by revoking a permission after granting it. Open the app and use the feature that needed that permission. The app should handle the denial gracefully with a clear message. An app that crashes on a revoked permission has a bug in its permission handling. Fix that bug before release because users revoke permissions often.
SELinux and process isolation in practice
SELinux adds mandatory access rules on top of normal user separation. A process may run as the app user and still be denied access to a system file by SELinux policy. Those policies are written by the device maker and by Android itself. They limit damage when an app process is compromised. A compromised app still faces SELinux rules that block many system actions.
Process isolation also matters for native code. A native library runs inside the app process and shares its user ID. A bug in native code can crash the app but it should not escape the sandbox on its own. Developers should still treat native code with care because it can read app private data. Keep native libraries updated and avoid loading untrusted native code.
App components add another boundary. Activities and services and receivers can be exported or kept private. An exported component can be called by other apps. A private component cannot. Review the exported flag for every component in your manifest. An exported component that you did not intend to share can become an unexpected entry point into your app.
Testing sandbox isolation step by step
Build a test plan with clear cases for files and permissions and components. Test file isolation with two apps that you own. Test permission revocation for each sensitive permission that your app requests. Test exported components by calling them from a second test app. Record the result for each case in a simple table in your test notes.
Use a debug build for early tests and a release build for a final check. A debug build may have different flags that change behaviour. A release build shows the real user experience. Compare the results and investigate any difference. A difference between debug and release often points to a manifest flag or a build setting.
Test on a stock device with a recent Android version. Older versions had different storage rules and different permission models. A test on an old version alone can hide a problem that appears on a new version. A test on a new version alone can hide a problem for users who still run an older version. Cover the range that your app supports.
- Test file isolation with two apps that you own.
- Revoke each sensitive permission and check the app response.
- Call exported components from a second test app.
- Repeat the final check on a release build.
Code you can adapt
// Check private file access from a second test app
// This code should fail when run from a different app UID
try {
FileInputStream in = new FileInputStream(
"/data/data/com.example.firstapp/files/secret.txt");
Log.d("SandboxTest", "Unexpected read success");
in.close();
} catch (Exception e) {
Log.d("SandboxTest", "Read blocked as expected: " + e.getMessage());
}
The snippet above is an original example written for this guide. It shows a sandbox test that expects a read from another app private directory to fail.
A blocked read is a good result in this test. It proves that user separation works for your test apps. Do not try to work around a blocked read with a trick that you found online. A workaround that breaks the sandbox would put user data at risk. Respect the boundary and design your app to share data through proper channels such as a content provider with clear permissions.
Proper sharing channels include content providers and the system share sheet and scoped media access. Those channels let the user control what leaves an app. They also let Android audit access in a clear way. Use those channels when your app truly needs to share data with another app.
How this topic connects to nearby work
Sandbox isolation and device attestation together shape trust and the latter is covered by Play Integrity hardware attestation.
Data inside the sandbox still needs encryption and the method is described in the guide to encrypted local storage using Android Keystore.
Sandbox tests fit into the wider routine presented in the guide to application security audits.
Putting it into practice
Work profiles add a second sandbox layer that developers often forget. A work profile runs apps under a separate user with its own data and its own policies. An app in a work profile cannot read data from the personal profile by default. Test your app in a work profile when your users include enterprise users. A feature that assumes personal storage will fail in a work profile in a way that a normal test will not reveal.
Clipboard sharing is another sandbox boundary that deserves a test. Android restricts clipboard access for background apps on newer versions. An app that reads the clipboard without a clear user action may receive empty data. Test clipboard behaviour with the app in the foreground and after it returns from the background. A feature that depends on clipboard data should explain to the user when access is restricted by the system.
A final sandbox check should test data sharing through the system share sheet. Share a file from your app to a second test app through the share sheet and confirm that only the shared file becomes available. Check that private files remain inaccessible after sharing. That test proves that the sharing channel works as intended without opening a wider hole. Record the shared file name and the result in your test notes. A sharing test completes the sandbox picture that file isolation tests alone cannot show.
Common mistakes
A common mistake is to store a token on shared storage for convenience. Shared storage is not private and other apps may read it. Move tokens to the private directory or to encrypted storage with a proper key. Convenience is not a good reason to weaken data protection.
Another mistake is to export a component by accident. A component without an explicit exported value may become exported when it has an intent filter. Set the exported value explicitly for every component. Review the merged manifest after a build to confirm the final values.
A third mistake is to request permissions that the app does not need. Extra permissions increase risk and they reduce user trust. Audit permissions before each release and remove any permission that has no clear feature behind it. A smaller permission list is easier to explain to users.
Frequently asked questions
Can one Android app read another app private files?
No by default. Each app runs under its own user ID and private files belong to that user. A second app cannot read those files through a normal path. Shared storage and proper sharing channels are the intended ways to share data.
Does sandboxing protect an app from a malicious app completely?
Sandboxing greatly reduces risk but it is not magic. A malicious app can still trick a user into granting a permission or sharing a file. Developers should still validate input and keep libraries updated. Defence in depth matters more than any single layer.
Why does my app work in debug but fail in release for storage?
Debug and release builds can differ in manifest flags and signing and storage settings. Check the merged manifest and the storage permission model for the Android version that you test. A release check on a real device often reveals the difference.
Conclusion
Sandboxing protects users by giving each app its own identity and its own private space. Permissions and SELinux and component rules add layers around that core idea. Developers who understand those layers build apps that are safer and easier to trust. Testing isolation with your own apps turns theory into practical skill.
Your concrete next step is to run the two app file test from this guide today on a test device. Confirm that the second app cannot read the first app private file. Record that result as the baseline for your sandbox testing.


