Android源码分析 - Framework层的ContentProvider全解析

开篇

本篇以android-11.0.0_r25作为基础解析

在四大组件中,可能我们平时用到最少的便是ContentProvider了,ContentProvider是用来帮助应用管理其自身和其他应用所存储数据的访问,并提供与其他应用共享数据的方法,使用ContentProvider可以安全的在应用之间共享和修改数据,比如说访问图库,通讯录等

在之前的文章中,我们提到了ContentProvider的启动时机,不妨顺水推舟,干脆把这一块分析个明白,本篇文章并不会教大家怎样使用ContentProvider,只将精力集中在ContentProvider在系统层面的启动与交互上

基础知识

ContentResolver

想要通过ContentProvider访问应用数据,我们通常需要借助ContentResolver的API,我们可以通过Context.getContentResolver方法获取其实例对象

ContentResolver是一个抽象类,它的抽象方法由ContextImpl.ApplicationContentResolver继承实现,我们实际上获取到的也是这个实例对象

Uri格式

ContentProvider的使用需要先获得提供者的Uri,它的格式如下:

  1. Scheme:固定为content://
  2. Authority:为提供者在AndroidManifest里设置的android:authorities属性
  3. 资源相对路径
  4. 资源ID

其中,资源相对路径和资源ID不是必须的,要看资源存储的数量及形式

举个栗子,外部存储中某张图片的Uri为:content://media/external/images/media/${id},其中media为Authority,/external/images/media为外部存储图片的相对路径,id为这张图片资源在数据库中存储的id

获取ContentProvider

ContentProvider作为共享数据的桥梁,最主要的几个功能无非是增、删、改、查,我们就以查作为入口来分析ContentProvider对象是怎么获取的

//ContentResolver.query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    ...

    //尝试获取unstableProvider
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
        ...
        try {
            //调用远程对象query
            qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
                    queryArgs, 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
            unstableProviderDied(unstableProvider);
            //尝试获取stableProvider
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            //调用远程对象query
            qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
                    queryArgs, remoteCancellationSignal);
        }
        if (qCursor == null) {
            return null;
        }

        // Force query execution.  Might fail and throw a runtime exception here.
        qCursor.getCount();
        ...

        // Wrap the cursor object into CursorWrapperInner object.
        //将qCursor和provider包装成CursorWrapperInner对象返回
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        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 {
        //释放资源
        if (qCursor != null) {
            qCursor.close();
        }
        if (cancellationSignal != null) {
            cancellationSignal.setRemote(null);
        }
        if (unstableProvider != null) {
            releaseUnstableProvider(unstableProvider);
        }
        if (stableProvider != null) {
            releaseProvider(stableProvider);
        }
    }
}

我们可以将这个方法大致分成以下几个步骤:

  1. 获取unstableProvider远程对象
  2. 调用unstableProvider对象的query方法,获取qCursor
  3. 如果query过程中远程对象死亡,尝试获取stableProvider并调用query方法获取qCursor
  4. 获取stableProvider(如果之前没获取的话)
  5. qCursorstableProvider包装成CursorWrapperInner对象返回
  6. 释放资源

既然ContentProvider可以在应用之前共享数据,那它必然是支持跨进程的,没错,用的还是我们熟悉的Binder通信,IContentProvider对象即是给调用方进程使用的远程Binder对象,回顾这个方法我们发现,IContentProvider远程对象是通过acquireUnstableProvideracquireProvider获取的,我们接下来看看这两个方法做了什么

这里有一个关于unstablestable的概念,对于通过这两种方式获取的ContentProvider分别会有unstableCountstableCount两种引用计数,如果远程ContentProvider所在进程死亡,且其stableCount > 0的话,则会将其通过stable方式关联的调用方进程一同杀死,具体的流程我们会在后面分析

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

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

public final IContentProvider acquireUnstableProvider(String name) {
    if (name == null) {
        return null;
    }
    return acquireUnstableProvider(mContext, name);
}

public final IContentProvider acquireProvider(String name) {
    if (name == null) {
        return null;
    }
    return acquireProvider(mContext, name);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
    return mMainThread.acquireProvider(c,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), false);
}

// ContextImpl.ApplicationContentResolver 内实现
protected IContentProvider acquireProvider(Context context, String auth) {
    return mMainThread.acquireProvider(context,
            ContentProvider.getAuthorityWithoutUserId(auth),
            resolveUserIdFromAuthority(auth), true);
}

ActivityThread.acquireProvider

Android系统是通过Authority来区分不同的ContentProvider的,经过一些简单的判断处理后,最终调用了ActivityThread.acquireProvider方法去获取ContentProvider,而acquireUnstableProvideracquireProvider的区别只是最后一个布尔值入参不同罢了

public final IContentProvider acquireProvider(
        Context c, String auth, int userId, boolean stable) {
    //尝试从本地缓存中获取ContentProvider对象
    final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
    if (provider != null) {
        return provider;
    }

    // There is a possible race here.  Another thread may try to acquire
    // the same provider at the same time.  When this happens, we want to ensure
    // that the first one wins.
    // Note that we cannot hold the lock while acquiring and installing the
    // provider since it might take a long time to run and it could also potentially
    // be re-entrant in the case where the provider is in the same process.
    ContentProviderHolder holder = null;
    try {
        synchronized (getGetProviderLock(auth, userId)) {
            //使用AMS获取ContentProvider对象
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    if (holder == null) {
        ...
        return null;
    }

    // Install provider will increment the reference count for us, and break
    // any ties in the race.
    //安装ContentProvider
    holder = installProvider(c, holder, holder.info,
            true /*noisy*/, holder.noReleaseNeeded, stable);
    return holder.provider;
}

这个方法大概做了以下几件事:

  1. 首先从缓存中尝试获取IContentProvider对象
  2. 使用AMS获取ContentProviderHolder对象
  3. 安装ContentProvider
  4. 返回IContentProvider对象

ActivityThread.acquireExistingProvider

我们首先看通过acquireExistingProvider方法尝试从缓存中获取IContentProvider对象

public final IContentProvider acquireExistingProvider(
        Context c, String auth, int userId, boolean stable) {
    synchronized (mProviderMap) {
        //从缓存Map中查找
        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();
        //判断远端进程是否已被杀死
        if (!jBinder.isBinderAlive()) {
            // The hosting process of the provider has died; we can't
            // use this one.
            //清理ContentProvider
            handleUnstableProviderDiedLocked(jBinder, true);
            return null;
        }

        // Only increment the ref count if we have one.  If we don't then the
        // provider is not reference counted and never needs to be released.
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc != null) {
            //更新引用计数
            incProviderRefLocked(prc, stable);
        }
        return provider;
    }
}

首先通过AuthorityuserId来从Map中查找是否已存在对应的ProviderClientRecord对象,然后从中取出IContentProvider对象,再检查其中的远程Binder对象是否已被杀死,最后一切无误,增加ContentProvider的引用计数

AMS.getContentProvider

如果这一步没有获取到,程序会继续从AMS获取ContentProvider

public final ContentProviderHolder getContentProvider(
        IApplicationThread caller, String callingPackage, String name, int userId,
        boolean stable) {
    if (caller == null) {
        String msg = "null IApplicationThread when getting content provider "
                + name;
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
    }
    // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
    // with cross-user grant.
    final int callingUid = Binder.getCallingUid();
    if (callingPackage != null && mAppOpsService.checkPackage(callingUid, callingPackage)
            != AppOpsManager.MODE_ALLOWED) {
        throw new SecurityException("Given calling package " + callingPackage
                + " does not match caller's uid " + callingUid);
    }
    return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
            null, stable, userId);
}

