Mobile Development 15 min read

How Android’s ContentProvider Works: Publishing, Resolving, and Cross‑Process Calls

The article explains Android’s ContentProvider mechanism, detailing how providers are published by the system, how the ActivityManagerService registers them, and how the ContentResolver in client processes acquires and interacts with these providers across processes using Binder IPC.

Tencent TDS Service
Tencent TDS Service
Tencent TDS Service
How Android’s ContentProvider Works: Publishing, Resolving, and Cross‑Process Calls
This article describes how ContentProvider publishers and callers are implemented at the Android framework layer.

As one of Android's four major components, ContentProvider is a key mechanism for static data transfer between processes. Its core relies on Binder, but unlike other components it also uses anonymous shared memory for large data sets to improve efficiency.

When a provider's process is not alive, other processes must start it before reading even simple data (unless multiprocess=true), which can be unfriendly to users. Because ContentProvider accesses data indirectly via a database, its performance is lower than direct DB operations, so it is generally not recommended for frequent use in user apps, though it provides a unified data‑operation standard for system‑level apps.

ContentProvider Publishing

During the first process start, handleBindApplication triggers provider installation.

if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
    }
}

If the manifest contains a <provider> entry, the system publishes it:

final ArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
    IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
    if (cph != null) {
        cph.noReleaseNeeded = true;
        results.add(cph);
    }
}
try {
    ActivityManagerNative.getDefault().publishContentProviders(
        getApplicationThread(), results);
} catch (RemoteException ex) {
}

The installProvider method (described later) creates the provider instance and returns a ContentProviderHolder.

final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();

Authority mapping is stored in mProviderMap:

for (String auth : auths) {
    final ProviderKey key = new ProviderKey(auth, userId);
    final ProviderClientRecord existing = mProviderMap.get(key);
    if (existing != null) {
    } else {
        mProviderMap.put(key, pcr);
    }
}

The ActivityManagerService (AMS) then iterates over all ContentProviderHolder objects to register them:

final int N = providers.size();
for (int i = 0; i < N; i++) {
    ContentProviderHolder src = providers.get(i);
    ...
    ContentProviderRecord dst = r.pubProviders.get(src.info.name);
    if (dst != null) {
        ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
        mProviderMap.putProviderByClass(comp, dst);
        String names[] = dst.info.authority.split(";");
        for (int j = 0; j < names.length; j++) {
            mProviderMap.putProviderByName(names[j], dst);
        }
        int launchingCount = mLaunchingProviders.size();
        int j;
        boolean wasInLaunchingProviders = false;
        for (j = 0; j < launchingCount; j++) {
            if (mLaunchingProviders.get(j) == dst) {
                mLaunchingProviders.remove(j);
                wasInLaunchingProviders = true;
                j--;
                launchingCount--;
            }
        }
        if (wasInLaunchingProviders) {
            mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
        }
        ...
    }
}

Provider maps store entries by class or authority. For singleton providers they go into mSingletonByClass / mSingletonByName; otherwise they are stored per‑user:

if (record.singleton) {
    mSingletonByClass.put(name, record);
} else {
    final int userId = UserHandle.getUserId(record.appInfo.uid);
    getProvidersByClass(userId).put(name, record);
}
if (record.singleton) {
    mSingletonByName.put(name, record);
} else {
    final int userId = UserHandle.getUserId(record.appInfo.uid);
    getProvidersByName(userId).put(name, record);
}

ContentResolver Cross‑Process Data Operations

Client processes use a unique ContentResolver instance (initialized in ContextImpl) to interact with providers:

context.getContentResolver().query(uri, ...);
public ContentResolver getContentResolver() {
    return mContentResolver;
}
mContentResolver = new ApplicationContentResolver(this, mainThread, user);

When inserting data, the resolver first acquires the remote provider via its Binder:

IContentProvider provider = acquireProvider(url);
if (provider == null) {
    throw new IllegalArgumentException("Unknown URL " + url);
}
try {
    long startTime = SystemClock.uptimeMillis();
    Uri createdRow = provider.insert(mPackageName, url, values);
    ...
    return createdRow;
} catch (RemoteException e) {
    return null;
} finally {
    releaseProvider(provider);
}

The acquisition checks that the URI scheme is content and extracts the authority:

if (!SCHEME_CONTENT.equals(uri.getScheme())) {
    return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
    return acquireProvider(mContext, auth);
}

ApplicationContentResolver forwards the request to the main thread, which contacts the ActivityManagerService:

