本文基于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

@Overridepublic 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的providerIContentProvider provider = acquireProvider(authority);// 获取不到provider,抛出如下log的异常if (provider == null) {throw new IllegalArgumentException("Unknown authority " + authority);}try {// call流程,binder对端是B进程,非system serverfinal Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,extras);Bundle.setDefusable(res, true);return res;} catch (RemoteException e) {return null;} finally {// 访问完后都会释放providerreleaseProvider(provider);}}

ActivityThread#acquireProvider

  1. 本地存在对应的IConetntProvider实例则直接获取
  2. 不存在则向AMS查询
    a. 对应的server端provider已发布,则直接执行本地的install操作。
    b. server端provider未发布,则本地先wait,等待对方发布成功被唤醒,超时时间为20s。
    @UnsupportedAppUsagepublic final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {// 先在本地查询是否有保存这个auth对应的IContentProvider,有则直接返回// 1.运行在自己进程的provider// 2. 已经访问过的其他进程的providerfinal IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);if (provider != null) {return provider;}// 根据auth,userId封装获取provider keyContentProviderHolder 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方法回调时notifykey.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);// notify后,将key中的holder赋值给holderholder = key.mHolder;}// 如果超时仍未收到notify,holder会为nullif (holder != null && holder.provider == null) {// probably timed outholder = 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未解锁情况大部分不允许访问providerif (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对象

    @UnsupportedAppUsagepublic final IContentProvider acquireExistingProvider(Context c, String auth, int userId, boolean stable) {synchronized (mProviderMap) {// 通过key获取provider clent 对象,如果不为null,说明当前进程已经install该providerfinal 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;}// 查看是否是自己进程的providerProviderRefCount 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为trueContentProviderHolder 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) {//打印异常logSlog.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 providerproviderRunning = false;conn = null;dyingProc = cpr.proc;} else {// provider对应进程存在,设置当前adj为erify adjcpr.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) {}// 获取失败,直接返回nullif (cpi == null) {return null;}// 跟上面一次的判读一样boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags)&& mService.isValidSingletonCall(r == null ? callingUid : r.uid, cpi.applicationInfo.uid);// 一样if (singleton) {userId = UserHandle.USER_SYSTEM;}cpi.applicationInfo = mService.getAppInfoForUser(cpi.applicationInfo, userId);checkTime(startTime, "getContentProviderImpl: got app info for user");checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, !singleton,name, startTime);// 系统还没ready,抛出异常........// user未运行,返回nullif (!mService.mUserController.isUserRunning(userId, 0)) {Slog.w(TAG, "Unable to launch app "+ cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid+ " for provider " + name + ": user " + userId + " is stopped");return null;}// 构造componentComponentName comp = new ComponentName(cpi.packageName, cpi.name);checkTime(startTime, "getContentProviderImpl: before getProviderByClass");// 检查AMS这边是否有保存该providercpr = mProviderMap.getProviderByClass(comp, userId);checkTime(startTime, "getContentProviderImpl: after getProviderByClass");boolean firstClass = cpr == null;// 第一次创建if (firstClass) {final long ident = Binder.clearCallingIdentity();// If permissions need a review before any of the app components can run,// we return no provider and launch a review activity if the calling app// is in the foreground.if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId, mService.mContext)) {return null;}try {checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");// 获取provider相关的application信息ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(cpi.applicationInfo.packageName,ActivityManagerService.STOCK_PM_FLAGS, userId);checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");if (ai == null) {Slog.w(TAG, "No package info for content provider " + cpi.name);return null;}// 构造新的content provider对象ai = mService.getAppInfoForUser(ai, userId);cpr = new ContentProviderRecord(mService, 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.cpr = new ContentProviderRecord(cpr);// This is sort of "firstClass"firstClass = true;}checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");if (r != null && cpr.canRunHere(r)) {// 上面一样的逻辑return cpr.newHolder(null, true);}// 看看当前请求的provider是不是在mLaunchingProviders// 如果一个 provider被请求过,但是因为对方进程没有启动没有publishProvider// 则会加入mLaunchingProviders中final int numLaunchingProviders = mLaunchingProviders.size();int i;for (i = 0; i < numLaunchingProviders; i++) {if (mLaunchingProviders.get(i) == cpr) {break;}}// 没有正在启动该provider进程if (i >= numLaunchingProviders) {final long origId = Binder.clearCallingIdentity();try {.....// Content provider is now in use, its package can't be stopped.try {checkTime(startTime,"getContentProviderImpl: before set stopped state");AppGlobals.getPackageManager().setPackageStoppedState(cpr.appInfo.packageName, false, userId);checkTime(startTime, "getContentProviderImpl: after set stopped state");} catch (RemoteException e) {} catch (IllegalArgumentException e) {Slog.w(TAG, "Failed trying to unstop package "+ cpr.appInfo.packageName + ": " + e);}// Use existing process if already startedcheckTime(startTime, "getContentProviderImpl: looking for process record");//进程已经存在ProcessRecord proc = mService.getProcessRecordLocked(cpi.processName, cpr.appInfo.uid);IApplicationThread thread;if (proc != null && (thread = proc.getThread()) != null&& !proc.isKilled()) {// 2. 进程存在,但是没有对应的provider,则通知app端install providerfinal ProcessProviderRecord pr = proc.mProviders;if (!pr.hasProvider(cpi.name)) {checkTime(startTime, "getContentProviderImpl: scheduling install");pr.installProvider(cpi.name, cpr);try {thread.scheduleInstallProvider(cpi);} catch (RemoteException e) {}}} else {// 3. 进程不存在,启动进程checkTime(startTime, "getContentProviderImpl: before start process");proc = mService.startProcessLocked(cpi.processName, cpr.appInfo, false, 0,new HostingRecord("content provider",new ComponentName(cpi.applicationInfo.packageName, cpi.name)),Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);checkTime(startTime, "getContentProviderImpl: after start process");// 短时间内crash多次,启动失败,打印异常logif (proc == null) {Slog.w(TAG, "Unable to launch app "+ cpi.applicationInfo.packageName + "/"+ cpi.applicationInfo.uid + " for provider " + name+ ": process is bad");return null;}}cpr.launchingApp = proc;// 将当前provider加到laucnhing列表,避免二次创建进程mLaunchingProviders.add(cpr);} finally {Binder.restoreCallingIdentity(origId);}}checkTime(startTime, "getContentProviderImpl: updating data structures");// 第一次创建,将当前provider加入mProviderMap map中if (firstClass) {mProviderMap.putProviderByClass(comp, cpr);}// 根据name加入map中mProviderMap.putProviderByName(name, cpr);// 创建连接conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,stable, false, startTime, mService.mProcessList, expectedUserId);// 等待对端publish providerif (conn != null) {conn.waiting = true;}}checkTime(startTime, "getContentProviderImpl: done!");mService.grantImplicitAccess(userId, null, callingUid,UserHandle.getAppId(cpi.applicationInfo.uid));if (caller != null) {// The client will be waiting, and we'll notify it when the provider is ready.synchronized (cpr) {if (cpr.provider == null) {if (cpr.launchingApp == null) {Slog.w(TAG, "Unable to launch app "+ cpi.applicationInfo.packageName + "/"+ cpi.applicationInfo.uid + " for provider "+ name + ": launching app became null");EventLogTags.writeAmProviderLostProcess(UserHandle.getUserId(cpi.applicationInfo.uid),cpi.applicationInfo.packageName,cpi.applicationInfo.uid, name);return null;}if (conn != null) {conn.waiting = true;}Message msg = mService.mHandler.obtainMessage(ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG);msg.obj = cpr;mService.mHandler.sendMessageDelayed(msg,ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);}}// Return a holder instance even if we are waiting for the publishing of the// provider, client will check for the holder.provider to see if it needs to wait// for it.// 返回holderreturn cpr.newHolder(conn, false);}}// 以下这些逻辑在S上基本不会走到,超时逻辑移到app端final long timeout =SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;boolean timedOut = false;synchronized (cpr) {while (cpr.provider == null) {if (cpr.launchingApp == null) {Slog.w(TAG, "Unable to launch app 中间B进程的创建&app的初始化过程省略
"+ cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid+ " for provider " + name + ": launching app became null");EventLogTags.writeAmProviderLostProcess(UserHandle.getUserId(cpi.applicationInfo.uid),cpi.applicationInfo.packageName, cpi.applicationInfo.uid, name);return null;}try {final long wait = Math.max(0L, timeout - SystemClock.uptimeMillis());if (DEBUG_MU) {Slog.v(TAG_MU, "Waiting to start provider " + cpr+ " launchingApp=" + cpr.launchingApp + " for " + wait + " ms");}if (conn != null) {conn.waiting = true;}cpr.wait(wait);if (cpr.provider == null) {timedOut = true;break;}} catch (InterruptedException ex) {} finally {if (conn != null) {conn.waiting = false;}}}}if (timedOut) {// Note we do it after releasing the lock.String callerName = "unknown";if (caller != null) {synchronized (mService.mProcLock) {final ProcessRecord record =mService.mProcessList.getLRURecordForAppLOSP(caller);if (record != null) {callerName = record.processName;}}}Slog.wtf(TAG, "Timeout waiting for provider "+ cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid+ " for provider " + name + " providerRunning=" + providerRunning+ " caller=" + callerName + "/" + Binder.getCallingUid());return null;}return cpr.newHolder(conn, false);}

中间B进程的创建&app的初始化过程省略

B进程install Provider

主要运行在B进程,app端install完成后binder ams去publish该provider

install场景有两种,对应上面查询过程中的2 3场景

  1. B进程bind application过程中install
  2. B进程收到ams的回调直接install

ActivityThread#handleBindApplication

ApplicationThread的binder线程发送消息到ActivityThread的主线程中去执行。

  @UnsupportedAppUsageprivate void handleBindApplication(AppBindData data) {......try { .......// 先安装providers(严格备份模式下不会拉起providers)// don't bring up providers in restricted mode; they may depend on the// app's custom Application classif (!data.restrictedBackupMode) {if (!ArrayUtils.isEmpty(data.providers)) {installContentProviders(app, data.providers);}}.......// 再执行application的onCreate逻辑try {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "app.onCreate");mInstrumentation.callApplicationOnCreate(app);Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);} catch (Exception e) {if (!mInstrumentation.onException(app, e)) {throw new RuntimeException("Unable to create application " + app.getClass().getName()+ ": " + e.toString(), e);}}} ......

ActivityThread#installContentProviders

@UnsupportedAppUsageprivate void installContentProviders(Context context, List<ProviderInfo> providers) {final ArrayList<ContentProviderHolder> results = new ArrayList<>();// 遍历providers去安装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);}}// 告知system server 发布完成try {ActivityManager.getService().publishContentProviders(getApplicationThread(), results);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

ActivityThread.java#installProvider

@UnsupportedAppUsageprivate ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;// 这里holder为nullif (holder == null || holder.provider == null) {// 打印provider的debug log.....Context c = null;ApplicationInfo ai = info.applicationInfo;if (context.getPackageName().equals(ai.packageName)) {// 一般走到这里c = context;} else if (mInitialApplication != null &&mInitialApplication.getPackageName().equals(ai.packageName)) {c = mInitialApplication;} else {try {c = context.createPackageContext(ai.packageName,Context.CONTEXT_INCLUDE_CODE);} catch (PackageManager.NameNotFoundException e) {// Ignore}}........try {final java.lang.ClassLoader cl = c.getClassLoader();LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);if (packageInfo == null) {// System startup case.packageInfo = getSystemContext().mPackageInfo;}//如果是本地自己app的providerlocalProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);provider = localProvider.getIContentProvider();if (provider == null) {Slog.e(TAG, "Failed to instantiate class " +info.name + " from sourceDir " +info.applicationInfo.sourceDir);return null;}// 打印contentprovider的初始化的debug logif (DEBUG_PROVIDER) Slog.v(TAG, "Instantiating local provider " + info.name);// XXX Need to create the correct context for this provider.localProvider.attachInfo(c, info);} catch (java.lang.Exception e) {if (!mInstrumentation.onException(null, e)) {throw new RuntimeException("Unable to get provider " + info.name+ ": " + e.toString(), e);}return null;}} else {provider = holder.provider;if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "+ info.name);}ContentProviderHolder retHolder;synchronized (mProviderMap) {// 增加引用计数的debug logif (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider+ " / " + info.name);IBinder jBinder = provider.asBinder();// 如果是本地自己app的providerif (localProvider != null) {ComponentName cname = new ComponentName(info.packageName, info.name);ProviderClientRecord pr = mLocalProvidersByName.get(cname);if (pr != null) {if (DEBUG_PROVIDER) {Slog.v(TAG, "installProvider: lost the race, "+ "using existing local provider");}provider = pr.mProvider;} else {// 第一次创建holder = new ContentProviderHolder(info);holder.provider = provider;holder.noReleaseNeeded = true;// 初始化客户端provider实例,通过providerkey将当前的ProviderClientRecord保存到mroviderMap中pr = installProviderAuthoritiesLocked(provider, localProvider, holder);// 通过IBinder和component保存到对应的map中mLocalProviders.put(jBinder, pr);mLocalProvidersByName.put(cname, pr);}retHolder = pr.mHolder;} else {// 其他app的providerProviderRefCount 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;}

ActivityThread#installProviderAuthoritiesLocked

install 安装的时候会将key添加到mProviderMap中去,这样下次查询就可以查到

 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);if (provider != null) {// If this provider is hosted by the core OS and cannot be upgraded,// then I guess we're okay doing blocking calls to it.for (String auth : auths) {switch (auth) {case ContactsContract.AUTHORITY:case CallLog.AUTHORITY:case CallLog.SHADOW_AUTHORITY:case BlockedNumberContract.AUTHORITY:case CalendarContract.AUTHORITY:case Downloads.Impl.AUTHORITY:case "telephony":Binder.allowBlocking(provider.asBinder());}}}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;}

Ams 发布B进程的provider

发布成功后通知A进程

超时20s没有发布成功也会通知A进程client端

进程死亡也会通知到A进程client端

ContentProviderHelper#publishContentProviders

 void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {if (providers == null) {return;}mService.enforceNotIsolatedCaller("publishContentProviders");// 需要拿AMS 锁synchronized (mService) {// 通过app端的binder对象获取system server端对应的进程实例final ProcessRecord r = mService.getRecordForAppLOSP(caller);// app端发布时,传入的caller对应的进程实例得存在if (r == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + Binder.getCallingPid()+ ") when publishing content providers");}// 清除calling 标识final long origId = Binder.clearCallingIdentity();boolean providersPublished = false;// 遍历providersfor (int i = 0, size = providers.size(); i < size; i++) {ContentProviderHolder src = providers.get(i);// holder未被初始化赋值,跳出当前循环if (src == null || src.info == null || src.provider == null) {continue;}// 从进程的ProcessProviderRecord中通过provider的name获取ContentProviderRecord实例// ProcessProviderRecord中mPubProviders列表在CPH的generateApplicationProvidersLocked中初始化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;// 按照Component和name添加provider实例到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是否是需要等待拉起的provider,是的话从mLaunchingProviders中移除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--;}}// 移除content provider的超时消息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);synchronized (dst) {dst.provider = src.provider;dst.setProcess(r);// 唤醒,告知app端可以获取holder了dst.notifyAll();dst.onProviderPublishStatusLocked(true);}dst.mRestartCount = 0;}// update the app's oom adj value and each provider's usage statsif (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);}}

ContentProviderRecord#onProviderPublishStatusLocked

   /*** Notify all clients that the provider has been published and ready to use,* or timed out.** @param status true: successfully published; false: timed out*/void onProviderPublishStatusLocked(boolean status) {final int numOfConns = connections.size();for (int i = 0; i < numOfConns; i++) {final ContentProviderConnection conn = connections.get(i);if (conn.waiting && conn.client != null) {final ProcessRecord client = conn.client;if (!status) {if (launchingApp == null) {Slog.w(TAG_AM, "Unable to launch app "+ appInfo.packageName + "/"+ appInfo.uid + " for provider "+ info.authority + ": launching app became null");EventLogTags.writeAmProviderLostProcess(UserHandle.getUserId(appInfo.uid),appInfo.packageName,appInfo.uid, info.authority);} else {Slog.wtf(TAG_AM, "Timeout waiting for provider "+ appInfo.packageName + "/"+ appInfo.uid + " for provider "+ info.authority+ " caller=" + client);}}final IApplicationThread thread = client.getThread();if (thread != null) {try {// 告知app端发布成功,可以去获取holderthread.notifyContentProviderPublishStatus(newHolder(status ? conn : null, false),info.authority, conn.mExpectedUserId, status);} catch (RemoteException e) {}}}conn.waiting = false;}}

A进程成功获取到IContentProvider

A进程收到binder回调,并notify 告知acquireProvider不再wait

ApplicationThread#notifyContentProviderPublishStatus

        @Overridepublic void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,@NonNull String authorities, int userId, boolean published) {final String auths[] = authorities.split(";");for (String auth: auths) {final ProviderKey key = getGetProviderKey(auth, userId);synchronized (key.mLock) {key.mHolder = holder;key.mLock.notifyAll();}}}

引用计数添加和减少

A获取到B进程的IContentProvider后会创建一个连接,具体表现为ContentProviderConnection。

    @GuardedBy("mLock")private int mStableCount;@GuardedBy("mLock")private int mUnstableCount;

引用计数增加

A进程app端流程同上面查询流程,不做赘述。
system server端如下:

ContentProviderHelper#incProviderCountLocked

 @GuardedBy("mService")private ContentProviderConnection incProviderCountLocked(ProcessRecord r,final ContentProviderRecord cpr, IBinder externalProcessToken, int callingUid,String callingPackage, String callingTag, boolean stable, boolean updateLru,long startTime, ProcessList processList, @UserIdInt int expectedUserId) {if (r == null) {cpr.addExternalProcessHandleLocked(externalProcessToken, callingUid, callingTag);return null;}// 获取caller进程的ProcessProviderRecordfinal ProcessProviderRecord pr = r.mProviders;for (int i = 0, size = pr.numberOfProviderConnections(); i < size; i++) {ContentProviderConnection conn = pr.getProviderConnectionAt(i);// 如果当前要获取的provider在该caller进程中已有相关连接,则计数+1if (conn.provider == cpr) {conn.incrementCount(stable);// 获取链接直接返回return conn;}}//否则 新建一个content provider的连接ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage,expectedUserId);conn.startAssociationIfNeeded();// 初始化count为1conn.initializeCount(stable);// 将当前连接添加到content provider的连接列表里cpr.connections.add(conn);// 将当前链接保存到进程provider列表里pr.addProviderConnection(conn);mService.startAssociationLocked(r.uid, r.processName, r.mState.getCurProcState(),cpr.uid, cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);if (updateLru && cpr.proc != null&& r.mState.getSetAdj() <= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {// caller的adj <= 250,更新持有provider进程的adjcheckTime(startTime, "getContentProviderImpl: before updateLruProcess");processList.updateLruProcessLocked(cpr.proc, false, null);checkTime(startTime, "getContentProviderImpl: after updateLruProcess");}return conn;}

引用计数减少

app端

ActivityThread#releaseProvider

 @UnsupportedAppUsagepublic final boolean releaseProvider(IContentProvider provider, boolean stable) {if (provider == null) {return false;}IBinder jBinder = provider.asBinder();synchronized (mProviderMap) {// 其他进程的providerProviderRefCount prc = mProviderRefCountMap.get(jBinder);// 自己进程的provider,没有引用计数,无需releaseif (prc == null) {// The provider has no ref count, no release is needed.return false;}boolean lastRef = false;// call 方式调用if (stable) {if (prc.stableCount == 0) {return false;}prc.stableCount -= 1;// 是否是其他进程providerif (prc.stableCount == 0) {// stable 和unstable 引用计数均为0,则此次是最后一个引用lastRef = prc.unstableCount == 0;try {// 告知amsActivityManager.getService().refContentProvider(prc.holder.connection, -1, lastRef ? 1 : 0);} catch (RemoteException e) {//do nothing content provider object is dead any way}}} else {if (prc.unstableCount == 0) {return false;}prc.unstableCount -= 1;if (prc.unstableCount == 0) {// 当前是最后一个引用lastRef = prc.stableCount == 0;if (!lastRef) {try {// 还有stable引用的话告知amsActivityManager.getService().refContentProvider(prc.holder.connection, 0, -1);} catch (RemoteException e) {//do nothing content provider object is dead any way}}}}if (lastRef) {// 当前引用没等待被移除if (!prc.removePending) {// 延时1s后从本地移除prc.removePending = true;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;}}

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.if (DEBUG_PROVIDER) Slog.v(TAG, "completeRemoveProvider: lost the race, "+ "provider still in use");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();// 从mProviderMap中移除,这样下次acquire时,acquireExistingProvider就返回nullif (myBinder == jBinder) {mProviderMap.removeAt(i);}}}try {if (DEBUG_PROVIDER) {Slog.v(TAG, "removeProvider: Invoking ActivityManagerService."+ "removeContentProvider(" + prc.holder.info.name + ")");}ActivityManager.getService().removeContentProvider(prc.holder.connection, false);} catch (RemoteException e) {//do nothing content provider object is dead any way}}
```这样下次acquire时,acquireExistingProvider就返回nullif (myBinder == jBinder) {mProviderMap.removeAt(i);}}}try {if (DEBUG_PROVIDER) {Slog.v(TAG, "removeProvider: Invoking ActivityManagerService."+ "removeContentProvider(" + prc.holder.info.name + ")");}ActivityManager.getService().removeContentProvider(prc.holder.connection, false);} catch (RemoteException e) {//do nothing content provider object is dead any way}}

system server端

ContentProviderHelper#removeContentProvider

  /*** Drop a content provider from a ProcessRecord's bookkeeping*/void removeContentProvider(IBinder connection, boolean stable) {mService.enforceNotIsolatedCaller("removeContentProvider");final long ident = Binder.clearCallingIdentity();try {ContentProviderConnection conn;try {conn = (ContentProviderConnection) connection;} catch (ClassCastException e) {String msg = "removeContentProvider: " + connection+ " not a ContentProviderConnection";Slog.w(TAG, msg);throw new IllegalArgumentException(msg);}if (conn == null) {throw new NullPointerException("connection is null");}ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"removeContentProvider: ",(conn.provider != null && conn.provider.info != null如果在acquireExistingProvider发现对端进程binder died了,则也会执行handleUnstableProviderDiedLocked? conn.provider.info.authority : ""));try {synchronized (mService) {decProviderCountLocked(conn, null, null, stable, true, true);}} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}} finally {Binder.restoreCallingIdentity(ident);}}

ContentProviderHelper#decProviderCountLocked

  @GuardedBy("mService")private boolean decProviderCountLocked(ContentProviderConnection conn,ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable,boolean enforceDelay, boolean updateOomAdj) {if (conn == null) {cpr.removeExternalProcessHandleLocked(externalProcessToken);return false;}if (conn.totalRefCount() > 1) {conn.decrementCount(stable);return false;}if (enforceDelay) {// delay the removal of the provider for 5 seconds - this optimizes for those cases// where providers are released and then quickly re-acquired, causing lots of churn.BackgroundThread.getHandler().postDelayed(() -> {handleProviderRemoval(conn, stable, updateOomAdj);}, 5 * 1000);} else {handleProviderRemoval(conn, stable, updateOomAdj);}return true;}

ContentProviderHelper#handleProviderRemoval

 private void handleProviderRemoval(ContentProviderConnection conn, boolean stable,boolean updateOomAdj) {synchronized (mService) {// if the proc was already killed or this is not the last reference, simply exit.if (conn == null || conn.provider == null || conn.decrementCount(stable) != 0) {return;}final ContentProviderRecord cpr = conn.provider;conn.stopAssociation();cpr.connections.remove(conn);conn.client.mProviders.removeProviderConnection(conn);if (conn.client.mState.getSetProcState()< ActivityManager.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.mProviders.setLastProviderTime(SystemClock.uptimeMillis());}}mService.stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid,cpr.appInfo.longVersionCode, cpr.name, cpr.info.processName);if (updateOomAdj) {mService.updateOomAdjLocked(conn.provider.proc,OomAdjuster.OOM_ADJ_REASON_REMOVE_PROVIDER);}}}

调整引用计数

ContentProviderHelper#refContentProvider

 boolean refContentProvider(IBinder connection, int stable, int unstable) {ContentProviderConnection conn;try {conn = (ContentProviderConnection) connection;} catch (ClassCastException e) {String msg = "refContentProvider: " + connection + " not a ContentProviderConnection";Slog.w(TAG, msg);throw new IllegalArgumentException(msg);}if (conn == null) {throw new NullPointerException("connection is null");}ActivityManagerService.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "refContentProvider: ",(conn.provider != null && conn.provider.info != null? conn.provider.info.authority : ""));try {conn.adjustCounts(stable, unstable);return !conn.dead;} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}

ContentProviderHelper#adjustCounts

 /*** Adjusts the reference counts up or down (the inputs may be positive,* zero, or negative.  This method does not return a total count because* a return is not needed for the current use case.*/public void adjustCounts(int stableIncrement, int unstableIncrement) {synchronized (mLock) {if (stableIncrement > 0) {mNumStableIncs += stableIncrement;}final int stable = mStableCount + stableIncrement;if (stable < 0) {throw new IllegalStateException("stableCount < 0: " + stable);}if (unstableIncrement > 0) {mNumUnstableIncs += unstableIncrement;}final int unstable = mUnstableCount + unstableIncrement;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);}mStableCount = stable;mUnstableCount = unstable;}}

进程死亡移除

在目标CP进程死后,AMS会去调用removeDyingProviderLocked,其中会根据ContentProviderConnection的stableCount和unstableCount处理引用该CP的进程。如果stableCount不为0,则杀死该客户进程。如果stableCount为0,而unstableCount不为0,则通过ApplicationThread#unstableProviderDied通知客户进程,并从ContentProviderRecord中移除该ContentProviderConnection。

如果在acquireExistingProvider发现对端进程binder died了,则也会执行handleUnstableProviderDiedLocked

ActivityThread#handleUnstableProviderDiedLocked

    final void handleUnstableProviderDiedLocked(IBinder provider, boolean fromClient) {ProviderRefCount prc = mProviderRefCountMap.get(provider);if (prc != null) {if (DEBUG_PROVIDER) Slog.v(TAG, "Cleaning up dead provider "+ provider + " " + prc.holder.info.name);// 引用计数移除mProviderRefCountMap.remove(provider);for (int i=mProviderMap.size()-1; i>=0; i--) {ProviderClientRecord pr = mProviderMap.valueAt(i);if (pr != null && pr.mProvider.asBinder() == provider) {Slog.i(TAG, "Removing dead content provider:" + pr.mProvider.toString());// 从map中移除mProviderMap.removeAt(i);}}if (fromClient) {// We found out about this due to execution in our client// code.  Tell the activity manager about it now, to ensure// that the next time we go to do anything with the provider// it knows it is dead (so we don't race with its death// notification).try {ActivityManager.getService().unstableProviderDied(prc.holder.connection);} catch (RemoteException e) {//do nothing content provider object is dead any way}}}}

ContentProvider详解相关推荐

  1. Android面试基础之ContentProvider详解(斗帝养成系列三)

    斗帝养成 斗师,一至九星,斗气纱衣,聚气化液态. 我匆忙了一生,我却留不下任何东西. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Service详解(斗帝养 ...

  2. Android之自定义ContentProvider详解

    第一个版本  对android中MIME类型的理解 初始MIME类型,是在学习ContentProvider的时候. 当在创建自己的ContentProvider的时,需要从抽象类ContentPro ...

  3. ContentProvider详解及使用大全

    Android中的Content provider机制可支持在多个应用中存储和读取数据.这也是跨应用共享数据的唯一方式.在android系统中,没有一个公共的内存区域,供多个应用共享存储数据. And ...

  4. Android——ContentProvider详解

    1. 简介 ContentProvider,内容提供者属于Android的四大组件之一 用于进程间 进行数据交互 & 共享,即跨进程通信 原理:使用binder机制(后续再进行介绍 统一资源标 ...

  5. Android四大组件之ContentProvider详解

    1. 为什么需要内容提供者contentProvider? 为不同的应用之间数据共享提供统一的访问接口,内容提供者的作用 把私有的数据给暴露出来 2. 内容提供者原理? 原理:可以把ContentPr ...

  6. Android面试基础之BroadcastReceiver详解(斗帝养成系列四)

    斗帝养成 大斗师,一至九星,斗气铠甲,斗气外放,聚气化固态,呈菱形.别说自己尽力了,那只是自欺欺人的假话. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Se ...

  7. ios首次加载web_IOS_IOS中UIWebView的使用详解,一、初始化与三种加载方式 UI - phpStudy...

    IOS中UIWebView的使用详解 一.初始化与三种加载方式 UIWebView继承与UIView,因此,其初始化方法和一般的view一样,通过alloc和init进行初始化,其加载数据的方式有三种 ...

  8. Android开发--详解ContentProvider/Cursor的使用

    ContentProvider是Android四大组件之一,所以如果是自己实现ContentProvider,需要在AndroidManifest.xml文件中进行声明,幸运的是,我们很少需要自己定义 ...

  9. Android笔记——四大组件详解与总结

    android四大组件分别为activity.service.content provider.broadcast receiver. -------------------------------- ...

最新文章

  1. SAP MM IV中的Duplicated Invoice Check功能的测试
  2. 002.ICMP--拼接ICMP包,实现简单Ping程序(原始套接字)
  3. PHP提高编程效率的方法
  4. mark python新手必碰到的问题---encode与decode,中文乱码[转]
  5. Server.MapPath()
  6. 浏览器怪异模式和标准模式之间的区别 DTD
  7. 云计算开源软件有哪些?
  8. css3常用伪类选择器
  9. Error running Tomcat8: Address localhost:1099 is already in use
  10. 简单的Python小游戏制作
  11. Wonderware Historian 2017安装,资料
  12. 计算机网络(谢希仁版)知识点汇总
  13. Field of view xxxx underlying table doesn't have a default value 的一种解决方法
  14. 微信Android热补丁方案Tinker
  15. oracle版本区别 win7_Oracle在Win7服务管理中消失的解决方法
  16. 这家80岁的游戏厂商,给了入行新人一份开发培训教材
  17. 百度竞价推广账户常见问题及调整方法
  18. Java项目:SSM实现的一个在线文具学习用品购买商城网站
  19. Android默认音乐,控制Android或任何其他音乐播放器的默认音乐播放器
  20. 河北北方学院专接本计算机,接本院校介绍——河北北方学院

热门文章

  1. matlab中的bsxfun
  2. txt、csv、trc、log格式转换成asc
  3. aspose将word转换为pdf[aspose.word.java 18.11]
  4. 【obs】发送前丢帧算法及帧优先级设置
  5. ASP.NET WEBAPI 跨域请求 405错误
  6. mysql 小版本直接升级
  7. 计算机专业方面期刊介绍--
  8. MYSQL中如何创建存储过程和存储函数(上篇)
  9. typename和class
  10. 使用customRef自定义ref,解决setup中处理异步问题。