经过一些检查后调用getContentProviderImpl方法,这个方法有点长,我们分段来看

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;

    synchronized(this) {
        //获取调用方所在进程记录
        ProcessRecord r = null;
        if (caller != null) {
            r = getRecordForAppLocked(caller);
            if (r == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid()
                        + ") when getting content provider " + name);
            }
        }

        boolean checkCrossUser = true;

        // First check if this content provider has been published...
        //检查需要的ContentProvider是否已被发布
        cpr = mProviderMap.getProviderByName(name, userId);
        // If that didn't work, check if it exists for user 0 and then
        // verify that it's a singleton provider before using it.
        //如果没找到,尝试从系统用户中查找已发布的ContentProvider
        //并确保它是可用的单例组件,条件如下:
        //是用户级应用程序且组件设置了单例flag且拥有INTERACT_ACROSS_USERS权限 或 App运行在system进程中 或 组件设置了单例flag且是同一个App
        if (cpr == null && userId != UserHandle.USER_SYSTEM) {
            cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
            if (cpr != null) {
                cpi = cpr.info;
                if (isSingleton(cpi.processName, cpi.applicationInfo,
                        cpi.name, cpi.flags)
                        && isValidSingletonCall(r == null ? callingUid : r.uid,
                                cpi.applicationInfo.uid)) {
                    userId = UserHandle.USER_SYSTEM;
                    checkCrossUser = false;
                } else {
                    cpr = null;
                    cpi = null;
                }
            }
        }

        //判断ContentProvider所在进程是否已死亡
        ProcessRecord dyingProc = null;
        if (cpr != null && cpr.proc != null) {
            providerRunning = !cpr.proc.killed;

            // Note if killedByAm is also set, this means the provider process has just been
            // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called
            // yet. So we need to call appDiedLocked() here and let it clean up.
            // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
            // how to test this case.)
            if (cpr.proc.killed && cpr.proc.killedByAm) {
                Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
                // Now we are going to wait for the death before starting the new process.
                dyingProc = cpr.proc;
            }
        }
        ...
    }
    ...
}

首先,第一部分,检查目标ContentProvider是否已被发布并记录在了mProviderMap中,注意这里的mProviderMapAMS中的一个成员变量,一系列Map的一个集合,和ActivityThread中的mProviderMap不是一个东西。如果在当前用户中找不到,且当前用户不是系统用户(UserHandle.USER_SYSTEM == 0),则尝试从系统用户中查找合法可用的单例ContentProvider,符合以下任一一个条件的ContentProvider即可被视作单例ContentProvider

  • App是用户级应用程序(uid >= 10000)且ContentProvider组件设置了单例flag(android:singleUser)且App拥有INTERACT_ACROSS_USERS权限
  • App运行在system进程中
  • ContentProvider组件设置了单例flag(android:singleUser)且是同一个App

至于为什么跨用户访问需要单例这个条件,这个和多用户相关,我也不是很清楚,以后如果分析到了多用户这块再回来补充。目前国内厂商的应用分身、手机分身功能大部分用的就是多用户技术

接着通过目标ContentProviderRecord是否存在和其所在进程是否还存活判断目标ContentProvider是否在运行中

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;

    synchronized(this) {
        ...
        //ContentProvider正在运行中
        if (providerRunning) {
            cpi = cpr.info;

            //如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
            //直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
            if (r != null && cpr.canRunHere(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 = cpr.newHolder(null);
                // don't give caller the provider object, it needs
                // to make its own.
                holder.provider = null;
                return holder;
            }

            // Don't expose providers between normal apps and instant apps
            try {
                if (AppGlobals.getPackageManager()
                        .resolveContentProvider(name, 0 /*flags*/, userId) == null) {
                    return null;
                }
            } catch (RemoteException e) {
            }

            ... //权限检查

            final long origId = Binder.clearCallingIdentity();

            // In this case the provider instance already exists, so we can
            // return it right away.
            //获取连接并更新引用计数
            conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                    stable);
            if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                if (cpr.proc != null
                        && r != null && r.setAdj <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                    // If this is a perceptible app accessing the provider,
                    // make sure to count it as being accessed and thus
                    // back up on the LRU list.  This is good because
                    // content providers are often expensive to start.
                    //更新进程优先级
                    mProcessList.updateLruProcessLocked(cpr.proc, false, null);
                }
            }

            final int verifiedAdj = cpr.proc.verifiedAdj;
            //更新进程adj
            boolean success = updateOomAdjLocked(cpr.proc, true,
                    OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
            // XXX things have changed so updateOomAdjLocked doesn't actually tell us
            // if the process has been successfully adjusted.  So to reduce races with
            // it, we will check whether the process still exists.  Note that this doesn't
            // completely get rid of races with LMK killing the process, but should make
            // them much smaller.
            if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
                success = false;
            }
            maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
            // NOTE: there is still a race here where a signal could be
            // pending on the process even though we managed to update its
            // adj level.  Not sure what to do about this, but at least
            // the race is now smaller.
            if (!success) {
                // Uh oh...  it looks like the provider's process
                // has been killed on us.  We need to wait for a new
                // process to be started, and make sure its death
                // doesn't kill our process.
                Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
                        + " is crashing; detaching " + r);
                //ContentProvider所在进程被杀了,更新引用计数
                boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                //仍有别的地方对这个ContentProvider有引用,直接返回null(需要等待进程清理干净才能重启)
                if (!lastRef) {
                    // This wasn't the last ref our process had on
                    // the provider...  we will be killed during cleaning up, bail.
                    return null;
                }
                // We'll just start a new process to host the content provider
                //将运行状态标为false,使得重新启动ContentProvider所在进程
                providerRunning = false;
                conn = null;
                dyingProc = cpr.proc;
            } else {
                cpr.proc.verifiedAdj = cpr.proc.setAdj;
            }

            Binder.restoreCallingIdentity(origId);
        }
        ...
    }
    ...
}

