ContentProvider-发布过程

Post on Oct 05, 2022 by Wei Lin

发布ContentProvider

ContentProvider的启动流程分析—CSDN

Android ContentProvider启动流程

拉起进程

创建ContentProvider是随着进程启动时进行的,在进程启动后,进入到进程的main方法,相关方法调用如下。

ActivityThread.main() -> 
ActivityThread.attach() -> 
AMS.attachApplication() -> 
AMS.attachApplicationLocked() {
    Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
    msg.obj = app;
    mHandler.sendMessageDelayed(msg,
             ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
} ->
AMS.attachApplicationLocked() ->
ApplicationThread.bindApplication() ->
ActivityThread.handleBindApplication() ->
AT.handleBindApplication()

AMS.attachApplicationLocked()

主要是通过bindApplication()创建Application和启动其它三大组件(如果需要的话),对Provider拉起的进程来说,主要看bindApplication()过程。

获取当前APP的所有ProviderInfo信息:

List<ProviderInfo> providers = normalMode
                                    ? mCpHelper.generateApplicationProvidersLocked(app)
                                    : null;
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);

ContentProviderHelper.generateApplicationProvidersLocked()如下:

List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) {
    final List<ProviderInfo> providers;
    try {
        providers = AppGlobals.getPackageManager().queryContentProviders(
                            app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS
                                | PackageManager.GET_URI_PERMISSION_PATTERNS
                                | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null)
                        .getList();
    } catch (RemoteException ex) {
        return null;
    }

传入到后续创建进程流程中:

thread.bindApplication(processName, appInfo,
        app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
        providerList, null, profilerInfo, null, null, null, testMode,
        mBinderTransactionTrackingEnabled, enableTrackAllocation,
        isRestrictedBackupMode || !normalMode, app.isPersistent(),
        new Configuration(app.getWindowProcessController().getConfiguration()),
        app.getCompat(), getCommonServicesLocked(app.isolated),
        mCoreSettingsObserver.getCoreSettingsLocked(),
        buildSerial, autofillOptions, contentCaptureOptions,
        app.getDisabledCompatChanges(), serializedSystemFontMap,
        app.getStartElapsedTime(), app.getStartUptime());

ApplicationThread.bindApplication()

在这里将相关参数封装到AppBindData对象中,然后通过H.BIND_APPLICATION消息发送到ActivityThread。

data.providers = providerList.getList();
sendMessage(H.BIND_APPLICATION, data);

AT.handleBindApplication()

private void handleBindApplication(AppBindData data) {...}

安装Provider:

// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
    if (!ArrayUtils.isEmpty(data.providers)) {
        installContentProviders(app, data.providers);
    }
}

AT.installContentProviders()

这里主要进行两步。

(1) installProvider():将进程中的所有Provider安装到本进程中

List providers来自于AMS.attachApplicationLocked(),实际上通过PMS获得该APP下所有ProviderInfo。

(2) publishContentProviders():将Provider信息发布到AMS中

其它APP访问Provider时通过AMS查询。

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

    for (ProviderInfo cpi : providers) {
        if (DEBUG_PROVIDER) {
            StringBuilder buf = new StringBuilder(128);
            buf.append("Pub ");
            buf.append(cpi.authority);
            buf.append(": ");
            buf.append(cpi.name);
            Log.i(TAG, buf.toString());
        }
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }

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

Provider的install及onCreate()过程

与发布过程在installContentProviders()前是一样的。

ActivityThread.installContentProviders() ->
ActivityThread.installProvider() ->
ContentProvider.attachInfo() ->
ContentProvider.onCreate()

在ActivityThread.installProvider()方法中,调用attachInfo()的作用是将新创建的ContentProvider的ProviderInfo和Context关联起来,最后调用该Provider的onCreate()方法初始化ContentProvider。

通常会在onCreate()中完成对数据库的创建和升级等操作,返回true表示内容提供器初始化成功,返回false则表示失败。

AT.installProvider()

