从源码角度看 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.