likes
comments
collection
share

从源码角度看ContentProvider

作者站长头像
站长
· 阅读数 4

简介

本文原文在我的博客CheapTalks,欢迎大家去看看~

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的抛出并不代表者对端进程就死亡了。

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

查看大图

从源码角度看ContentProvider

核心操作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;

查看大图

从源码角度看ContentProvider

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尚未实例化,则需要在这个宿主进程中进行"安装"

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在本地尝试实例化
    if (holder == null || holder.provider == null) {
...
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if (mInitialApplication != null &&
                mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            try {
                c = context.createPackageContext(ai.packageName,
                        Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
...
        try {
            // 利用反射进行provider实例化
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
...
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            if (!mInstrumentation.onException(null, e)) {
                throw new RuntimeException(
                        "Unable to get provider " + info.name
                        + ": " + e.toString(), e);
            }
            return null;
        }
    } else {
        provider = holder.provider;
        if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                + info.name);
    }

installProvider (2)

进行引用方面的操纵;在宿主进程进行provider的缓存

IActivityManager.ContentProviderHolder retHolder;

synchronized (mProviderMap) {
    // 获取provider代理
    IBinder jBinder = provider.asBinder();
    // 如果这个provider在上一个操作刚被创建
    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;
            // 根据Uri authority name进行分类缓存
            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 (DEBUG_PROVIDER) {
                Slog.v(TAG, "installProvider: lost the race, updating ref count");
            }
...
            if (!noReleaseNeeded) {
                incProviderRefLocked(prc, stable);
                try {
                    ActivityManagerNative.getDefault().removeContentProvider(
                            holder.connection, stable);
                } catch (RemoteException e) {
                    //do nothing content provider object is dead any way
                }
            }
        } 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;
    }
}

return retHolder;

AMS.publishContentProviders

在provider宿主进程进行实例化成功之后,就需要通知AMS,告诉它不需要再等待了。此后,访问provider的应用进程的getContnetProviderImpl才真正的结束

public final void publishContentProviders(IApplicationThread caller,
                                          List<ContentProviderHolder> providers) {
...
    enforceNotIsolatedCaller("publishContentProviders");
    synchronized (this) {
       // 获取provider宿主进程
        final ProcessRecord r = getRecordForAppLocked(caller);
...

        final long origId = Binder.clearCallingIdentity();

        final int N = providers.size();
        for (int i = 0; i < N; i++) {
            ContentProviderHolder src = providers.get(i);
            if (src == null || src.info == null || src.provider == null) {
                continue;
            }
            ContentProviderRecord dst = r.pubProviders.get(src.info.name);
            if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
            if (dst != null) {
                ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                // 根据类进行缓存
                mProviderMap.putProviderByClass(comp, dst);
                String names[] = dst.info.authority.split(";");
                // 根据uri authority name进行缓存
                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--;
                    }
                }
                // 这里移除provider ANR的"定时炸弹"
                if (wasInLaunchingProviders) {
                    mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                synchronized (dst) {
                    dst.provider = src.provider;
                    dst.proc = r;
                    // 通知AMS provider已经"发布"成功
                    dst.notifyAll();
                }
                updateOomAdjLocked(r);
                maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                        src.info.authority);
            }
        }

        Binder.restoreCallingIdentity(origId);
    }
}

查看大图

从源码角度看ContentProvider

ContentProvider隐藏陷阱之联级诛杀

因为ContentProvider的设计,当provider的宿主进程死亡时,访问它的进程如果正在做CRUD,那么这个进程也会受到牵连。

进程被杀后

以下从ProcessRecord.kill为入口点进行分析

ProcessRecord.java

void kill(String reason, boolean noisy) {
    if (!killedByAm) {
...
        // 杀死进程
        Process.killProcessQuiet(pid);
        Process.killProcessGroup(uid, pid);
        if (!persistent) {
            killed = true;
            killedByAm = true;
        }
...
    }
}

当进程死亡后,将会调用当初在attachApplication时注册的死亡回调

ActivityManagerService.java

private final class AppDeathRecipient implements IBinder.DeathRecipient {
    final ProcessRecord mApp;
    final int mPid;
    final IApplicationThread mAppThread;