private ContentProviderHolder installProvider(Context context,
        ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {...}

AT.installProvider()中的主要作用为将跨进程的Provider相关数据保存到本进程中,执行以下流程:

  • 通过ProviderInfo info获取Provider所在进程的Context
  • 通过Context获取到Provider对象及Binder引用
  • 将Provider相关信息封装到ContentProviderHolder对象中
  • 更新该Provider的引用计数
private ContentProviderHolder installProvider(Context context,
         ContentProviderHolder holder, ProviderInfo info,
         boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // holder为null表示还没有install过
    if (holder == null || holder.provider == null) {
        if (DEBUG_PROVIDER || noisy) {
            Slog.d(TAG, "Loading provider " + info.authority + ": "
                    + info.name);
        }
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        //...
        //获取Provider所在进程的Context

        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            if (packageInfo == null) {
                // System startup case.
                packageInfo = getSystemContext().mPackageInfo;
            }
            // 创建provider对象
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            // 获取IContentProvider对象,用于跨进程binder引用
            provider = localProvider.getIContentProvider();
            //...
            // provider的attach,其中最重要的是会执行provider的onCreate
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            //...
        }
    } else {
        provider = holder.provider;
        if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                + info.name);
    }
    // provider的对象创建好了,那么接下来需要做的就是数据结构的封装
    // 把provider相关信息保存到ContentProviderHolder对象中
    ContentProviderHolder retHolder;

    synchronized (mProviderMap) {
        if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
                + " / " + info.name);
        IBinder jBinder = provider.asBinder();
        if (localProvider != null) {
            ComponentName cname = new ComponentName(info.packageName, info.name);
            // mLocalProvidersByName的key是component信息,value是对应的ProviderClientReocrd
            ProviderClientRecord pr = mLocalProvidersByName.get(cname);
            if (pr != null) {
                // 不为空代表install过
                provider = pr.mProvider;
            } else {
                // 对于新创建的provider,创建其对应的ContentProviderHolder对象
                holder = new ContentProviderHolder(info);
                holder.provider = provider;
                holder.noReleaseNeeded = true;
                pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                // mLocalProviders的key是IContentProvider的binder对象,value是ProviderClientRecord
                // 将封装好的provider放入map中
                mLocalProviders.put(jBinder, pr);
                mLocalProvidersByName.put(cname, pr);
            }
            retHolder = pr.mHolder;
        } else { // 接下来更新引用计数
            // mProviderRefCountMap的key是binder对象,value是ProviderRefCount
            // ProviderRefCount中记录了这个provider的stable和unstable的数量
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                if (DEBUG_PROVIDER) {
                    Slog.v(TAG, "installProvider: lost the race, updating ref count");
                }
                // 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).
                if (!noReleaseNeeded) {
                    incProviderRefLocked(prc, stable);
                    try {
                        ActivityManager.getService().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;
}

ContentProvider.attachInfo()

调用到目标Provider中,授予读写权限并执行onCreate()。

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

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    mNoPerms = testing;
    mCallingAttributionSource = new ThreadLocal<>();

    /*
     * Only allow it to be set once, so after the content service gives
     * this to us clients can't change it.
     */
    if (mContext == null) {
        mContext = context;
        if (context != null && mTransport != null) {
            mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                    Context.APP_OPS_SERVICE);
        }
        mMyUid = Process.myUid();
        if (info != null) {
            setReadPermission(info.readPermission);
            setWritePermission(info.writePermission);
            setPathPermissions(info.pathPermissions);
            mExported = info.exported;
            mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
            setAuthorities(info.authority);
        }
        if (Build.IS_DEBUGGABLE) {
            setTransportLoggingEnabled(Log.isLoggable(getClass().getSimpleName(),
                    Log.VERBOSE));
        }
        ContentProvider.this.onCreate();
    }
}

MyContentProvider.onCreate()

执行自定义Provider对象的onCreate()。

Provider的publish过程

Provider进程端安装Provider过程:

ActivityThread.installContentProviders() ->
AMS.publishContentProviders() ->
ContentProviderHelper.publishContentProviders(){
removeMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG) }

