本文基于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。
- stable : call/insert/delete/update等方式建立的都是stable连接;对端挂掉,client会被级联查杀。
- 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
- 本地存在对应的IConetntProvider实例则直接获取
- 不存在则向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
代码过于庞大,去除一些浅显易懂的判断代码
查询过程中有如下三种场景:
- B进程存在且对应的provider已经发布,直接创建连接
- B进程存在,provider尚未发布,通知B进程去发布provider
- B进程不存在,去创建B进程
返回null的情况
- 对端进程死亡,且不是最后一次引用
- provider不在运行,且pms解析provider信息失败
- provider所在user不在运行中
- 是否需要权限review 页面,一般为false
- pms解析provider所在app的application信息失败
- provider所在进程不存在&短时间crash多次拉起进程失败
- 经历过上面场景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,

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

535

被折叠的 条评论
为什么被折叠?



