从源码角度看 ContentProvider

简介

本专栏目前暂时免费,大家能打赏鼓励下最好不过了,哈哈~

ContentProvider相信大家都耳熟能详了,在安卓系统越来越注重系统安全的前提下,不知道大家好不好奇provider是如何在Android层层安全措施之下使进程之间实现数据增删改查操作的。仔细想来,我们大概能猜到,安卓进程间通信惯用的"伎俩"是binder call,ContentProvider的实现很有可能就使用AMS当作provider的客户端与服务端的中间人。本篇文章,我将详细分析客户端app是如何请求到在其他应用进程运行的ContentProvider的,文章最后会附带着这期间AMS和APP两端涉及到的数据结构。

由于篇幅有限,ContentProvider涉及到的其它知识我没有细讲。例如,数据的query很有可能涉及到大量的数据,而安卓的binder同步缓冲区也才1016K这么大,所以provider的查找操作实际上是混合了ashmem与binder这两种跨进程通信技术,其中binder的主要作用是传送ashmem的fd;又如,应用开发常常使用到的ContentObserver,这块的分析可以在我以前写的ContentService的分析找到。

从CRUD之query说起

一般我们写APP时,是通过ContentResolver来进行CRUD调用的。而这个操作一般是通过context.getContentResolver().xxx这样的调用链过来的。Context这块是个装饰者模式,真实的执行者是ContextImp, 这块我不多言了,我们从ContextImpl这块的源码开始分析。

ContextImpl.java

  • private final ApplicationContentResolver mContentResolver;
  • @Override
  • public ContentResolver getContentResolver() {
  • return mContentResolver;
  • }
  • private ContextImpl(ContextImpl container, ActivityThread mainThread,
  • LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
  • Display display, Configuration overrideConfiguration, int createDisplayWithId) {
  • ...
  • mContentResolver = new ApplicationContentResolver(this, mainThread, user);
  • }

可以看到,最终ApplicationContentResolver负责了我们的APP ContentProvider CRUD这块的操作。

ContentResolver.java

  • public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
  • @Nullable String selection, @Nullable String[] selectionArgs,
  • @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
  • ...
  • // 获取一个unstableProvider,这个stable与unstable的概念在于AMS中会维护unstable, stable这两个计数
  • // 如果当stable的计数大于0,当ContentProvider服务端进程死亡时,那么使用这个服务的客户端进程也会受牵连被杀死
  • // 这里query先尝试使用unstableProvider,表示AMS那边只会增加unstable的计数,客户端不会收到联级诛杀的牵连
  • IContentProvider unstableProvider = acquireUnstableProvider(uri);
  • if (unstableProvider == null) {
  • return null;
  • }
  • IContentProvider stableProvider = null;
  • Cursor qCursor = null;
  • try {
  • long startTime = SystemClock.uptimeMillis();
  • ...
  • try {
  • // binder call到服务端,返回了一个cursor对象
  • // 当调用cursor.close时,会调用调releaseProvider来释放ContentProvider服务端与客户端之间的引用
  • qCursor = unstableProvider.query(mPackageName, uri, projection,
  • selection, selectionArgs, sortOrder, remoteCancellationSignal);
  • } catch (DeadObjectException e) {
  • // The remote process has died... but we only hold an unstable
  • // reference though, so we might recover!!! Let's try!!!!
  • // This is exciting!!1!!1!!!!1
  • // 第一次使用unstable尝试,服务端进程可能死亡了抛了异常
  • // 先释放unstableProvider相关的引用
  • unstableProviderDied(unstableProvider);
  • // 第二次进行尝试时,将使用stableProvider
  • stableProvider = acquireProvider(uri);
  • if (stableProvider == null) {
  • return null;
  • }
  • // 注意,失败一次后将使用stableProvider,这次如果服务端进程被杀
  • // 并且cursor还没有调用close之前,那么客户端的进程会受到牵连也被杀死
  • qCursor = stableProvider.query(mPackageName, uri, projection,
  • selection, selectionArgs, sortOrder, remoteCancellationSignal);
  • }
  • if (qCursor == null) {
  • return null;
  • }
  • ...
  • // 装饰一下
  • CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
  • stableProvider != null ? stableProvider : acquireProvider(uri));
  • stableProvider = null;
  • qCursor = null;
  • return wrapper;
  • } catch (RemoteException e) {
  • // Arbitrary and not worth documenting, as Activity
  • // Manager will kill this process shortly anyway.
  • return null;
  • } finally {
  • // 如果不为空才close
  • // 上面的操作如果成功了,qCursor是不会是空的,所以这个关闭操作交给了APP端来做
  • if (qCursor != null) {
  • qCursor.close();
  • }
  • ...
  • if (unstableProvider != null) {
  • releaseUnstableProvider(unstableProvider);
  • }
  • if (stableProvider != null) {
  • releaseProvider(stableProvider);
  • }
  • }
  • }

