SoFunction
Updated on 2025-03-01

ContentProvider client handles provider logic analysis

introduction

The previous article analyzes the logic of AMS processing provider. Please be sure to read the previous article carefully, otherwise you may have a lot of doubts when reading this article.

Taking the query provider as an example, we analyze how the client handles the provider, which callsContentResolver#query()

// 
public final @Nullable Cursor query(final @ @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    (uri, "uri");
    // mWrapped of ApplicationContentResolver is null    try {
        if (mWrapped != null) {
            return (uri, projection, queryArgs, cancellationSignal);
        }
    } catch (RemoteException e) {
        return null;
    }
    // 1. Get the unstable provider    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        long startTime = ();
        // Get the interface for canceling the operation        ICancellationSignal remoteCancellationSignal = null;
        if (cancellationSignal != null) {
            ();
            remoteCancellationSignal = ();
            (remoteCancellationSignal);
        }
        try {
            // 2. Perform an action            qCursor = ((), uri, projection,
                    queryArgs, remoteCancellationSignal);
        } catch (DeadObjectException e) {
            // Handle the situation where the unstable provider process is lapsed            // Notify AMS that the provider process has lapsed            unstableProviderDied(unstableProvider);
            // Get the stable provider and try to get the data again            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = ((), uri, projection,
                    queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
        // Force query execution.  Might fail and throw a runtime exception here.
        ();
        long durationMillis = () - startTime;
        maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
        // Note that here, we will finally get the provider interface from the stable provider        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        // 3. Return data        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        // ...
    }
}

Looking at the entire provider query process, it is actually three steps

  • Get the provider.
  • Perform a query from the retrieved provider.
  • Returns the result of the query.

We noticed that there are two types of providers, unstable provider and stable provider in the code. The difference between the two is that if the provider process lapses, the client process will be killed for the stable provider, while unstable will not. We will analyze this later.

Now we need to focus on the key points and analyze how to obtain provider. The acquisition methods of unstable provider and stable provider are actually the same. This article only analyzes and obtains unstbale provider.

1. Get the provider

For the app process, the implementation class of the ContentResolver interface is ApplicationContentResolver. The operation to obtain the unstable provider will eventually be called.ApplicationContentResolver#acquireUnstableProvider()

//
class ContextImpl {
    private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return (c,
                    (auth),
                    resolveUserIdFromAuthority(auth), false);
        }
    }
}

It turns out that it was finally handed over to ActivityThread to get the provider

// 
public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    // Get it from local    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }
    ContentProviderHolder holder = null;
    // Synthesize a KEY    final ProviderKey key = getGetProviderKey(auth, userId);
    try {
        synchronized (key) {
            // 1. Get ActivityManagerService Get            holder = ().getContentProvider(
                    getApplicationThread(), (), auth, userId, stable);
            // 2. Wait for the provider to be published to complete            // holder != null means that the provider exists            // == null means that the provider is being published            // To false, it means that the provider is not installed on the client            if (holder != null &&  == null && !) {
                synchronized () {
                    // 2.1 timeout, etc. Provider release                    // The timeout is generally 20s                    (ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                    // Here, the data obtained may be empty due to the timeout                    // It can also be because the provider is released and is awakened by AMS. The holder is the data returned by AMS.                    holder = ;
                }
                // 2.2 Confirm whether it is timeout wake-up                if (holder != null &&  == null) {
                    // probably timed out
                    holder = null;
                }
            }
        }
    } 
    // ...
    // Here is a log of the failed acquisition of provider    if (holder == null) {
        if ((c).isUserUnlocked(userId)) {
            (TAG, "Failed to find provider info for " + auth);
        } else {
            (TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
        }
        return null;
    }
    // 3. Successfully get the provider from the server and install it locally    holder = installProvider(c, holder, ,
            true /*noisy*/, , stable);
    return ;
}

