ContentProvider详解

本文深入解析了Android系统中A进程如何访问B进程的ContentProvider,详细介绍了查询流程、连接类型、权限管理、进程间的连接建立与释放,以及AMS与应用进程之间的交互过程。内容涵盖从ContentResolver发起请求,到 AMS 管理ContentProvider的整个生命周期。

本文基于android 12.0
主要讲解的是A进程访问B进程的ContentProvider的流程, 主要涉及到的代码路径:

frameworks/base/core/java/android/content/ContentResolver.java
frameworks/base/core/java/android/app/ContextImpl$ApplicationContentResolver.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderRecord.java
frameworks/base/services/core/java/com/android/server/am/ContentProviderConnection.java

查询和发布流程

进程A查询ContentProvider

运行在A进程中,即客户端进程。这里需要注意发生anr时若堆栈卡在acquireProvider则是跟system server端通信;若是访问IContentProvider的接口,则是跟B进程(provider的server端)通信。

stable 和 unstable连接

代表client端和server端的连接,query方式若失败,还会重新创建stable连接。只有query和其他操作同时进行时,ContentProviderConnection的unstableCount和stableCount才会同时为1。

  1. stable : call/insert/delete/update等方式建立的都是stable连接;对端挂掉,client会被级联查杀。
  2. unstable :query建立的是unstable连接;对端挂掉,client端没事。
    在这里插入图片描述

ContentResolver#call

@Override
    public final @Nullable Bundle call(@NonNull String authority, @NonNull String method,
            @Nullable String arg, @Nullable Bundle extras) {
   
   
        Objects.requireNonNull(authority, "authority");
        Objects.requireNonNull(method, "method");

        try {
   
   
            if (mWrapped != null) return mWrapped.call(authority, method, arg, extras);
        } catch (RemoteException e) {
   
   
            return null;
        }
		// call方法默认去获取stable的provider
        IContentProvider provider = acquireProvider(authority);
		// 获取不到provider,抛出如下log的异常
        if (provider == null) {
   
   
            throw new IllegalArgumentException("Unknown authority " + authority);
        }
        try {
   
   
			// call流程,binder对端是B进程,非system server
            final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
                    extras);
            Bundle.setDefusable(res, true);
            return res;
        } catch (RemoteException e) {
   
   
            return null;
        } finally {
   
   
			// 访问完后都会释放provider
            releaseProvider(provider);
        }
    }

ActivityThread#acquireProvider

  1. 本地存在对应的IConetntProvider实例则直接获取
  2. 不存在则向AMS查询
    a. 对应的server端provider已发布,则直接执行本地的install操作。
    b. server端provider未发布,则本地先wait,等待对方发布成功被唤醒,超时时间为20s。
    @UnsupportedAppUsage
    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
   
   
		// 先在本地查询是否有保存这个auth对应的IContentProvider,有则直接返回
		// 1.运行在自己进程的provider
        // 2. 已经访问过的其他进程的provider
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
   
   
            return provider;
        }
		// 根据auth,userId封装获取provider key
        ContentProviderHolder holder = null;
        final ProviderKey key = getGetProviderKey(auth, userId);
        try {
   
   
            synchronized (key) {
   
   
				// 不存在通过AMS查询 , 直接返回一个ContentProviderHolder的实例
                holder = ActivityManager.getService().getContentProvider(
                        getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
            	// 如果对端进程不存在或进程存在provider未发布,则需要等待对端provider发布成功
                if (holder != null && holder.provider == null && !holder.mLocal) {
   
   
                    synchronized (key.mLock) {
   
   
						// 等待provider进程发布provider完成,超时时间为20s
						// 会在ContentProviderRecord的notifyContentProviderPublishStatus方法回调时notify
                        key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
						// notify后,将key中的holder赋值给holder
                        holder = key.mHolder;
                    }
					// 如果超时仍未收到notify,holder会为null
                    if (holder != null && holder.provider == null) {
   
   
                        // probably timed out
                        holder = null;
                    }
                }
            }
        } catch (RemoteException ex) {
   
   
            throw ex.rethrowFromSystemServer();
        } catch (InterruptedException e) {
   
   
            holder = null;
        } finally {
   
   
            // Clear the holder from the key since the key itself is never cleared.
            synchronized (key.mLock) {
   
   
                key.mHolder = null;
            }
        }
		// 打印失败log,user未解锁情况大部分不允许访问provider
        if (holder == null) {
   
   
            if (UserManager.get(c).isUserUnlocked(userId)) {
   
   
                Slog.e(TAG, "Failed to find provider info for " + auth);
            } else {
   
   
                Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");
            }
            return null;
        }
		// 在本进程中installProvider,增加引用计数
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

ActivityThread#acquireExistingProvider

获取本地存在的IContentProvider对象

    @UnsupportedAppUsage
    public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
   
   
        synchronized (mProviderMap) {
   
   
			// 通过key获取provider clent 对象,如果不为null,说明当前进程已经install该provider
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
   
   
                return null;
            }

            IContentProvider provider = pr.mProvider;
			// 对端进程挂掉了, 返回null,在acquireProvider重新向ams获取
            IBinder jBinder = provider.asBinder();
            if (!jBinder.isBinderAlive()) {
   
   
                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
                        + ": existing object's process dead");
                handleUnstableProviderDiedLocked(jBinder, true);
                return null;
            }
            // 查看是否是自己进程的provider
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
			// 增加访问其他进程ContentProvider的引用计数
            // 否则引用计数从来不需要被释放
            if (prc != null) {
   
   
                incProviderRefLocked(prc,stable);
            }
            return provider;
        }
    }

