本文基于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进程中
- ContentResolver的接口类型
call方法:delete,insert,update都是首先使用acquireProvider,增加的是stable 引用计数。 query方法:先去acquireUnstableProvider,若发生DeadObjectException,就使用acquireProvider重新获取IContentProvider,并增加stable引用计数。
只有query和其他操作同时进行时,ContentProviderConnection的unstableCount和stableCount才会同时为1
注意:在ContentResolver的call方法中,发生anr时如果堆栈卡在acquireProvider则binder对端是system server;如果卡在provider.call 这里,binder对端则是B进程。
@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;
}
IContentProvider provider = acquireProvider(authority);
if (provider == null) {
throw new IllegalArgumentException("Unknown authority " + authority);
}
try {
final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
extras);
Bundle.setDefusable(res, true);
return res;
} catch (RemoteException e) {
return null;
} finally {
releaseProvider(provider);
}
}
- 本地是否已经存在IContentProvider实例
主要代码在ActivityThread.java中 本地存在对应的IContentProvider实例直接获取;否则向ams查询,ams会直接返回一个ContentProviderHolder的实例。 如果对端B进程不存在或进程存在provider没发布,则上面返回的holder的provider为null,这时A进程需要在acquireProvider中等待对端provider发布,超时时间为20s。
@UnsupportedAppUsage
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
holder = key.mHolder;
}
if (holder != null && holder.provider == null) {
holder = null;
}
}
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
} catch (InterruptedException e) {
holder = null;
} finally {
synchronized (key.mLock) {
key.mHolder = null;
}
}
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;
}
holder = installProvider(c, holder, holder.info,
true , holder.noReleaseNeeded, stable);
return holder.provider;
}
获取本地存在的IContentProvider对象
@UnsupportedAppUsage
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
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()) {
Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+ ": existing object's process dead");
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
incProviderRefLocked(prc,stable);
}
return provider;
}
}
Ams查询provider
运行在system server进程,android 12.0后ContentProvider相关的逻辑都从AMS.java中拆出到ContentProviderHelper.java中了。 查询过程中有如下三种场景:
- B进程存在且对应的provider已经发布,直接创建连接
- B进程存在,provider尚未发布,通知B进程去发布provider
- B进程不存在,去创建B进程
ContentProviderHelper.java中,代码过于庞大,去除一些浅显易懂的判断代码
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();
..........
boolean checkCrossUser = true;
checkTime(startTime, "getContentProviderImpl: getProviderByName");
cpr = mProviderMap.getProviderByName(name, userId);
..........
ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.isKilled();
..............
if (providerRunning) {
cpi = cpr.info;
if (r != null && cpr.canRunHere(r)) {
checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser,
cpr.name.flattenToShortString(), startTime);
ContentProviderHolder holder = cpr.newHolder(null, true);
holder.provider = null;
return holder;
}
..........
final long origId = Binder.clearCallingIdentity();
try {
checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
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);
if (success && verifiedAdj != cpr.proc.mState.getSetAdj()
&& !isProcessAliveLocked(cpr.proc)) {
success = false;
}
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
if (!success) {
Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable,
false, false);
if (!lastRef) {
return null;
}
providerRunning = false;
conn = null;
dyingProc = cpr.proc;
} else {
cpr.proc.mState.setVerifiedAdj(cpr.proc.mState.getSetAdj());
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
if (!providerRunning) {
try {
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) {
}
if (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);
........
if (!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;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
if (!requestTargetProviderPermissionsReviewIfNeededLocked(
cpi, r, userId, mService.mContext)) {
return null;
}
try {
checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
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;
}
ai = mService.getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(mService, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
} finally {
Binder.restoreCallingIdentity(ident);
}
} else if (dyingProc == cpr.proc && dyingProc != null) {
cpr = new ContentProviderRecord(cpr);
firstClass = true;
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
if (r != null && cpr.canRunHere(r)) {
return cpr.newHolder(null, true);
}
final int numLaunchingProviders = mLaunchingProviders.size();
int i;
for (i = 0; i < numLaunchingProviders; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
if (i >= numLaunchingProviders) {
final long origId = Binder.clearCallingIdentity();
try {
.....
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);
}
checkTime(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()) {
final 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 {
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");
if (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;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable, false, startTime, mService.mProcessList, expectedUserId);
if (conn != null) {
conn.waiting = true;
}
}
checkTime(startTime, "getContentProviderImpl: done!");
mService.grantImplicitAccess(userId, null, callingUid,
UserHandle.getAppId(cpi.applicationInfo.uid));
if (caller != null) {
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 cpr.newHolder(conn, false);
}
}
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 "
+ 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) {
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场景
- B进程bind application过程中install
- B进程收到ams的回调直接install
ActivityThread.java中
@UnsupportedAppUsage
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 , true , true );
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
ActivityThread.java中
@UnsupportedAppUsage
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) {
.....
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) {
}
}
........
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
packageInfo = getSystemContext().mPackageInfo;
}
localProvider = 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;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
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) {
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);
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;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, updating ref count");
}
if (!noReleaseNeeded) {
incProviderRefLocked(prc, stable);
try {
ActivityManager.getService().removeContentProvider(
holder.connection, stable);
} catch (RemoteException e) {
}
}
} 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;
}
Ams 发布B进程的provider
发布成功后通知A进程 ContentProviderHelper.java中
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
mService.enforceNotIsolatedCaller("publishContentProviders");
synchronized (mService) {
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;
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;
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);
}
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);
}
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
dst.notifyAll();
dst.onProviderPublishStatusLocked(true);
}
dst.mRestartCount = 0;
}
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);
}
}
ContentProviderRecord.java中
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 {
thread.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.java中
@Override
public 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();
}
}
}
Connection的引用计数
引用计数增加
A进程app端流程同上面查询流程,不做赘述。 system server端如下:
引用计数减少
|