protected IContentProvider acquireProvider(Context context, String auth) {
    return mMainThread.acquireProvider(context,
        ContentProvider.getAuthorityWithoutUserId(auth),
        resolveUserIdFromAuthority(auth), true);
}

Providers can be marked as stable or unstable . A stable provider will cause the client process to crash if the provider dies; an unstable one will instead raise a DeadObjectException that can be handled.

The ActivityThread’s acquireProvider performs three steps:

Check the process‑local cache for an existing provider.

If not cached, query AMS for the provider.

If still absent, install the provider (or start its process) via installProvider.

final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
    return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
    holder = ActivityManagerNative.getDefault().getContentProvider(
        getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
    return null;
}
holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;

When acquiring from the local cache, reference counting is updated if needed:

synchronized (mProviderMap) {
    final ProviderKey key = new ProviderKey(auth, userId);
    final ProviderClientRecord pr = mProviderMap.get(key);
    if (pr == null) {
        return null;
    }
    IContentProvider provider = pr.mProvider;
    IBinder jBinder = provider.asBinder();
    ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
    if (prc != null) {
        incProviderRefLocked(prc, stable);
    }
    return provider;
}

If the provider is not running, AMS looks it up in its ProviderMap. If the provider has not been launched, AMS either starts the existing process or spawns a new one, then schedules installation:

ContentProviderRecord cpr;
cpr = mProviderMap.getProviderByName(name, userId);
if (providerRunning) {
    if (r != null && cpr.canRunHere(r)) {
        ContentProviderHolder holder = cpr.newHolder(null);
        holder.provider = null;
        return holder;
    }
}
// provider not running
cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
if (r != null && cpr.canRunHere(r)) {
    return cpr.newHolder(null);
}
ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
    if (!proc.pubProviders.containsKey(cpi.name)) {
        proc.pubProviders.put(cpi.name, cpr);
        proc.thread.scheduleInstallProvider(cpi);
    }
} else {
    proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider",
        new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false);
}
 mProviderMap.putProviderByName(name, cpr);

The actual installation creates the provider instance (if needed) and registers it in the various maps:

private IActivityManager.ContentProviderHolder installProvider(Context context,
    IActivityManager.ContentProviderHolder holder, ProviderInfo info,
    boolean noisy, boolean noReleaseNeeded, boolean stable) {
    // implementation details described below
}

Key parameters: ContentProviderHolder: holds cached provider info; null on first launch. ProviderInfo: metadata about the provider; must not be null. noReleaseNeeded: when true, the provider is permanently installed and will not be released.

ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
    try {
        final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            return null;
        }
        localProvider.attachInfo(c, info);
    } catch (java.lang.Exception e) {
    }
} else {
    provider = holder.provider;
}

After obtaining the Binder, the system either reuses an existing ProviderClientRecord or creates a new one, updating reference counts according to the noReleaseNeeded flag:

IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
    IBinder jBinder = provider.asBinder();
    if (localProvider != null) {
        ComponentName cname = new ComponentName(info.packageName, info.name);
        ProviderClientRecord pr = mLocalProvidersByName.get(cname);
        if (pr != null) {
            provider = pr.mProvider;
        } else {
            holder = new IActivityManager.ContentProviderHolder(info);
            holder.provider = provider;
            holder.noReleaseNeeded = true;
            pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
            mLocalProviders.put(jBinder, pr);
            mLocalProvidersByName.put(cname, pr);
        }
        retHolder = pr.mHolder;
    } else {
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
            if (!noReleaseNeeded) {
                incProviderRefLocked(prc, stable);
                try {
                    ActivityManagerNative.getDefault().removeContentProvider(
                        holder.connection, stable);
                } catch (RemoteException ignored) {}
            }
        } else {
            ProviderClientRecord client = installProviderAuthoritiesLocked(
                provider, localProvider, holder);
            if (noReleaseNeeded) {
                prc = new ProviderRefCount(holder, client, 1000, 1000);
            } else {
                prc = stable
                    ? new ProviderRefCount(holder, client, 1, 0)
                    : new ProviderRefCount(holder, client, 0, 1);
            }
            mProviderRefCountMap.put(jBinder, prc);
        }
        retHolder = prc.holder;
    }
}

Through this intricate publishing and resolution process, Android enables safe, efficient cross‑process data access while managing provider lifecycles, reference counting, and stability semantics.

AndroidIPCContentProviderBinder
Tencent TDS Service
Written by

Tencent TDS Service

TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.