第二部分,如果目标ContentProvider正在运行中,首先检查目标ContentProvider是否可以在调用者进程中直接运行,需要满足以下任一一个条件:

  • 调用者和目标ContentProvider是同一个App中的同进程
  • 调用者和目标ContentProvider属同一个App且ContentProvider组件支持多进程(android:multiprocess

在这种情况下,直接返回一个新的ContentProviderHolder让调用者进程自己处理获得ContentProvider即可,具体逻辑在ActivityThread.installProvider方法中,后面会分析

如果不满足这种情况,即调用方进程和目标ContentProvider不在一个进程中,需要跨进程调用,获取ContentProviderConnection连接并更新引用计数

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;

    synchronized(this) {
        ...
        //ContentProvider未在运行
        if (!providerRunning) {
            //通过PMS获取ContentProvider信息
            try {
                cpi = AppGlobals.getPackageManager().
                    resolveContentProvider(name,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
            } catch (RemoteException ex) {
            }
            if (cpi == null) {
                return null;
            }
            // If the provider is a singleton AND
            // (it's a call within the same user || the provider is a
            // privileged app)
            // Then allow connecting to the singleton provider
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags)
                    && isValidSingletonCall(r == null ? callingUid : r.uid,
                            cpi.applicationInfo.uid);
            if (singleton) {
                userId = UserHandle.USER_SYSTEM;
            }
            cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);

            ... //各项检查

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            //通过Class(android:name属性)获取ContentProviderRecord
            cpr = mProviderMap.getProviderByClass(comp, userId);
            //此ContentProvider是第一次运行
            boolean firstClass = cpr == null;
            if (firstClass) {
                final long ident = Binder.clearCallingIdentity();

                ... //权限处理

                try {
                    //获取应用信息
                    ApplicationInfo ai =
                        AppGlobals.getPackageManager().
                            getApplicationInfo(
                                    cpi.applicationInfo.packageName,
                                    STOCK_PM_FLAGS, userId);
                    if (ai == null) {
                        Slog.w(TAG, "No package info for content provider "
                                + cpi.name);
                        return null;
                    }
                    ai = getAppInfoForUser(ai, userId);
                    //新建ContentProvider记录
                    cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                } catch (RemoteException ex) {
                    // pm is in same process, this will never happen.
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            } else if (dyingProc == cpr.proc && dyingProc != null) {
                // The old stable connection's client should be killed during proc cleaning up,
                // so do not re-use the old ContentProviderRecord, otherwise the new clients
                // could get killed unexpectedly.
                //旧的ContentProvider进程在死亡过程中,不要复用旧的ContentProviderRecord,避免出现预期之外的问题
                cpr = new ContentProviderRecord(cpr);
                // This is sort of "firstClass"
                firstClass = true;
            }

            //如果此ContentProvider可以在调用者进程中直接运行(同一个App的同进程 或 同一个App且Provider组件支持多进程)
            //直接返回一个新的ContentProviderHolder让调用者进程自己启动ContentProvider
            if (r != null && cpr.canRunHere(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 cpr.newHolder(null);
            }

            // This is single process, and our app is now connecting to it.
            // See if we are already in the process of launching this
            // provider.
            //查找正在启动中的ContentProvider
            final int N = mLaunchingProviders.size();
            int i;
            for (i = 0; i < N; i++) {
                if (mLaunchingProviders.get(i) == cpr) {
                    break;
                }
            }

            // If the provider is not already being launched, then get it
            // started.
            //目标ContentProvider不在启动中
            if (i >= N) {
                final long origId = Binder.clearCallingIdentity();

                try {
                    // Content provider is now in use, its package can't be stopped.
                    //将App状态置为unstopped,设置休眠状态为false
                    try {
                        AppGlobals.getPackageManager().setPackageStoppedState(
                                cpr.appInfo.packageName, false, userId);
                    } catch (RemoteException e) {
                    } catch (IllegalArgumentException e) {
                        Slog.w(TAG, "Failed trying to unstop package "
                                + cpr.appInfo.packageName + ": " + e);
                    }

                    // Use existing process if already started
                    //获取目标ContentProvider所在进程记录
                    ProcessRecord proc = getProcessRecordLocked(
                            cpi.processName, cpr.appInfo.uid, false);
                    if (proc != null && proc.thread != null && !proc.killed) { //进程存活
                        if (!proc.pubProviders.containsKey(cpi.name)) {
                            //将ContentProviderRecord保存到进程已发布ContentProvider列表中
                            proc.pubProviders.put(cpi.name, cpr);
                            try {
                                //调度ActivityThread直接安装ContentProvider
                                proc.thread.scheduleInstallProvider(cpi);
                            } catch (RemoteException e) {
                            }
                        }
                    } else { //进程死亡
                        //启动App(App启动过程中会自动启动ContentProvider)
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0,
                                new HostingRecord("content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name)),
                                ZYGOTE_POLICY_FLAG_EMPTY, false, false, false);
                        if (proc == null) {
                            ...
                            return null;
                        }
                    }
                    cpr.launchingApp = proc;
                    //将目标ContentProvider添加到启动中列表
                    mLaunchingProviders.add(cpr);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }

            // Make sure the provider is published (the same provider class
            // may be published under multiple names).
            if (firstClass) {
                mProviderMap.putProviderByClass(comp, cpr);
            }

            mProviderMap.putProviderByName(name, cpr);
            //获取连接并更新引用计数
            conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
                    stable);
            if (conn != null) {
                conn.waiting = true;
            }
        }

        grantImplicitAccess(userId, null /*intent*/, callingUid,
                UserHandle.getAppId(cpi.applicationInfo.uid));
    }
    ...
}

第三部分,如果目标ContentProvider未在运行,先通过PMS获取ContentProvider信息,接着尝试通过Class(android:name属性)获取ContentProviderRecord,如果获取不到,说明这个ContentProvider是第一次运行(开机后),这种情况下需要新建ContentProviderRecord,如果获取到了,但是其所在进程被标记为正在死亡,此时同样需要新建ContentProviderRecord,不要复用旧的ContentProviderRecord,避免出现预期之外的问题

接下来同样检查目标ContentProvider是否可以在调用者进程中直接运行,如果可以直接返回一个新的ContentProviderHolder让调用者进程自己启动获取ContentProvider

接着检查正在启动中的ContentProvider列表,如果不在列表中,我们可能需要手动启动它,此时又有两种情况:

  1. ContentProvider所在进程已启动:如果进程已发布ContentProvider列表中不包含这个ContentProviderRecord,则将其添加到列表中,然后调用目标进程中的ApplicationThread.scheduleInstallProvider方法安装启动ContentProvider
  2. ContentProvider所在进程未启动:启动目标进程,目标进程启动过程中会自动安装启动ContentProviderActivityThread.handleBindApplication方法中)

最后更新mProviderMap,获取ContentProviderConnection连接并更新引用计数

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;
    boolean providerRunning = false;

    // Wait for the provider to be published...
    final long timeout =
            SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
    boolean timedOut = false;
    synchronized (cpr) {
        while (cpr.provider == null) {
            //ContentProvider启动过程中进程死亡,返回null
            if (cpr.launchingApp == null) {
                ...
                return null;
            }
            try {
                //计算最大等待时间
                final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());
                if (conn != null) {
                    conn.waiting = true;
                }
                //释放锁,等待ContentProvider启动完成
                cpr.wait(wait);
                //等待时间已过,ContentProvider还是没能启动完成并发布,超时
                if (cpr.provider == null) {
                    timedOut = true;
                    break;
                }
            } catch (InterruptedException ex) {
            } finally {
                if (conn != null) {
                    conn.waiting = false;
                }
            }
        }
    }
    if (timedOut) {
        ... //超时处理
        return null;
    }

    //返回新的ContentProviderHolder
    return cpr.newHolder(conn);
}

第四部分,如果ContentProvider已存在,直接新建一个ContentProviderHolder返回,如果ContentProvider之前不存在,现在正在启动中,则以当前时间加上CONTENT_PROVIDER_READY_TIMEOUT_MILLIS推算出一个超时时间,给目标ContentProviderRecord上锁后,调用wait方法等待,直到ContentProvider成功发布后notify解除wait状态(在AMS.publishContentProviders方法中,之后会分析到),或一直等待直到超时。wait状态解除后,判断内部ContentProvider是否已被赋值,如果没有,则可以断定超时,此时返回null,如有,则返回一个新的ContentProviderHolder

