Because Android enforces a strict Application Sandbox, every app runs under its own Linux User ID (UID). App A cannot simply read a SQLite database file located in App B's private /data/data/ directory; the Linux kernel will block it.
To share data securely between apps, Android provides the ContentProvider component.
The Content URI Structure
A ContentProvider uses a standard Uniform Resource Identifier (URI) to identify data. The URI acts like a REST API endpoint but for local IPC.
Format: content://authority/path/id
- Scheme: Always
content:// - Authority: A unique string identifying the specific provider (e.g.,
com.android.contacts) - Path: The type of data being requested (e.g.,
peopleorphones) - ID: (Optional) The specific row ID to query (e.g.,
content://com.android.contacts/people/5)
ContentResolver vs. ContentProvider
The system is strictly split into a client-server model.
- The Client (
ContentResolver): The app requesting data uses aContentResolverobject. It acts as the client-side proxy. You callresolver.query(), passing the URI. - The Server (
ContentProvider): The app hosting the data implements a subclass ofContentProvider. It overrides thequery(),insert(),update(), anddelete()methods.
When the client queries a URI, the Android ActivityManagerService (AMS) intercepts the call, finds the app hosting that URI, wakes the app up if it is asleep, and routes the query across process boundaries via Binder IPC.
// Example of querying the Contacts ContentProvider
Uri contactsUri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME };
Cursor cursor = getContentResolver().query(contactsUri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Log.d("Contacts", "Found name: " + name);
cursor.close();
}
Security and Permissions
ContentProviders are highly secure. By default, a provider is not exported (not accessible to other apps).
If you set android:exported="true" in the manifest, you should protect the data by defining read and write permissions. If the querying app does not hold the exact permission string required by the provider, the system throws a SecurityException.
FileProvider
While ContentProvider is excellent for structured SQLite data, sharing massive files (like sending a photo to an image editor) requires a specialized subclass called FileProvider.
Instead of trying to pass a 5MB image over a Binder transaction (which would instantly crash due to the 1MB Binder transaction limit), FileProvider grants the receiving app temporary read access to a specific file on the disk by generating a unique content:// URI and passing a raw file descriptor over Binder.