The process of obtaining provider on the client is roughly divided into the following steps

  • Get the provider from AMS.
  • If the provider is still in the process of publishing, then the timeout waits for its release to be completed. But waiting is time-limited, about 20s. There are two possibilities to be woken up during the waiting timeout, one is because the timeout, and the other is because the provider was successfully released, and AMS woke up the client. Therefore, it is necessary to determine which situation is. The detection condition is whether to obtain the provider binder after being awakened, that is,. See [1.1 Wait for the provider to be released
  • After successfully obtaining the provider from AMS, then "install" it locally. The command of this method is not very good. If you successfully obtain the provider from AMS, the logic here is to save the data. If AMS notifies the client, the provider can be installed in the client process. The client will create a ContentProvider object in this method and save it. This is called installation. See [1.2 Install provider

1.1 Wait for the provider to be released

From the previous article, we can see that when the provider release timeout or successfully releases, it will be calledContentProviderRecord#onProviderPublishStatusLocked(boolean status)Notify the client provider Release status。 parameterstatusIf true, it means the release is successful, if false, it means the release timeout.

// 
void onProviderPublishStatusLocked(boolean status) {
    final int numOfConns = ();
    for (int i = 0; i < numOfConns; i++) {
        // traverse all client connections waiting for the provider to publish        final ContentProviderConnection conn = (i);
        if ( &&  != null) {
            final ProcessRecord client = ;
            // Log the log of publishing timeout            if (!status) {
                // From this we can see that when status is false, it does not necessarily mean that the release timeout, and it may also be because the process has lapsed.                if (launchingApp == null) {
                    (TAG_AM, "Unable to launch app "
                            +  + "/"
                            +  + " for provider "
                            +  + ": launching app became null");
                    (
                            (),
                            ,
                            , );
                } else {
                    (TAG_AM, "Timeout waiting for provider "
                            +  + "/"
                            +  + " for provider "
                            + 
                            + " caller=" + client);
                }
            }
            // Notify the client            final IApplicationThread thread = ();
            if (thread != null) {
                try {
                    (
                            newHolder(status ? conn : null, false),
                            , , status);
                } catch (RemoteException e) {
                }
            }
        }
         = false;
    }
}

It's simple, by iterating through all client connections waiting for the provider to publish, and then notifying them through the thread of the client attach.

// 
public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
        @NonNull String authorities, int userId, boolean published) {
    final String auths[] = (";");
    for (String auth: auths) {
        final ProviderKey key = getGetProviderKey(auth, userId);
        synchronized () {
            // Save the data transmitted from the server             = holder;
            // Wake up the thread waiting for the provider            ();
        }
    }
}

After the client receives the message, it wakes up the waiting thread. Who is waiting? Is it a bit familiar here? In fact, it is the timeout waiting when obtaining the provider in the previous analysis. Some codes are as follows

// 
public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    // ...
    try {
        synchronized (key) {
            holder = ().getContentProvider(
                    getApplicationThread(), (), auth, userId, stable);
            // Wait for the provider to be published to complete            if (holder != null &&  == null && !) {
                synchronized () {
                    // Timeout waiting                    (ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                    // Here, the data obtained may be empty due to the timeout                    // It can also be because the provider is released and is awakened by AMS. The holder is the data returned by AMS.                    holder = ;
                }
                // Confirm whether it is timeout wake-up                if (holder != null &&  == null) {
                    // probably timed out
                    holder = null;
                }
            }
        }
    } 
    // ...
}

Timeout waiting for the provider to be published, if it is awakened, get it again, because if published successfully,It is not empty because it is the provider binder, otherwise it will wake up timeout.

1.2 Install provider

If the client successfully obtains the provider from AMS, it will install it. In fact, the operation here is to save the data. In fact, the most important thing is to save the provider interface and also save the provider binder.

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // Successfully obtaining the provider from AMS, both of the following conditions are not valid    if (holder == null ||  == null) {
        // ...
    } else {
        // Getting the provider interface is actually to get the provider binder        provider = ;
    }
    ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        // Get binder object from provider interface        IBinder jBinder = ();
        if (localProvider != null) {
            // ...
        } else {
            ProviderRefCount prc = (jBinder);
            if (prc != null) {
                // ...
            } else {
                // 1. Create a provider record and save it                ProviderClientRecord client = installProviderAuthoritiesLocked(
                        provider, localProvider, holder);
                // The provider of persistent app does not need to be released                if (noReleaseNeeded) {
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else {
                    prc = stable
                            ? new ProviderRefCount(holder, client, 1, 0)
                            : new ProviderRefCount(holder, client, 0, 1);
                }
                // 2. Save the provider count                (jBinder, prc);
            }
            retHolder = ;
        }
    }
    return retHolder;
}
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
        ContentProvider localProvider, ContentProviderHolder holder) {
    final String auths[] = (";");
    final int userId = ();
    // ...
    // Create a provider record    final ProviderClientRecord pcr = new ProviderClientRecord(
            auths, provider, localProvider, holder);
    // A ContentProvider can declare multiple authorities    for (String auth : auths) {
        final ProviderKey key = new ProviderKey(auth, userId);
        // mProviderMap Save        final ProviderClientRecord existing = (key);
        if (existing != null) {
            (TAG, "Content provider " + 
                    + " already published as " + auth);
        } else {
            (key, pcr);
        }
    }
    return pcr;
}