有一点需要提一下,当binder call时抛出DeadObjectException时,不一定是对端进程死亡了,有可能是对端binder缓冲区被占满之类的异常;这块之前问过Google的工程师,他们貌似有细化这个DeadObjectException的计划,不过现在来看DeadObjectException的抛出并不代表者对端进程就死亡了。

代码注释很详细,大体流程可以看看图。

查看大图

核心操作acquireProvider

客户端操作

在ContentResolver中调用acquireStableProvider和acquireUnstableProvider之后都会调用到ApplicationContextResolver这个子类中,之后再调用ActivityThread的acquireProvider的方法,区别stable与unstable的Provider在于这个stable字段,若是true则是获取stableProvider,反之是获取unstableProvider。咱们看代码:

ApplicationContentResolver.java

  • private static final class ApplicationContentResolver extends ContentResolver {
  • private final ActivityThread mMainThread;
  • private final UserHandle mUser;
  • // stableProvider
  • @Override
  • protected IContentProvider acquireProvider(Context context, String auth) {
  • return mMainThread.acquireProvider(context,
  • ContentProvider.getAuthorityWithoutUserId(auth),
  • resolveUserIdFromAuthority(auth), true);
  • }
  • // unstableProvider
  • @Override
  • protected IContentProvider acquireUnstableProvider(Context c, String auth) {
  • return mMainThread.acquireProvider(c,
  • ContentProvider.getAuthorityWithoutUserId(auth),
  • resolveUserIdFromAuthority(auth), false);
  • }
  • ...

ApplicationContentResolver其实是ActivityThread的内部类,这里为了方便看,这两块的代码还是分开分析吧

ActivityThread.java

  • public final IContentProvider acquireProvider(
  • Context c, String auth, int userId, boolean stable) {
  • // 现在本地寻找provider,如果没有的话才像AMS去请求
  • final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
  • if (provider != null) {
  • return provider;
  • }
  • ...
  • IActivityManager.ContentProviderHolder holder = null;
  • try {
  • // 向AMS请求ContentProvider,这块是核心方法
  • holder = ActivityManagerNative.getDefault().getContentProvider(
  • getApplicationThread(), auth, userId, stable);
  • } catch (RemoteException ex) {
  • }
  • if (holder == null) {
  • Slog.e(TAG, "Failed to find provider info for " + auth);
  • return null;
  • }
  • // "安装"provider,说白了就是新建实例,增减引用这类操作
  • // 这块的代码放到后面的scheduleInstallProvider再分析
  • holder = installProvider(c, holder, holder.info,
  • true /*noisy*/, holder.noReleaseNeeded, stable);
  • return holder.provider;
  • }

AMS.getContentProviderImpl (1)

ActivityThread binder call 到AMS之后,紧接着就会调用getContentProviderImpl,这个方法比较大,分拆进行分析

ActivityManagerService.java

  • private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
  • String name, IBinder token, boolean stable, int userId) {
  • ContentProviderRecord cpr;
  • ContentProviderConnection conn = null;
  • ProviderInfo cpi = null;
  • synchronized (this) {
  • long startTime = SystemClock.elapsedRealtime();
  • // 获取调用者的ProcessRecord对象
  • ProcessRecord r = null;
  • if (caller != null) {
  • r = getRecordForAppLocked(caller);
  • ...
  • }
  • boolean checkCrossUser = true;
  • ...
  • // 通过uri authority name来获取ContentProviderRecord
  • cpr = mProviderMap.getProviderByName(name, userId);
  • ...
  • if (cpr == null && userId != UserHandle.USER_OWNER) {
  • // 检查userId=0是否已存有ContentProviderRecord
  • cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
  • if (cpr != null) {
  • cpi = cpr.info;
  • if (isSingleton(cpi.processName, cpi.applicationInfo,
  • cpi.name, cpi.flags)
  • && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
  • userId = UserHandle.USER_OWNER;
  • checkCrossUser = false;
  • } else {
  • cpr = null;
  • cpi = null;
  • }
  • }
  • }

这一段AMS会在providerMap中寻找正在运行的provider。如果找到,那么说明这个provider已经被启动了,随后增加引用即可;如果没有找到,那么就需要调用installProvider在provider客户端中进行provider的"安装",随后AMS将等待这个客户端publishProvider。

AMS.getContentProviderImpl (2)

  • // 根据先前的查询,判断当前的provider是否正在运行
  • boolean providerRunning = cpr != null;
  • if (providerRunning) {
  • cpi = cpr.info;
  • String msg;
  • ...
  • // 如果provider能够在客户端进行直接运行,那么在这里就返回provider给客户端
  • if (r != null && cpr.canRunHere(r)) {
  • ContentProviderHolder holder = cpr.newHolder(null);
  • holder.provider = null;
  • return holder;
  • }
  • final long origId = Binder.clearCallingIdentity();
  • ...
  • // 增加引用
  • conn = incProviderCountLocked(r, cpr, token, stable);
  • // 如何stable和unstable的总引用计数为1,那么更新LruProcess列表
  • if (conn != null && (conn.stableCount + conn.unstableCount) == 1) {
  • if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
  • ...
  • updateLruProcessLocked(cpr.proc, false, null);
  • checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
  • }
  • }
  • if (cpr.proc != null) {
  • ...
  • // 更新provider进程的adj
  • boolean success = updateOomAdjLocked(cpr.proc);
  • maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
  • ...
  • if (!success) {
  • ...
  • // 如果不成功,那么减少引用计数并杀死provider进程
  • boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
  • checkTime(startTime, "getContentProviderImpl: before appDied");
  • appDiedLocked(cpr.proc);
  • checkTime(startTime, "getContentProviderImpl: after appDied");
  • if (!lastRef) {
  • // This wasn't the last ref our process had on
  • // the provider... we have now been killed, bail.
  • return null;
  • }
  • providerRunning = false;
  • conn = null;
  • }
  • }
  • Binder.restoreCallingIdentity(origId);
  • }

AMS.getContentProviderImpl (3)

  • boolean singleton;
  • // 如果provider没有正在运行
  • if (!providerRunning) {
  • try {
  • checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
  • // 获取ProviderInfo
  • cpi = AppGlobals.getPackageManager().
  • resolveContentProvider(name,
  • STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
  • checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
  • } catch (RemoteException ex) {
  • }
  • // 如果为空,则说明没有找到这个provider,直接返回空给客户端
  • if (cpi == null) {
  • return null;
  • }
  • // Provider是否为单例
  • singleton = isSingleton(cpi.processName, cpi.applicationInfo,
  • cpi.name, cpi.flags)
  • && isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
  • if (singleton) {
  • userId = UserHandle.USER_OWNER;
  • }
  • cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
  • ...
  • // 通过ComponentName获取providerMap中的cpr
  • ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
  • cpr = mProviderMap.getProviderByClass(comp, userId);
  • ...
  • // 该provider是否是第一次被创建
  • final boolean firstClass = cpr == null;
  • if (firstClass) {
  • final long ident = Binder.clearCallingIdentity();
  • try {
  • ApplicationInfo ai =
  • AppGlobals.getPackageManager().
  • getApplicationInfo(
  • cpi.applicationInfo.packageName,
  • STOCK_PM_FLAGS, userId);
  • ...
  • ai = getAppInfoForUser(ai, userId);
  • // 创建一个cpr
  • cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
  • } catch (RemoteException ex) {
  • // pm is in same process, this will never happen.
  • } finally {
  • Binder.restoreCallingIdentity(ident);
  • }
  • }
  • ...
  • if (r != null && cpr.canRunHere(r)) {
  • return cpr.newHolder(null);
  • }
  • ...
  • final int N = mLaunchingProviders.size();
  • int i;
  • for (i = 0; i < N; i++) {
  • if (mLaunchingProviders.get(i) == cpr) {
  • break;
  • }
  • }
  • // provider还没有被运行
  • if (i >= N) {
  • final long origId = Binder.clearCallingIdentity();
  • try {
  • // Content provider is now in use, its package can't be stopped.
  • try {
  • checkTime(startTime, "getContentProviderImpl: before set stopped state");
  • AppGlobals.getPackageManager().setPackageStoppedState(
  • cpr.appInfo.packageName, false, userId);
  • checkTime(startTime, "getContentProviderImpl: after set stopped state");
  • } catch (RemoteException e) {
  • } catch (IllegalArgumentException e) {
  • Slog.w(TAG, "Failed trying to unstop package "
  • + cpr.appInfo.packageName + ": " + e);
  • }
  • // 获取到运行provider的进程
  • ProcessRecord proc = getProcessRecordLocked(
  • cpi.processName, cpr.appInfo.uid, false);
  • // 如果这个进程已经启动,那么binder call给这个进程,创建provider
  • if (proc != null && proc.thread != null) {
  • if (!proc.pubProviders.containsKey(cpi.name)) {
  • checkTime(startTime, "getContentProviderImpl: scheduling install");
  • proc.pubProviders.put(cpi.name, cpr);
  • try {
  • proc.thread.scheduleInstallProvider(cpi);
  • } catch (RemoteException e) {
  • }
  • }
  • } else {
  • // 如果进程没有启动,那么就启动这个进程
  • // 需要说的一点是,进程启动完毕后,创建provider的操作将会在ActivityThread初始化时进行
  • proc = startProcessLocked(cpi.processName,
  • cpr.appInfo, false, 0, "content provider",
  • new ComponentName(cpi.applicationInfo.packageName,
  • cpi.name), false, false, false);
  • // 进程没有创建成功,直接返回空给客户端
  • if (proc == null) {
  • Slog.w(TAG, "Unable to launch app "
  • + cpi.applicationInfo.packageName + "/"
  • + cpi.applicationInfo.uid + " for provider "
  • + name + ": process is bad");
  • return null;
  • }
  • }
  • cpr.launchingApp = proc;
  • mLaunchingProviders.add(cpr);
  • } finally {
  • Binder.restoreCallingIdentity(origId);
  • }
  • }
  • ...
  • // 如果第一次创建这个provider的实例,在providerMap中进行缓存
  • if (firstClass) {
  • mProviderMap.putProviderByClass(comp, cpr);
  • }
  • mProviderMap.putProviderByName(name, cpr);
  • // 增加引用
  • conn = incProviderCountLocked(r, cpr, token, stable);
  • if (conn != null) {
  • conn.waiting = true;
  • }
  • }
  • }

AMS.getContentProviderImpl (4)

此时getContentProviderImpl的分析已经接近尾声。我们看到,如果provider此时尚未在其进程中被创建,那么AMS将会对这个provider进行实例化,也就是"publish"发布。AMS会在这里等待APP进程的完成,随后才会返回。

  • synchronized (cpr) {
  • while (cpr.provider == null) {
  • if (cpr.launchingApp == null) {
  • ...
  • return null;
  • }
  • try {
  • ...
  • if (conn != null) {
  • conn.waiting = true;
  • }
  • // 等待provider"发布"完成
  • cpr.wait();
  • } catch (InterruptedException ex) {
  • } finally {
  • if (conn != null) {
  • conn.waiting = false;
  • }
  • }
  • }
  • }
  • return cpr != null ? cpr.newHolder(conn) : null;

查看大图

scheduleInstallProvider

应用provider的实例化有两个入口,一个是当进程已经存在时,AMS binder call到APP实例化某个provider;一个是当进程与Application互相绑定时,批量对provider进行安装。

ActivityThread.java

  • private void installContentProviders(
  • Context context, List<ProviderInfo> providers) {
  • final ArrayList<IActivityManager.ContentProviderHolder> results =
  • new ArrayList<IActivityManager.ContentProviderHolder>();
  • for (ProviderInfo cpi : providers) {
  • ...
  • // 对provider进行实例化
  • IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
  • false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
  • if (cph != null) {
  • cph.noReleaseNeeded = true;
  • results.add(cph);
  • }
  • }
  • try {
  • // 完成实例化,通知AMS,进行provider"发布"
  • ActivityManagerNative.getDefault().publishContentProviders(
  • getApplicationThread(), results);
  • } catch (RemoteException ex) {
  • }
  • }

installProvider (1)

如果provider尚未实例化,则需要在这个宿主进程中进行"安装"

```java
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 如果向AMS.getContentProviderImpl返回NULL或者需要app"安装"provider,就会对provider在本地尝试实例化

top Created with Sketch.