ActivityThread.installProvider

由于这个方法同时包含了启动安装本地ContentProvider和获取安装远程ContentProvider的逻辑,所以放到后面启动ContentProvider章节里一起分析

启动ContentProvider

从前面的章节获取ContentProvider中,我们已经归纳出ContentProvider的启动分为两种情况,接着我们就来分析在这两种情况下,ContentProvider的启动路径

进程已启动

在进程已启动的情况下,如果进程已发布ContentProvider列表中不包含这个ContentProviderRecord,则将其添加到列表中,然后调用目标进程中的ApplicationThread.scheduleInstallProvider方法安装启动ContentProvider

ApplicationThread.scheduleInstallProvider会通过Hander发送一条what值为H.INSTALL_PROVIDER的消息,我们根据这个what值搜索,发现会走到ActivityThread.handleInstallProvider方法中,在这个方法内又会调用installContentProviders方法安装启动ContentProvider

进程未启动

在进程未启动的情况下,直接启动目标进程,在之前的文章 Android源码分析 - Activity启动流程(中) 里,我们分析了App的启动流程,其中有两个地方对启动ContentProvider至关重要

AMS.attachApplicationLocked

在这个方法中会调用generateApplicationProvidersLocked方法

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    ...
    //normalMode一般情况下均为true
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    ...
}

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    List<ProviderInfo> providers = null;
    try {
        //通过PMS获取App中同一个进程内的所有的ContentProvider组件信息
        providers = AppGlobals.getPackageManager()
                .queryContentProviders(app.processName, app.uid,
                        STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
                                | MATCH_DEBUG_TRIAGED_MISSING, /*metadastaKey=*/ null)
                .getList();
    } catch (RemoteException ex) {
    }
    int userId = app.userId;
    if (providers != null) {
        int N = providers.size();
        //有必要的情况下进行Map扩容
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
            // TODO: keep logic in sync with installEncryptionUnawareProviders
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            //对于单例ContentProvider,需要在默认用户中启动,如果不是默认用户的话则直接将其丢弃掉,不启动
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_SYSTEM) {
                // This is a singleton provider, but a user besides the
                // default user is asking to initialize a process it runs
                // in...  well, no, it doesn't actually run in this process,
                // it runs in the process of the default user.  Get rid of it.
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
                //新建ContentProviderRecord并将其加入到缓存中
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            //将ContentProviderRecord保存到进程已发布ContentProvider列表中
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                //将App添加至进程中运行的包列表中
                app.addPackage(cpi.applicationInfo.packageName,
                        cpi.applicationInfo.longVersionCode, mProcessStats);
            }
            notifyPackageUse(cpi.applicationInfo.packageName,
                                PackageManager.NOTIFY_PACKAGE_USE_CONTENT_PROVIDER);
        }
    }
    return providers;
}

这个方法主要是获取需要启动的ContentProviderContentProviderRecord,如果是第一次启动这个ContentProvider则需要新建一个ContentProviderRecord并将其存入缓存,然后将其保存到进程已发布ContentProvider列表中,以供后面使用。同时这个方法返回了需要启动的ProviderInfo列表,AMS.attachApplicationLocked方法可以根据这个列表判断是否有需要启动的ContentProvider并设置ContentProvider启动超时检测

ActivityThread.handleBindApplication

private void handleBindApplication(AppBindData data) {
    ...
    try {
        // If the app is being launched for full backup or restore, bring it up in
        // a restricted environment with the base application class.
        //创建Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
    
        ...

        mInitialApplication = app;

        // don't bring up providers in restricted mode; they may depend on the
        // app's custom Application class
        //在非受限模式下启动ContentProvider
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers);
            }
        }

        ...

        //执行Application的onCreate方法
        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            ...
        }
    } finally {
        // If the app targets < O-MR1, or doesn't change the thread policy
        // during startup, clobber the policy to maintain behavior of b/36951662
        if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
            StrictMode.setThreadPolicy(savedPolicy);
        }
    }
    ...
}

可以看到,在这个方法中直接调用了installContentProviders方法安装启动ContentProvider

另外提一点,为什么我要把Application的创建和onCreate也放进来呢?现在市面上有很多库,包括很多教程告诉我们,可以通过注册ContentProvider的方式初始化SDK,获取全局Context,比如说著名的内存泄漏检测工具LeakCanary的新版本,想要使用它,直接添加它的依赖就行了,不需要对代码做哪怕一点的改动,究其原理,就是因为ContentProvider的启动时机是在Application创建后,Application.onCreate调用前,并且ContentProvider内的Context成员变量大概率就是Application,大家以后在开发过程中也可以妙用这一点

ActivityThread.installContentProviders

好了,现在这两种情况最终都走到了ActivityThread.installContentProviders方法中,那我们接下来就好好分析ContentProvider实际的启动安装流程

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<ContentProviderHolder> results = new ArrayList<>();

    for (ProviderInfo cpi : providers) {
        //逐个启动
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

    try {
        //发布ContentProvider
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

这个方法很简单,便利所有待启动的ContentProvider信息列表,逐个启动安装ContentProvider,最后一起发布

ActivityThread.installProvider

我们先看installProvider方法,我们在上一章中分析到,获取ContentProvider的时候也会调用这个方法,这次我们就结合起来一起分析

通过上文的代码,我们发现,有两处地方会调用installProvider方法,方法的入参有三种形式,分别为:

  • holder不为nullinfo不为nullholder.providernull:在ActivityThread.acquireProvider方法中被调用,路径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 发现目标ContentProvider可以在调用者进程中直接运行 -> 直接返回一个新的ContentProviderHolder(包含ProviderInfo) -> ActivityThread.installProvider,在这种情况下installProvider方法会在本地启动安装ContentProvider

  • holdernullinfo不为null:在ActivityThread.installContentProviders方法中被调用,两条路径,一是App进程启动后自动执行,二是在AMS.getContentProvider方法中发现目标进程已启动但是ContentProvider未启动,调用ActivityThread.scheduleInstallProvider方法执行,在这种情况下installProvider方法会在本地启动安装ContentProvider

  • holder不为nullholder.provider不为null:在ActivityThread.acquireProvider方法中被调用,路径为 没有获取到已存在的ContentProvider -> AMS.getContentProvider -> AMS.getContentProviderImpl -> 获取到目标进程的远程ContentProvider引用 -> 包装成ContentProviderHolder返回 -> ActivityThread.installProvider,在这种情况下installProvider方法直接可以获取到远程ContentProvider引用,然后进行处理

我们将这三种情况分成两种case分别分析

本地启动ContentProvider
private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) { //启动本地ContentProvider
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        //首先获取Context,一般情况下就是Application
        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
            }
        }
        if (c == null) {
            return null;
        }

        //Split Apks动态加载相关
        if (info.splitName != null) {
            try {
                c = c.createContextForSplit(info.splitName);
            } catch (NameNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            //获取应用信息
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            //通过AppComponentFactory实例化ContentProvider
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            //Transport类,继承自ContentProviderNative(Binder服务端)
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // XXX Need to create the correct context for this provider.
            //初始化ContentProvider,调用其onCreate方法
            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 { //获取外部ContentProvider
        ...
    }

    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        //对于本地ContentProvider来说,这里的实际类型是Transport,继承自ContentProviderNative(Binder服务端)
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) { //本地启动ContentProvider的情况
            ComponentName cname = new ComponentName(info.packageName, info.name);
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                //如果已经存在相应的ContentProvider记录,使用其内部已存在的ContentProvider
                provider = pr.mProvider;
            } else {
                //否则使用新创建的ContentProvider
                holder = new ContentProviderHolder(info);
                holder.provider = provider;
                //对于本地ContentProvider来说,不存在释放引用这种情况
                holder.noReleaseNeeded = true;
                //创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                //保存ProviderClientRecord到本地缓存中
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else { //获取远程ContentProvider的情况
            ...
        }
    }
    return retHolder;
}