Ams查询provider

运行在system server进程,android 12.0后ContentProvider相关的逻辑都从AMS.java中拆出到ContentProviderHelper.java中了。
在这里插入图片描述

ContentProviderHelper#getContentProviderImpl

代码过于庞大,去除一些浅显易懂的判断代码

查询过程中有如下三种场景:
  1. B进程存在且对应的provider已经发布,直接创建连接
  2. B进程存在,provider尚未发布,通知B进程去发布provider
  3. B进程不存在,去创建B进程
返回null的情况
  1. 对端进程死亡,且不是最后一次引用
  2. provider不在运行,且pms解析provider信息失败
  3. provider所在user不在运行中
  4. 是否需要权限review 页面,一般为false
  5. pms解析provider所在app的application信息失败
  6. provider所在进程不存在&短时间crash多次拉起进程失败
  7. 经历过上面场景2.3后,launchingApp为null
  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;
        final int expectedUserId = userId;
        synchronized (mService) {
   
   
            long startTime = SystemClock.uptimeMillis();
			// 对caller判断
			..........
            boolean checkCrossUser = true;

            checkTime(startTime, "getContentProviderImpl: getProviderByName");

            // 检查mProviderMap中该provider是不是已经发布了
            cpr = mProviderMap.getProviderByName(name, userId);
            // 如果当前查询的user不是system,且要查找的provider是只能存在system的单例
     		..........

            ProcessRecord dyingProc = null;
            if (cpr != null && cpr.proc != null) {
   
   
				// 判断当前provider是不是已经在运行了
                providerRunning = !cpr.proc.isKilled();

                // 如果provider所在进程被AMS查杀了,但是没有回调appDiedLocked方法,标记为dyingProc
              ..............
			// 1. 当前provider正在运行
            if (providerRunning) {
   
   
                cpi = cpr.info;
				// canRunHere主要针对自己请求的provider运行在自己进程中的情况
                if (r != null && cpr.canRunHere(r)) {
   
   
                    checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
                            cpr.name.flattenToShortString(), startTime);

                   //1.1 这种情况,无须制造一个connection,且local为true
                    ContentProviderHolder holder = cpr.newHolder(null, true);
                    // 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
         ..........
				// 清除caling相关信息,以免校验callingUid等问题
                final long origId = Binder.clearCallingIdentity();
                try {
   
   
                    checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");

                    // 1.2 建立provider之间的链接.
                    conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage,
                            callingTag, stable, true, startTime, mService.mProcessList,
                            expectedUserId);

                    checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                    final int verifiedAdj = cpr.proc.mState.getVerifiedAdj();
					// 更新进程优先级
                    boolean success = mService.updateOomAdjLocked(cpr.proc,
                            OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
                    // 如果verify adj和计算后的adj不相等且provider的持有进程死亡
                    if (success && verifiedAdj != cpr.proc.mState.getSetAdj()
                            && !isProcessAliveLocked(cpr.proc)) {
   
   
                        success = false;
                    }
                    maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
                    checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
                    // 如果更新adj失败(持有provider的进程死亡等)
                    if (!success) {
   
   
                       //打印异常log
                        Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
                                + " is crashing; detaching " + r);
                        boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
                                false, false);
                        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
                        providerRunning = false;
                        conn = null;
                        dyingProc = cpr.proc;
                    } else {
   
   
						// provider对应进程存在,设置当前adj为erify adj
                        cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
                    }
                } finally {
   
   
					// 恢复calling相关信息
                    Binder.restoreCallingIdentity(origId);
                }
            }
			// provider未发布
            if (!providerRunning) {
   
   
                try {
   
   
					// 获取provider的info信息
                    checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
                    cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
                            ActivityManagerService.STOCK_PM_FLAGS
                                    | PackageManager.GET_URI_PERMISSION_PATTERNS,
                            userId);
                    checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
                } catch (RemoteException ex) {
   
   
                }
				// 获取失败,直接返回null
                if (cpi == null) {
   
   
                    return null;
                }
                // 跟上面一次的判读一样
                boolean singleton = mService.isSingleton(
                        cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)
                            && mService.isValidSingletonCall(
                                    r == null ? callingUid : r.uid,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值