It's very simple, it's just to use two data structures to save data.

2. Provider implements multi-process instances

We always mentioned vaguely before that provider can be installed in the client process. So under what conditions can provider be installed in the client process? As mentioned in the analysis of the previous article, some code is now displayed.

// 
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    // ...
    synchronized (mService) {
        // Get the client's process instance        ProcessRecord r = null;
        if (caller != null) {
            r = (caller);
            if (r == null) {
                throw new SecurityException("Unable to find app for caller " + caller
                        + " (p) when getting content provider "
                        + name);
            }
        }
        // ...
        // provider is running        if (providerRunning) {
            cpi = ;
            if (r != null && (r)) {
                // This provider has been published or is in the process
                // of being published...  but it is also allowed to run
                // in the caller's process, so don't make a connection
                // and just let the caller instantiate its own instance.
                ContentProviderHolder holder = (null, true);
                // don't give caller the provider object, it needs to make its own.
                 = null;
                return holder;
            }
            // ...
        }
        // provider is not running        if (!providerRunning) {
            // ...
            if (r != null && (r)) {
                // If this is a multiprocess provider, then just return its
                // info and allow the caller to instantiate it.  Only do
                // this if the provider is the same user as the caller's
                // process, or can run as root (so can be in any process).
                return (null, true);
            }
            // ...
        }
        // ...
    }
    // ...
}

You can see that no matter whether the provider is running or not, there is a chance to create a provider instance in the client process, and this opportunity isContentProviderRecord#canRunHere()

The provider has been run and can still be run in the client process, that is, create a ContentProvider instance in the client process. What is the purpose of this design?

public boolean canRunHere(ProcessRecord app) {
    // info is the provider information, that is, the provider information declared in AndroidManifest    // Provider can run conditions in the client process    // 1. The uid of the app where the provider is located is the same as the uid of the client app    // 2. Provider supports multi-process or the process name of the provider is the same as the process name of the client app    return ( || ())
            && uid == ;
}

The conditions here need to be clearly seen. First of all, the app where the provider is located is the same as the uid of the client app, but in fact the following thing is the same

<manifest 
    android:sharedUserId="">

Then, the provider also needs to support multiple processes, which is actually the following thing

<provider 
    android:multiprocess="true"/>

If the provider does not support multi-process, as long as the process name of the provider is the same as the process name of the client app, the provider can also run in the client process. So what is the name of the provider process? Provider can declare its own process name, as follows

<provider
    android:process=""
   />

If the provider does not declare its own process name, then the provider process name is taken from the app's process name.

Now how the provider runs in the client process, will you play it? If you know how to play, then continue to see how the client installs the provider. This time, it's really installed the provider.

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // At this time == null is true    if (holder == null ||  == null) {
        // ...
        try {
            final  cl = ();
            LoadedApk packageInfo = peekPackageInfo(, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            // 1. Create a ContentProvider object through reflection            localProvider = ()
                    .instantiateProvider(cl, );
            // Getting the provider interface is actually to get the provider binder            provider = ();
            if (provider == null) {
                return null;
            }
            // 2. Save the provider information for the ContentProvider object and call ContentProvider#onCreate()            (c, info);
        } catch ( e) {
            // ...
        }
    } else {
        // ...
    }
    ContentProviderHolder retHolder;
    synchronized (mProviderMap) {
        if (DEBUG_PROVIDER) (TAG, "Checking to add " + provider
                + " / " + );
        IBinder jBinder = ();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(, );
            ProviderClientRecord pr = (cname);
            if (pr != null) {
                // ...
            } else {
                // Create a local ContentProviderHolder                holder = new ContentProviderHolder(info);
                // Save the provider binder                 = provider;
                // Locally installed provider does not need to be released                 = true;
                // 3. Create a provider record and save it                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                (jBinder, pr);
                (cname, pr);
            }
            retHolder = ;
        } else {
            // ...
        }
    }
    return retHolder;
}