我们在这里找到了ContentProvider创建并启动的入口,首先通过传入的Context(实际上就是Application)判断并确定创建并给ContentProvider使用的的Context是什么(一般情况下也是Application),然后获取到应用信息LoadedApk,再通过它得到AppComponentFactory(前面的文章中介绍过,如果没有在AndroidManifest中设置android:appComponentFactory属性,使用的便是默认的AppComponentFactory),接着通过AppComponentFactory.instantiateProvider方法实例化ContentProvider对象

public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
        @NonNull String className)
        throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (ContentProvider) cl.loadClass(className).newInstance();
}

默认的话就是通过ClassName反射调用默认构造函数实例化ContentProvider对象,最后再调用ContentProvider.attachInfo方法初始化

public void attachInfo(Context context, ProviderInfo info) {
    attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    ...
    if (mContext == null) {
        mContext = context;
        ...
        ContentProvider.this.onCreate();
    }
}

详细内容我们就不分析了,只需要知道这里给mContext赋了值,然后调用了ContentProvider.onCreate方法就可以了

到了这一步,ContentProvider就算是启动完成了,接下来需要执行一些安装步骤,其实也就是对缓存等进行一些处理。在ContentProvider实例化后,会调用其getIContentProvider方法给provider变量赋值,这里获得的对象其实是一个Transport对象,继承自ContentProviderNative,是一个Binder服务端对象,在ContentProvider初始化后,会对Transport对象调用asBinder方法获得Binder对象,这里获得的其实还是自己本身,接着从缓存中尝试获取ProviderClientRecord对象,如果获取到了,说明已经存在了相应的ContentProvider,使用ProviderClientRecord内部的ContentProvider,刚刚新创建的那个就可以丢弃了,如果没获取到,就去新建ContentProviderHolder以及ProviderClientRecord,然后将他们添加到各种缓存中,至此,ContentProvider的安装过程也到此结束

获取处理远程ContentProvider
private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) { //启动本地ContentProvider
        ...
    } else { //获取外部ContentProvider
        //实际类型为ContentProviderProxy
        provider = holder.provider;
    }

    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        //对于外部ContentProvider来说,这里的实际类型是BinderProxy
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) { //本地启动ContentProvider的情况
            ...
        } else { //获取远程ContentProvider的情况
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) { //如果ContentProvider引用已存在
                // We need to transfer our new reference to the existing
                // ref count, releasing the old one...  but only if
                // release is needed (that is, it is not running in the
                // system process).
                //对于远程ContentProvider来说,如果目标App为system应用(UID为ROOT_UID或SYSTEM_UID)
                //并且目标App不为设置(包名不为com.android.settings),则noReleaseNeeded为true
                if (!noReleaseNeeded) {
                    //增加已存在的ContentProvider引用的引用计数
                    incProviderRefLocked(prc, stable);
                    try {
                        //释放传入的引用,移除ContentProviderConnection相关信息,更新引用计数
                        ActivityManager.getService().removeContentProvider(
                                holder.connection, stable);
                    } catch (RemoteException e) {
                        //do nothing content provider object is dead any way
                    }
                }
            } else {
                //创建ProviderClientRecord并将其保存到mProviderMap本地缓存中
                ProviderClientRecord client = installProviderAuthoritiesLocked(
                        provider, localProvider, holder);
                if (noReleaseNeeded) { //同上,目标App为system应用,不需要释放引用
                    //新建一个ProviderRefCount,但引用计数初始化为一个较大的数值
                    //这样后续无论调用方进程的ContentProvider引用计数如何变动都不会影响到AMS
                    prc = new ProviderRefCount(holder, client, 1000, 1000);
                } else { //需要释放引用的情况下
                    //正常的新建初始化一个ProviderRefCount
                    prc = stable
                            ? new ProviderRefCount(holder, client, 1, 0)
                            : new ProviderRefCount(holder, client, 0, 1);
                }
                //保存至缓存
                mProviderRefCountMap.put(jBinder, prc);
            }
            retHolder = prc.holder;
        }
    }
    return retHolder;
}

对于holder.provider不为null的情况,直接获取远程ContentProvider引用,然后进行处理就可以了。这里获取到的IContentProvider的实际类型是ContentProviderProxy,然后对其调用asBinder方法,获取到的是BinderProxy对象,接着从缓存中尝试获取ProviderRefCount对象,如果缓存中已经有相应的引用对象了,则在需要释放引用(!noReleaseNeeded)的情况下使用原有的引用,释放参数传入进来的ContentProvider引用

这里noReleaseNeeded是在ContentProviderRecord构造时赋值的,为true的条件是目标App为system应用(UIDROOT_UIDSYSTEM_UID)并且目标App不为设置(包名不为com.android.settings

如果缓存中没有查找到相应的ProviderRefCount对象,新建ProviderClientRecordProviderRefCount对象,并将他们保存到缓存中,至于为什么在noReleaseNeeded的情况下,新建的ProviderRefCount的引用计数初始值为1000,我猜测是因为noReleaseNeeded代表了不需要释放引用,所以这里干脆设置一个比较大的值,这样无论调用方进程的ContentProvider引用计数怎样变动,都不会再调用到AMS的方法中去处理引用的变化,在非常早期的Android版本中(Android 4.0.1),这个值曾被设置为10000

至此,远程ContentProvider的安装也结束了

ActivityThread.installProviderAuthoritiesLocked

接下来我们再简单的看一下两种case都会走到的installProviderAuthoritiesLocked方法吧

private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
        ContentProvider localProvider, ContentProviderHolder holder) {
    final String auths[] = holder.info.authority.split(";");
    final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
    ...
    final ProviderClientRecord pcr = new ProviderClientRecord(
            auths, provider, localProvider, holder);
    for (String auth : auths) {
        final ProviderKey key = new ProviderKey(auth, userId);
        final ProviderClientRecord existing = mProviderMap.get(key);
        if (existing != null) {
            Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
                    + " already published as " + auth);
        } else {
            mProviderMap.put(key, pcr);
        }
    }
    return pcr;
}

这个方法很简单,新建了一个ProviderClientRecord并将其添加到mProviderMap缓存中,这里的操作对应着最前面的acquireExistingProvider方法,有了这个缓存,以后就可以直接拿,而不用再复杂的经过一系列的AMS跨进程操作了