    AppDeathRecipient(ProcessRecord app, int pid,
                      IApplicationThread thread) {
        if (DEBUG_ALL) Slog.v(
                TAG, "New death recipient " + this
                        + " for thread " + thread.asBinder());
        mApp = app;
        mPid = pid;
        mAppThread = thread;
    }

    @Override
    public void binderDied() {
        // 进程死亡后,将会调用到里面
        synchronized (ActivityManagerService.this) {
            appDiedLocked(mApp, mPid, mAppThread, true);
        }
    }
}

final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
                         boolean fromBinderDied) {
...
        handleAppDiedLocked(app, false, true);

...
}

private final void handleAppDiedLocked(ProcessRecord app,
                                       boolean restarting, boolean allowRestart) {
    int pid = app.pid;
    boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
...
}

private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
                                                     boolean restarting, boolean allowRestart, int index) {
...
    // 删除和该进程有关的已"发布"的provider
    for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
        ContentProviderRecord cpr = app.pubProviders.valueAt(i);
        final boolean always = app.bad || !allowRestart;
        boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
        if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
...
            restart = true;
        }

        cpr.provider = null;
        cpr.proc = null;
    }
    app.pubProviders.clear();

进行provider级联诛杀

private final boolean removeDyingProviderLocked(ProcessRecord proc,
                                                ContentProviderRecord cpr, boolean always) {
    final boolean inLaunching = mLaunchingProviders.contains(cpr);

    // 如果这个provider尚正在运行
    if (!inLaunching || always) {
        synchronized (cpr) {
            cpr.launchingApp = null;
            cpr.notifyAll();
        }
        mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
        String names[] = cpr.info.authority.split(";");
        for (int j = 0; j < names.length; j++) {
            mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
        }
    }

    // 遍历这个进程所有的Connection
    for (int i = cpr.connections.size() - 1; i >= 0; i--) {
        ContentProviderConnection conn = cpr.connections.get(i);
        if (conn.waiting) {
            // If this connection is waiting for the provider, then we don't
            // need to mess with its process unless we are always removing
            // or for some reason the provider is not currently launching.
            if (inLaunching && !always) {
                continue;
            }
        }
        // 获取到这个provider的客户端
        ProcessRecord capp = conn.client;
        conn.dead = true;
        // 这个connection的stable计数大于0才会杀死其客户端
        if (conn.stableCount > 0) {
            // 如果这个app不是常驻进程且正在运行中,那么将会进行联级诛杀
            if (!capp.persistent && capp.thread != null
                    && capp.pid != 0
                    && capp.pid != MY_PID) {
                capp.kill("depends on provider "
                        + cpr.name.flattenToShortString()
                        + " in dying proc " + (proc != null ? proc.processName : "??"), true);
            }
        } else if (capp.thread != null && conn.provider.provider != null) {
            try {
                capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
            } catch (RemoteException e) {
            }
            // In the protocol here, we don't expect the client to correctly
            // clean up this connection, we'll just remove it.
            cpr.connections.remove(i);
            if (conn.client.conProviders.remove(conn)) {
                stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
            }
        }
    }

    if (inLaunching && always) {
        mLaunchingProviders.remove(cpr);
    }
    return inLaunching;
}

provider中,query操作首次是使用unstableProvider,失败一次后会使用stableProvider;其余insert, update, delete操作直接使用的是stableProvider

联级存在的意义在于保护provider客户端与服务端的数据一致性;因为插入,删除这些操作会涉及到数据更新,所以如果provider出现了异常,为了保证客户端维护的数据是正确了,只能强迫客户端进程直接死亡再重新启动恢复数据

查看大图

从源码角度看ContentProvider

releaseProvider

前面我们已经分析,acquireProvider会增加stable的引用计数,而provider服务端死亡时,如果stable计数大于0,那么provider客户端也会收到波及被杀死。那什么时候会stable的计数会减少呢,答案在releaseProvider这个方法中