AMS.publishContentProviders()的工作主要就是将ContentProvider的信息保存到AMS服务中去。AMS服务保存ContentProvider的信息主要是在类ProviderMap中,它里边有两种保存的Provider信息的集合:

  • ProviderByClass:以ComponentName为key保存了ContentProviderRecord的信息;
  • ProviderByName:以authority为key保存了ContentProviderRecord的信息。

AMS.publishContentProviders()

实际上的发布过程是在CPH.publishContentProviders()中完成的。

public final void publishContentProviders(IApplicationThread caller,
        List<ContentProviderHolder> providers) {
    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
        final int maxLength = 256;
        final StringBuilder sb = new StringBuilder(maxLength);
        sb.append("publishContentProviders: ");
        if (providers != null) {
            boolean first = true;
            for (int i = 0, size = providers.size(); i < size; i++) {
                final ContentProviderHolder holder = providers.get(i);
                if (holder != null && holder.info != null && holder.info.authority != null) {
                    final int len = holder.info.authority.length();
                    if (sb.length() + len > maxLength) {
                        sb.append("[[TRUNCATED]]");
                        break;
                    }
                    if (!first) {
                        sb.append(';');
                    } else {
                        first = false;
                    }
                    sb.append(holder.info.authority);
                }
            }
        }
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, sb.toString());
    }
    try {
        mCpHelper.publishContentProviders(caller, providers);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }
}

CPH.publishContentProviders()

​ 在这里将Provider的信息添加到system_server进程中,实际上就是CPH的mProviderMap数据中,以后如果有其它进程访问,则不再执行onCreate(),而是直接通过AMS返回对象。

本步骤主要进行以下流程:

  • 遍历Provider所在进程下所有已经安装的provider,将新的Provider保存到mProviderMap中;
  • 从正在启动列表mLaunchingProviders中移除该Provider信息,因为已成功启动;
  • 移除该Provider的超时消息;
  • 唤醒正在等待该Provider的线程
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
    if (providers == null) {
        return;
    }

    mService.enforceNotIsolatedOrSdkSandboxCaller("publishContentProviders");
    synchronized (mService) {
        // 获取Provider所在进程对象
        final ProcessRecord r = mService.getRecordForAppLOSP(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();
        boolean providersPublished = false;
        // 遍历该进程下所有已经安装的provider
        for (int i = 0, size = providers.size(); i < size; i++) {
            ContentProviderHolder src = providers.get(i);
            if (src == null || src.info == null || src.provider == null) {
                continue;
            }
            ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
            if (dst == null) {
                continue;
            }
            if (DEBUG_MU) {
                Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
            }
            providersPublished = true;
            // 将这个providerRecord放入到mProviderMap中
            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);
            }
            // 如果该Provider在正在启动的列表中,则移除
            boolean wasInLaunchingProviders = false;
            for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
                if (mLaunchingProviders.get(j) == dst) {
                    mLaunchingProviders.remove(j);
                    wasInLaunchingProviders = true;
                    j--;
                    numLaunching--;
                }
            }
            // 移除超时消息
            if (wasInLaunchingProviders) {
                mService.mHandler.removeMessages(
                        ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
                mService.mHandler.removeMessages(
                        ActivityManagerService.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, mService.mProcessStats);
            // 唤醒正在等待该Provider的线程
            synchronized (dst) {
                dst.provider = src.provider;
                dst.setProcess(r);
                dst.notifyAll();
                dst.onProviderPublishStatusLocked(true);
            }
            dst.mRestartCount = 0;
            if (hasProviderConnectionLocked(r)) {
                r.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
            }
        }

        // update the app's oom adj value and each provider's usage stats
        if (providersPublished) {
            mService.updateOomAdjLocked(r, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
            for (int i = 0, size = providers.size(); i < size; i++) {
                ContentProviderHolder src = providers.get(i);
                if (src == null || src.info == null || src.provider == null) {
                    continue;
                }
                maybeUpdateProviderUsageStatsLocked(r,
                        src.info.packageName, src.info.authority);
            }
        }

        Binder.restoreCallingIdentity(origId);
    }
}