AMS.publishContentProviders

ContentProvider全部启动安装完后,便要调用AMS.publishContentProviders将他们发布出去,供外部使用了

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
    if (providers == null) {
        return;
    }

    synchronized (this) {
        final ProcessRecord r = getRecordForAppLocked(caller);
        if (r == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + Binder.getCallingPid()
                    + ") when publishing content providers");
        }

        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;
            }
            //App进程启动时或AMS.getContentProvider中已经将相应ContentProviderRecord添加到了pubProviders中
            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);
                }

                //ContentProvider已经启动完毕,将其从正在启动的ContentProvider列表中移除
                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--;
                    }
                }
                //移除ContentProvider启动超时监听
                if (wasInLaunchingProviders) {
                    mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                // Make sure the package is associated with the process.
                // XXX We shouldn't need to do this, since we have added the package
                // when we generated the providers in generateApplicationProvidersLocked().
                // But for some reason in some cases we get here with the package no longer
                // added...  for now just patch it in to make things happy.
                r.addPackage(dst.info.applicationInfo.packageName,
                        dst.info.applicationInfo.longVersionCode, mProcessStats);
                synchronized (dst) {
                    dst.provider = src.provider;
                    dst.setProcess(r);
                    //让出锁,通知其他wait的地方
                    //对应着AMS.getContentProvider的第四部分:等待ContentProvider启动完成
                    dst.notifyAll();
                }
                dst.mRestartCount = 0;
                updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
                maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
                        src.info.authority);
            }
        }

        Binder.restoreCallingIdentity(origId);
    }
}

遍历整个待发布的ContentProvider列表,从ProcessRecord.pubProviders中查找相对应的ContentProviderRecord,我们在之前的章节中已经分析过了,App进程启动时或AMS.getContentProvider中已经将相应ContentProviderRecord添加到了pubProviders中,然后就是将其保存到各个缓存中,由于ContentProvider已经启动完毕,所以需要将其从正在启动的ContentProvider列表中移除,在ContentProvider正常启动的情况下,我们需要将ContentProvider的启动超时监听移除,最后,获取ContentProviderRecord同步锁,将准备好的ContentProvider赋值到ContentProviderRecord中,接着调用notifyAll方法通知其他调用过wait的地方,将锁让出,这里对应的就是AMS.getContentProvider的第四部分:等待ContentProvider启动完成

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
        String name, IBinder token, int callingUid, String callingPackage, String callingTag,
        boolean stable, int userId) {
    ...
    //这里的cpr和在publishContentProviders获得的dst是一个对象
    synchronized (cpr) {
        while (cpr.provider == null) {
            ...
            //释放锁,等待ContentProvider启动完成
            cpr.wait(wait);
            ...
        }
    }
    ...
}

这样,ContentProvider一发布,这里就会收到通知,解除wait状态,获得到ContentProvider,返回出去,是不是感觉一切都串起来了?

ContentProvider引用计数

ContentProvider的获取与启动分析完了,接下来我们聊聊它的引用计数,为下一小节分析ContentProvider死亡杀死调用方进程的过程做准备

ActivityThread层的引用计数是和AMS层的引用计数分开的,ActivityThread记录的是目标ContentProvider在本进程中有多少处正在使用,而AMS记录的是目标ContentProvider正在被多少个进程使用

ActivityThread层的引用计数

增加引用计数

我们先从ActivityThread层增加引用计数开始说起,在ActivityThread获取ContentProvider时,便会调用incProviderRefLocked方法来增加引用计数,具体的时机为acquireExistingProviderinstallProvider时,代码我就不重复放了,大家看前面几个小节就行(后同)

private final void incProviderRefLocked(ProviderRefCount prc, boolean stable) {
    if (stable) {
        //增加ActivityThread的stable引用计数
        prc.stableCount += 1;
        //本进程对目标ContentProvider产生了stable引用关系
        if (prc.stableCount == 1) {
            // We are acquiring a new stable reference on the provider.
            int unstableDelta;
            //正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
            if (prc.removePending) {
                // We have a pending remove operation, which is holding the
                // last unstable reference.  At this point we are converting
                // that unstable reference to our new stable reference.
                //当ActivityThread释放一个stable的ContentProvider时,如果释放完后,
                //发现stable和unstable引用计数均为0,则会暂时保留一个unstable引用
                //所以这里需要为 -1 ,将这个unstable引用移除
                unstableDelta = -1;
                // Cancel the removal of the provider.
                prc.removePending = false;
                // There is a race! It fails to remove the message, which
                // will be handled in completeRemoveProvider().
                //取消移除ContentProvider引用
                mH.removeMessages(H.REMOVE_PROVIDER, prc);
            } else {
                //对于正常情况,只需要增加stable引用计数,不需要动unstable引用计数
                unstableDelta = 0;
            }
            try {
                //AMS层修改引用计数
                ActivityManager.getService().refContentProvider(
                        prc.holder.connection, 1, unstableDelta);
            } catch (RemoteException e) {
                //do nothing content provider object is dead any way
            }
        }
    } else {
        //增加ActivityThread的unstable引用计数
        prc.unstableCount += 1;
        //本进程对目标ContentProvider产生了unstable引用关系
        if (prc.unstableCount == 1) {
            // We are acquiring a new unstable reference on the provider.
            //正在移除ContentProvider引用中(释放ContentProvider后发现stable和unstable引用计数均为0)
            if (prc.removePending) {
                // Oh look, we actually have a remove pending for the
                // provider, which is still holding the last unstable
                // reference.  We just need to cancel that to take new
                // ownership of the reference.
                //取消移除ContentProvider引用
                prc.removePending = false;
                mH.removeMessages(H.REMOVE_PROVIDER, prc);
            } else {
                // First unstable ref, increment our count in the
                // activity manager.
                try {
                    //增加AMS层的unstable引用计数
                    ActivityManager.getService().refContentProvider(
                            prc.holder.connection, 0, 1);
                } catch (RemoteException e) {
                    //do nothing content provider object is dead any way
                }
            }
        }
    }
}

这里的逻辑需要配合着ContentProvider释放引用那里一起看才好理解,我先提前解释一下

首先,removePending这个变量表示此ContentProvider正在移除中,当ActivityThread减少引用计数,检查到stableunstable引用计数均为0后被赋值为true,并且会向Handler发送一条what值为REMOVE_PROVIDER的延时消息,在一定时间后便会触发ContentProvider移除操作,清理本地缓存,再将removePending重新置为false,所以当这里removePendingtrue则说明此ContentProvider还没完全被移除,我们把这个消息取消掉继续使用这个ContentProvider

对于stable引用的情况下,当ActivityThread减少引用计数,检查到stableunstable引用计数均为0后,会暂时保留一个unstable引用,等到后面真正触发到了移除ContentProvider的时候再将这个unstable引用移除,所以在增加引用计数的时候需要考虑到这一点,在这种情况下要将AMS层的unstable引用计数减一

对于其他的情况就是正常的增加ActivityThread层引用计数,然后调用AMS.refContentProvider方法操作AMS层的引用计数

减少引用计数

ContentProvider使用完后会调用ActivityThread.releaseProvider方法,以query方法为例,最后会调用releaseUnstableProviderreleaseProvider方法,最终都会走到这里来