触发时机

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) {
    Preconditions.checkNotNull(uri, "uri");
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
...
    try {
...
        try {
            qCursor = unstableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        } catch (DeadObjectException e) {
...
            unstableProviderDied(unstableProvider);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(mPackageName, uri, projection,
                    selection, selectionArgs, sortOrder, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }
...
        // 返回cursor,待客户端调用close后才会调用到releaseProvider
        return wrapper;
    } catch (RemoteException e) {
        return null;
    } finally {
...
    }
}

public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
    Preconditions.checkNotNull(url, "url");
    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);
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
        return createdRow;
    } catch (RemoteException e) {
        return null;
    } finally {
        // 直接在最后调用releaseProvider
        releaseProvider(provider);
    }
}

public final int update(@NonNull Uri uri, @Nullable ContentValues values,
        @Nullable String where, @Nullable String[] selectionArgs) {
    Preconditions.checkNotNull(uri, "uri");
    IContentProvider provider = acquireProvider(uri);
    if (provider == null) {
        throw new IllegalArgumentException("Unknown URI " + uri);
    }
    try {
        long startTime = SystemClock.uptimeMillis();
        int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
        return rowsUpdated;
    } catch (RemoteException e) {
        return -1;
    } finally {
        releaseProvider(provider);
    }
}

public final int delete(@NonNull Uri url, @Nullable String where,
        @Nullable String[] selectionArgs) {
    Preconditions.checkNotNull(url, "url");
    IContentProvider provider = acquireProvider(url);
    if (provider == null) {
        throw new IllegalArgumentException("Unknown URL " + url);
    }
    try {
        long startTime = SystemClock.uptimeMillis();
        int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
        long durationMillis = SystemClock.uptimeMillis() - startTime;
        maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
        return rowsDeleted;
    } catch (RemoteException e) {
        return -1;
    } finally {
        releaseProvider(provider);
    }
}

public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
        @Nullable String arg, @Nullable Bundle extras) {
    Preconditions.checkNotNull(uri, "uri");
    Preconditions.checkNotNull(method, "method");
    IContentProvider provider = acquireProvider(uri);
    if (provider == null) {
        throw new IllegalArgumentException("Unknown URI " + uri);
    }
    try {
        return provider.call(mPackageName, method, arg, extras);
    } catch (RemoteException e) {
        // Arbitrary and not worth documenting, as Activity
        // Manager will kill this process shortly anyway.
        return null;
    } finally {
        releaseProvider(provider);
    }
}

以上,除了query外,其它操作都会在最后调用releaseProvider释放provider的计数;query比较特殊,会在调用cursor的close后才调用;

这里有个特殊的方法:call,它的不同之处在于数据传输的方式。其它的query, insert, update, delete这些操作是使用binder+ashmem结合的方式进行数据传输,而call纯粹使用的是binder进行

查看大图

从源码角度看ContentProvider

App端数据结构

查看大图

从源码角度看ContentProvider

  • ProviderKey: 包含URL authority字符串
  • ProviderClientRecord: 与AMS端的ContentProviderRecord对应,主要引用了provider proxy句柄
  • ProviderRefCount: 封装了stable, unstable两种引用
  • ContentProviderHolder: 主要引用了ContentProviderConnection proxy句柄,provider proxy句柄
  • ProviderInfo: provider的抽象存储类
  • ComponentName: 组件类的抽象类

system_server端数据结构

查看大图

从源码角度看ContentProvider

  • ProviderMap: AMS端用来缓存ContentProviderRecord的对象,提供四种不同的查询方式
  • ContentProviderRecord: provider在AMS中的封装类,主要引用了provider binder proxy、进程信息
  • Association: 两个进程相互关联的抽象
  • ProcessRecord: 进程的抽象,包含了进程优先级、四大组件等等信息
  • ContentProviderConnection: 主要引用了provider客户端的进程抽象

总结

本篇博客详细分析了provider代理对象是如果被获取的,其中涉及了provider客户端,system_server的AMS,provider服务端三个进程的通信。同时也分析了contentprovider的联级诛杀原理,这块的知识点客户端开发需要格外注意,否则自己的app无故死亡了都不知道。

关于provider客户端如何同服务端使用ashmem与binder结合的方式来传输数据,因为本篇实在已经太长了,我没有细讲,以后再作分析。

关于ContentProvider的监听,这块感兴趣的可以看我的博客从源码层解析ContentService如何实现数据变化监听