In fact, this part of the code has been analyzed in the previous article. Here is a brief introduction to the process.

  • The client creates the ContentProvider object itself, then saves the provider information and calls itContentProvider#onCreate()method.
  • Create a provider record, that is, a ContentProviderRecord object, and then save it with a data structure.

3. The difference between the two providers

We mentioned the difference between unstable provider and stable provider. Now we use code to explain the difference between the two.

Assume that we passActivityManager#forceStopPackage()Let's kill the provider process, the call in AMS is as follows

public void forceStopPackage(final String packageName, int userId) {
    // ...
    try {
        IPackageManager pm = ();
        synchronized(this) {
            int[] users = userId == UserHandle.USER_ALL
                    ? () : new int[] { userId };
            for (int user : users) {
                // ...
                if ((user, 0)) {
                    // Kill the process                    forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    // Send broadcast                    finishForceStopPackageLocked(packageName, pkgUid);
                }
            }
        }
    } finally {
        (callingId);
    }
}

Finally, the following code is called

final boolean forceStopPackageLocked(String packageName, int appId,
        boolean callerWillRestart, boolean purgeCache, boolean doit,
        boolean evenPersistent, boolean uninstalling, int userId, String reason) {
    // ...
    // Get all providers of the app    ArrayList&lt;ContentProviderRecord&gt; providers = new ArrayList&lt;&gt;();
    if (().collectPackageProvidersLocked(packageName, null, doit,
            evenPersistent, userId, providers)) {
        if (!doit) {
            return true;
        }
        didSomething = true;
    }
    // Remove provider    for (i = () - 1; i &gt;= 0; i--) {
        (null, (i), true);
    }
    // ...
}

No surprise, finallyContentProviderHelperTo remove the provider

boolean removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr,
        boolean always) {
    // ...
    for (int i = () - 1; i &gt;= 0; i--) {
        ContentProviderConnection conn = (i);
        // ...
        ProcessRecord capp = ;
        final IApplicationThread thread = ();
         = true;
        // 1. If there is a stable provider client        if (() &gt; 0) {
            final int pid = ();
            // Note that you must exclude the persistent app process and the system_server process            if (!() &amp;&amp; thread != null
                    &amp;&amp; pid != 0 &amp;&amp; pid != ActivityManagerService.MY_PID) {
                // Kill the client process                (
                        "depends on provider " + ()
                        + " in dying proc " + (proc != null ?  : "??")
                        + " (adj " + (proc != null ? () : "??") + ")",
                        ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                        ApplicationExitInfo.SUBREASON_UNKNOWN,
                        true);
            }
        } 
        // 2. If only unstable provider client        else if (thread != null &amp;&amp;  != null) {
            try {
                // Notify the client to remove data                (());
            } catch (RemoteException e) {
            }
            // In the protocol here, we don't expect the client to correctly
            // clean up this connection, we'll just remove it.
            (i);
            if ((conn)) {
                (, ,
                        , , , );
            }
        }
    }
    // ...
}

I saw it. For the stable provider, if the provider process fails, the client will also be implicated and killed.

For unstable provider, if the provider process fails, the client will just remove the saved data and will not be killed.

Finally, let's take a look at the two provider codes when obtaining the provider at the beginning of the article

// 
public final @Nullable Cursor query(final @ @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    // ...
    // Get unstable provider    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        // ...
        // Note that the stable provider obtained here and returns        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        stableProvider = null;
        qCursor = null;
        // Return data        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
        // ...
    }
}

We can see that the query is using an unstable provider, but the returned result Curosr uses a stable provider. What does this mean? It shows that before Cursor is closed, as long as the provider process lapses, the client will be implicated and killed.

Finish

The above is the detailed content of the ContentProvider client processing provider logic analysis. For more information about ContentProvider client provider, please follow my other related articles!