public final boolean releaseProvider(IContentProvider provider, boolean stable) {
    if (provider == null) {
        return false;
    }

    IBinder jBinder = provider.asBinder();
    synchronized (mProviderMap) {
        ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
        if (prc == null) {
            // The provider has no ref count, no release is needed.
            return false;
        }

        boolean lastRef = false;
        if (stable) {
            //引用计数已经为0,无法再减了
            if (prc.stableCount == 0) {
                return false;
            }
            //减少ActivityThread的stable引用计数
            prc.stableCount -= 1;
            if (prc.stableCount == 0) {
                // What we do at this point depends on whether there are
                // any unstable refs left: if there are, we just tell the
                // activity manager to decrement its stable count; if there
                // aren't, we need to enqueue this provider to be removed,
                // and convert to holding a single unstable ref while
                // doing so.
                lastRef = prc.unstableCount == 0;
                try {
                    //如果是最后的引用,则暂时保留一个unstable引用
                    ActivityManager.getService().refContentProvider(
                            prc.holder.connection, -1, lastRef ? 1 : 0);
                } catch (RemoteException e) {
                    //do nothing content provider object is dead any way
                }
            }
        } else {
            //引用计数已经为0,无法再减了
            if (prc.unstableCount == 0) {
                return false;
            }
            //减少ActivityThread的unstable引用计数
            prc.unstableCount -= 1;
            if (prc.unstableCount == 0) {
                // If this is the last reference, we need to enqueue
                // this provider to be removed instead of telling the
                // activity manager to remove it at this point.
                lastRef = prc.stableCount == 0;
                //如果是最后的引用,则不进入到这里,暂时保留一个unstable引用
                if (!lastRef) {
                    try {
                        //减少AMS引用计数
                        ActivityManager.getService().refContentProvider(
                                prc.holder.connection, 0, -1);
                    } catch (RemoteException e) {
                        //do nothing content provider object is dead any way
                    }
                }
            }
        }

        if (lastRef) {
            if (!prc.removePending) {
                // Schedule the actual remove asynchronously, since we don't know the context
                // this will be called in.
                //表面此ContentProvider正在移除中
                prc.removePending = true;
                //发送延时消息,等待一定时间后移除ContentProvider
                Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, prc);
                mH.sendMessageDelayed(msg, CONTENT_PROVIDER_RETAIN_TIME);
            } else {
                Slog.w(TAG, "Duplicate remove pending of provider " + prc.holder.info.name);
            }
        }
        return true;
    }
}

可以看到,在减完引用计数后,如果发现是最后一个引用,即stableunstable引用计数均为0,此时无论是stable还是unstable都会让AMS暂时保留一个unstable引用,然后发送一条what值为REMOVE_PROVIDER的延时消息,等待一定时间后移除ContentProvider,当时间到了触发这条消息时,会调用到ActivityThread.completeRemoveProvider方法

final void completeRemoveProvider(ProviderRefCount prc) {
    synchronized (mProviderMap) {
        if (!prc.removePending) {
            // There was a race!  Some other client managed to acquire
            // the provider before the removal was completed.
            // Abort the removal.  We will do it later.
            return;
        }

        // More complicated race!! Some client managed to acquire the
        // provider and release it before the removal was completed.
        // Continue the removal, and abort the next remove message.
        prc.removePending = false;

        //移除缓存
        final IBinder jBinder = prc.holder.provider.asBinder();
        ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
        if (existingPrc == prc) {
            mProviderRefCountMap.remove(jBinder);
        }

        //移除缓存
        for (int i=mProviderMap.size()-1; i>=0; i--) {
            ProviderClientRecord pr = mProviderMap.valueAt(i);
            IBinder myBinder = pr.mProvider.asBinder();
            if (myBinder == jBinder) {
                mProviderMap.removeAt(i);
            }
        }
    }

    try {
        //处理AMS层引用计数
        ActivityManager.getService().removeContentProvider(
                prc.holder.connection, false);
    } catch (RemoteException e) {
        //do nothing content provider object is dead any way
    }
}

这个方法将进程内所持有的ContentProvider相关缓存清除,然后调用AMS.removeContentProvider方法通知AMS移除ContentProvider,处理相应的引用计数。这里我们发现,调用AMS.removeContentProvider方法传入的最后一个参数stablefalse,因为我们之前在stableunstable引用计数均为0的情况下,保留了一个unstable引用,所以这时移除的ContentProvider引用也是unstable引用

AMS层的引用计数

接着我们来看AMS层的引用计数

AMS.refContentProvider

我们就先从我们刚刚分析的ActivityThread层的引用计数修改后续:refContentProvider 看起

public boolean refContentProvider(IBinder connection, int stable, int unstable) {
    ContentProviderConnection conn;
    ...
    conn = (ContentProviderConnection)connection;
    ...

    synchronized (this) {
        if (stable > 0) {
            conn.numStableIncs += stable;
        }
        stable = conn.stableCount + stable;
        if (stable < 0) {
            throw new IllegalStateException("stableCount < 0: " + stable);
        }

        if (unstable > 0) {
            conn.numUnstableIncs += unstable;
        }
        unstable = conn.unstableCount + unstable;
        if (unstable < 0) {
            throw new IllegalStateException("unstableCount < 0: " + unstable);
        }

        if ((stable+unstable) <= 0) {
            throw new IllegalStateException("ref counts can't go to zero here: stable="
                    + stable + " unstable=" + unstable);
        }
        conn.stableCount = stable;
        conn.unstableCount = unstable;
        return !conn.dead;
    }
}

这个方法很简单,应该不需要再多做分析了吧?就是简单的修改ContentProviderConnection的引用计数值

AMS.incProviderCountLocked

接下来我们看AMS层引用计数的增加,AMS.incProviderCountLocked这个方法的触发时机是在AMS.getContentProviderImpl方法中

ContentProviderConnection incProviderCountLocked(ProcessRecord r,
        final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,
        String callingPackage, String callingTag, boolean stable) {
    if (r != null) {
        for (int i=0; i<r.conProviders.size(); i++) {
            ContentProviderConnection conn = r.conProviders.get(i);
            //如果连接已存在,在其基础上增加引用计数
            if (conn.provider == cpr) {
                if (stable) {
                    conn.stableCount++;
                    conn.numStableIncs++;
                } else {
                    conn.unstableCount++;
                    conn.numUnstableIncs++;
                }
                return conn;
            }
        }
        //新建ContentProviderConnection连接
        ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage);
        //建立关联
        conn.startAssociationIfNeeded();
        if (stable) {
            conn.stableCount = 1;
            conn.numStableIncs = 1;
        } else {
            conn.unstableCount = 1;
            conn.numUnstableIncs = 1;
        }
        //添加连接
        cpr.connections.add(conn);
        r.conProviders.add(conn);
        //建立关联
        startAssociationLocked(r.uid, r.processName, r.getCurProcState(),
                cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
        return conn;
    }
    cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);
    return null;
}

如果调用方进程已存在对应ContentProviderConnection连接,则在其基础上增加引用计数,否则新建连接,然后初始化引用计数值

AMS.decProviderCountLocked

然后是减少引用计数,之前在ActivityThread减引用到0后,会延时调用ActivityThread.completeRemoveProvider方法,在这个方法中会调用到AMS.removeContentProvider方法

public void removeContentProvider(IBinder connection, boolean stable) {
    long ident = Binder.clearCallingIdentity();
    try {
        synchronized (this) {
            ContentProviderConnection conn = (ContentProviderConnection)connection;
            ...
            //减少引用计数
            if (decProviderCountLocked(conn, null, null, stable)) {
                //更新进程优先级
                updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);
            }
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

在这个方法中便会调用AMS.decProviderCountLocked减少引用计数,然后更新进程优先级

boolean decProviderCountLocked(ContentProviderConnection conn,
        ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
    if (conn != null) {
        cpr = conn.provider;
        //减少引用计数值
        if (stable) {
            conn.stableCount--;
        } else {
            conn.unstableCount--;
        }
        if (conn.stableCount == 0 && conn.unstableCount == 0) {
            //停止关联
            conn.stopAssociation();
            //移除连接
            cpr.connections.remove(conn);
            conn.client.conProviders.remove(conn);
            if (conn.client.setProcState < PROCESS_STATE_LAST_ACTIVITY) {
                // The client is more important than last activity -- note the time this
                // is happening, so we keep the old provider process around a bit as last
                // activity to avoid thrashing it.
                if (cpr.proc != null) {
                    cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
                }
            }
            //停止关联
            stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,
                    cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            return true;
        }
        return false;
    }
    cpr.removeExternalProcessHandleLocked(externalProcessToken);
    return false;
}

减少引用计数值,如果stableunstable引用计数均为0,则将这个连接移除

ContentProvider死亡杀死调用方进程的过程

我们前面提到过,ContentProvider所在进程死亡会将与其所有有stable关联的调用方进程杀死,这是怎么做到的呢?在之前的文章中,我们介绍过进程启动时,在调用AMS.attachApplicationLocked时会注册一个App进程死亡回调,我们就从进程死亡,触发死亡回调开始分析

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    ...
    //注册App进程死亡回调
    AppDeathRecipient adr = new AppDeathRecipient(
            app, pid, thread);
    thread.asBinder().linkToDeath(adr, 0);
    app.deathRecipient = adr;
    ...
}

注册了死亡回调后,如果对应binder进程死亡,便会回调IBinder.DeathRecipient.binderDied方法,我们来看一下AppDeathRecipient对这个方法的实现

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

    AppDeathRecipient(ProcessRecord app, int pid,
            IApplicationThread thread) {
        mApp = app;
        mPid = pid;
        mAppThread = thread;
    }

    @Override
    public void binderDied() {
        synchronized(ActivityManagerService.this) {
            appDiedLocked(mApp, mPid, mAppThread, true, null);
        }
    }
}

直接转手调用AMS.appDiedLocked方法,然后经过handleAppDiedLocked调用到cleanUpApplicationRecordLocked方法中

final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
        boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
    ...

    boolean restart = false;

    // Remove published content providers.
    //清除已发布的ContentProvider
    for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
        ContentProviderRecord cpr = app.pubProviders.valueAt(i);
        if (cpr.proc != app) {
            // If the hosting process record isn't really us, bail out
            continue;
        }
        final boolean alwaysRemove = app.bad || !allowRestart;
        final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
        if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
            // We left the provider in the launching list, need to
            // restart it.
            restart = true;
        }

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

    // Take care of any launching providers waiting for this process.
    //清除正在启动中的ContentProvider
    if (cleanupAppInLaunchingProvidersLocked(app, false)) {
        mProcessList.noteProcessDiedLocked(app);
        restart = true;
    }

    // Unregister from connected content providers.
    //清除已连接的ContentProvider
    if (!app.conProviders.isEmpty()) {
        for (int i = app.conProviders.size() - 1; i >= 0; i--) {
            ContentProviderConnection conn = app.conProviders.get(i);
            conn.provider.connections.remove(conn);
            stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
                    conn.provider.appInfo.longVersionCode, conn.provider.name,
                    conn.provider.info.processName);
        }
        app.conProviders.clear();
    }
    ...
}

可以看到,这个方法中遍历了ProcessRecord.pubProviders,逐个对发布的ContentProvider调用removeDyingProviderLocked方法执行移除操作

private final boolean removeDyingProviderLocked(ProcessRecord proc,
        ContentProviderRecord cpr, boolean always) {
    boolean inLaunching = mLaunchingProviders.contains(cpr);
    if (inLaunching && !always && ++cpr.mRestartCount > ContentProviderRecord.MAX_RETRY_COUNT) {
        // It's being launched but we've reached maximum attempts, force the removal
        always = true;
    }

    if (!inLaunching || always) {
        synchronized (cpr) {
            cpr.launchingApp = null;
            cpr.notifyAll();
        }
        final int userId = UserHandle.getUserId(cpr.uid);
        // Don't remove from provider map if it doesn't match
        // could be a new content provider is starting
        //移除缓存
        if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
            mProviderMap.removeProviderByClass(cpr.name, userId);
        }
        String names[] = cpr.info.authority.split(";");
        for (int j = 0; j < names.length; j++) {
            // Don't remove from provider map if it doesn't match
            // could be a new content provider is starting
            //移除缓存
            if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
                mProviderMap.removeProviderByName(names[j], userId);
            }
        }
    }

    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;
            }
        }
        ProcessRecord capp = conn.client;
        conn.dead = true;
        if (conn.stableCount > 0) {
            if (!capp.isPersistent() && capp.thread != null
                    && capp.pid != 0
                    && capp.pid != MY_PID) {
                //当调用方与被杀死的目标ContentProvider进程间有stable连接
                //并且调用方App进程非persistent进程并且非system_server进程中的情况下
                //杀死调用方进程
                capp.kill("depends on provider "
                        + cpr.name.flattenToShortString()
                        + " in dying proc " + (proc != null ? proc.processName : "??")
                        + " (adj " + (proc != null ? proc.setAdj : "??") + ")",
                        ApplicationExitInfo.REASON_DEPENDENCY_DIED,
                        ApplicationExitInfo.SUBREASON_UNKNOWN,
                        true);
            }
        } else if (capp.thread != null && conn.provider.provider != null) {
            try {
                //通知调用方移除ContentProvider
                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.appInfo.longVersionCode, cpr.name, cpr.info.processName);
            }
        }
    }

    if (inLaunching && always) {
        mLaunchingProviders.remove(cpr);
        cpr.mRestartCount = 0;
        inLaunching = false;
    }
    return inLaunching;
}

可以看到,在这个方法中遍历了ContentProvider下的所有连接,当发现有其他进程与自己建立了stable连接(conn.stableCount > 0),且调用方进程不是persistent进程(常驻进程,只有拥有系统签名的App设置这个属性才生效),也不是运行在system_server进程,调用ProcessRecord.kill方法直接杀死进程,对于没有建立stable连接的调用方进程,调用IApplicationThread.unstableProviderDied方法通知调用方进程移除相应的ContentProvider

所以,使用ContentProvider是有一定风险的,大家要注意规避

总结

到这里,整个Framework层关于ContentProvider的内容应该都分析完了,希望大家看完后能获得一些收获,接下来的文章应该会去分析Service相关源码,敬请期待~

你可能感兴趣的:(android)