ActivityManagerService: ContentProvider架构 介绍了 provider 相关的类的关系,以及 provider 通讯的过程。本文以此为基础,来完整分析 provider 的访问过程。
任何一次源码分析,都要基于一个例子,否则分析过程中会迷失方向,因此我准备了一个分析案例,如下
- 一个宿主app,它有一个 ContentProvider,并且宿主app进程没有启动。
- 一个客户端app,在 MainActivity 中,访问 宿主app 的 ContentProvider,访问的方式我选择查询。
本文基于 Android 14 代码分析。
客户端访问 provider
查询 provider 的实现如下
// ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Objects.requireNonNull(uri, "uri");
try {
if (mWrapped != null) {
return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
}
} catch (RemoteException e) {
return null;
}
// 1. 获取 provider 接口
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
// 2. 通过 provider 接口进行查询
qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// ...
}
if (qCursor == null) {
return null;
}
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
// 3. 返回数据
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// ...
} finally {
// ...
}
}
查询 provider 的过程很简单,首先就是获取 provider 接口,然后通过接口查询数据,最后返回数据。
通过接口去查询 provider 数据,其实就是一次 Binder 通讯,这个过程就不需要分析了。因此,接下来只用分析获取 provider 接口的过程。
可能读者注意到了代码中 unstable provider 的概念,其实还有一个 stable provider 概念,我们在后面的文章中进行答疑解惑。
获取 provider 接口
// ContentResolver.java
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
// 由子类实现
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
如果从 Activity 中查询 provider,那么 ContentResolver 的实现类是 ApplicationContentResolver,看下它是如何实现获取 provider 的函数
// ContextImpl.java
private static final class ApplicationContentResolver extends ContentResolver {
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
// 由 ActivityThread 完成获取 unstable provider
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
}
// ActivityThread.java
// 参数 stable 此时为 false
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 首先从本地查询是否存在 provider 接口
// 因为 provider 数据最终都会保存到 mProviderMap 中
// 对于分析的案例,很显然是获取不到的
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
// 使用参数生成 KEY 值,并从 ArrayMap<ProviderKey, ProviderKey> mGetProviderKeys 获取 VALUE
// TODO: 这个数据结构设计的真是离谱,KEY 值明明可以通过 auth + userId 拼接一个 String 即可,偏偏要搞一个跟 VALUE 一模一样的类型
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
// 1. 从服务端获取 provider 数据
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// If the returned holder is non-null but its provider is null and it's not
// local, we'll need to wait for the publishing of the provider.
// holder != null 表示确实存在 provider
// holder.provider == null 表示 provider 还没有发布
// holder.mLocal 为 false,表示 ContentProvider 不在客户端 app 中
// 这三个条件在一起,表示 app 端需要等待 provider 发布
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
if (key.mHolder != null) {
if (DEBUG_PROVIDER) {
Slog.i(TAG, "already received provider: " + auth);
}
} else {
// 2.如果 provider 正在发布,那么必须阻塞当前线程以等待发布结果
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
}
// 3.线程被唤醒,首先要保存新的 provider 数据
// 因为服务端告知客户端 provider 成功发布时,会传递 provider 数据,而这个数据被保存到 key.mHolder
holder = key.mHolder;
}
// 检测是否是 provider 发布超时
// 客户端收到 provider 的发布状态,有两种情况,一种是发布成功,另外一种发布超时
// 如果 holder.provider 为 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;
}
}
// 获取不到 provider,有两种可能
// 一种是,要访问的 provider 不存在,服务端返回 null
// 另外一种是,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;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
// 4.如果获取到 provider,那么安装它
// 主要是用数据结构保存 provider 数据
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
// 5.返回 provider 接口
return holder.provider;
}
针对本文分析的案例,客户端获取 provider 接口的过程如下
- 从服务端获取 provider 数据,这些数据包装到 ContentProviderHolder 中。
- 由于 provider 宿主进程还没有启动,更没有发布 provider,因此获取 provider 接口的线程只能阻塞等待。
- 当客户端收到 provider 发布成功的消息时,会把新的 provider 数据更新到 key.mHolder 中,并且唤醒由于等待 provider 而阻塞的线程。因此当阻塞的线程被唤醒时,立即更新 provider 数据。
- 安装 provider,主要就是用相关的数据结构保存 provider 数据。
- 返回 provider 接口,也就是 ContentProviderHolder#provider。
目前,获取 provider 接口的线程阻塞了,而唤醒的时机要等到 provider 发布之后,因此目前只能暂时分析服务端到底提供给客户端哪些 provider 数据。
服务端返回provider数据
接下来,分析服务端返回 provider 数据的过程
// ActvityManagerService.java
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProvider: ", name);
try {
// 参数 stable 此时为 false
return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
在服务端,provider 的事务都是交给 ContentProviderHelper 处理的
// ContentProviderHelper.java
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
// ...
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
ContentProviderRecord cpr = null;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
boolean providerRunning = false;
final int expectedUserId = userId;
synchronized (mService) {
long startTime = SystemClock.uptimeMillis();
ProcessRecord r = null;
if (caller != null) {
// 获取客户端进程实例
r = mService.getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid() + ") when getting content provider "
+ name);
}
}
// ...
// 处理 provider 宿主进程没有运行的情况
if (!providerRunning) {
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
// 从 PKMS 查询 provider 信息
// 参数 name 是 provider 的 authority
cpi = AppGlobals.getPackageManager().resolveContentProvider(name,
ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.GET_URI_PERMISSION_PATTERNS,
userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
// 如果没有查询到 provider 信息,那么代表不存在这样的 ContentProvider,因此返回 null
if (cpi == null) {
return null;
}
// If the provider is a singleton AND
// (it's a call within the same user || the provider is a privileged app)
// Then allow connecting to the singleton provider
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");
// ... 省略一段权限检测代码...
// 以 ComponentName 为 KEY,从 mProviderMap 获取 provider
// 由于 provider 还没有发布,因此 cpr 还是为 null
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
// 此时 cpr 为 null,因此 firstClass 为 true
boolean firstClass = cpr == null || (dyingProc == cpr.proc && dyingProc != 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");
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);
// 1. 构建 ContentProviderRecord
// ContentProviderRecord 是 provider 在服务端的代表
cpr = new ContentProviderRecord(mService, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
// r 是客户端进程
// 这里检测 provider 是否可以运行在客户端进程
// 本文不考虑这种情况
if (r != null && cpr.canRunHere(r)) {
// ...
}
if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {
Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null)
+ " pruid " + cpr.appInfo.uid + "): " + cpr.info.name
+ " callers=" + Debug.getCallers(6));
}
// mLaunchingProviders 表示正在启动的 provider
// 这里就是为了检测 provider 是否正在启动
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 {
// ...
// 获取 provider 宿主进程实例
// 此时宿主进程还没有启动,因此 proc 为 null
ProcessRecord proc = mService.getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid);
IApplicationThread thread;
if (proc != null && (thread = proc.getThread()) != null
&& !proc.isKilled()) {
// ...
} else {
// ...
// 2.provider 宿主进程不存在,那么就创建
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
new HostingRecord(HostingRecord.HOSTING_TYPE_CONTENT_PROVIDER,
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
// ...
}
// ContentProviderRecord#launchingApp 保存正在启动的宿主 app
cpr.launchingApp = proc;
// mLaunchingProviders 保存正在启动的 provider
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
// 3. 保存 provider 数据
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
// name 为 authority
mProviderMap.putProviderByName(name, cpr);
// 4. 创建 provider 链接,并初始化计数
conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag,
stable, false, startTime, mService.mProcessList, expectedUserId);
if (conn != null) {
// 4.1表示客户端正在等待 provider
conn.waiting = true;
}
} // if (!providerRunning)
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;
}
// 5. 发布超时消息,以免客户端无限期阻塞等待 provider
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);
}
}
enforceContentProviderRestrictionsForSdkSandbox(cpi);
// 6. 返回 provider 数据给客户端
return cpr.newHolder(conn, false);
}
}
// ...
}
本文分析的案例的情况是,provider 宿主进程还没有运行,更没有发布 provider,那么服务端返回 provider 数据的过程如下
- 在服务端创建一个 provider 记录 ContentProviderRecord。ContentProviderRecord#provider 就是 provider 接口,最终会通过 ContentProviderHolder 传递给客户端,但是目前这个接口还是 null,因为宿主进程还没有发布 provider。
- provider 宿主进程还没有运行,因此要创建进程。注意,fork 进程,以及运行宿主 app 是一个异步的过程。
- 使用 ContentProviderHelper#mProviderMap 根据 ComponentName 和 authority 分别保存 ContentProviderRecord。
- 创建一个 provider 链接 ContentProviderConnection,代表这次客户端对 provider 的访问。
- 发布超时消息,以免客户端一直阻塞地等待等待 provider 发布完成。
- 构建返回给客户端的数据 ContentProviderHolder。
创建链接
// ContentProviderHelper.java
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;
}
// r 是客户端进程
// r.mProviders 是进程用来保存的 provider 信息的
final ProcessProviderRecord pr = r.mProviders;
for (int i = 0, size = pr.numberOfProviderConnections(); i < size; i++) {
ContentProviderConnection conn = pr.getProviderConnectionAt(i);
// 如果客户端进程中已经有这个provider链接,那么增加计数即可
if (conn.provider == cpr) {
conn.incrementCount(stable);
return conn;
}
}
// Create a new ContentProviderConnection. The reference count is known to be 1.
// 1.创建一个 provider 链接
ContentProviderConnection conn = new ContentProviderConnection(cpr, r, callingPackage,
expectedUserId);
conn.startAssociationIfNeeded();
// 初始化计数
conn.initializeCount(stable);
// 2.ContentProvierRecord 保存链接
cpr.connections.add(conn);
if (cpr.proc != null) {
cpr.proc.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_PROVIDER);
}
// 3. 客户端进程 ProcessRecord 保存链接
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) {
// If this is a perceptible app accessing the provider, make
// sure to count it as being accessed and thus back up on
// the LRU list. This is good because content providers are
// often expensive to start. The calls to checkTime() use
// the "getContentProviderImpl" tag here, because it's part
// of the checktime log in getContentProviderImpl().
checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
processList.updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
return conn;
}
简单来说,就是创建链接 ContentProviderConnection,然后进程 ProcessRecord#mProviders 和 ContentProvier#connections 分别保存链接。
既然称之为链接,那么到底是谁的链接呢?当然是客户端对要访问 ContentProvider 的链接,那么链接的一端是客户端进程,另外一端就是 ContentProvider。那么链接是如何做到保存两端的呢?看下类关系图
classDiagram
class ContentProviderRecord{
+IContentProvider provider
~ArrayList< ContentProviderConnection > connections
}
class ContentProviderConnection{
+ContentProviderRecord provider
+ProcessRecord client
}
ContentProviderRecord *-- ContentProviderConnection
ContentProviderConnection#client 保存了客户端进程,这就是链接的一端。ContentProviderConnection#provider 指向了服务端的 ContentProviderRecord,而 ContentProviderRecord#provider 是 provider 接口,它指向宿主进程的 ContentProvider,因此这就是链接的另外一端。
构造返回给客户端的 ContentProviderHolder
服务端要返回数据类型是 ContentProviderHolder,先来看下它的类图
classDiagram
class ContentProviderHolder{
+ProviderInfo info
+IContentProvider provider
+IBinder connection
+boolean noReleaseNeeded
+boolean mLocal
}
ContentProviderHolder 几个成员变量的意思如下
- ProviderInfo info 这是从 PKMS 查到的 ContentProvider 信息。
- IContentProvider provider 这是 provider 接口。需要宿主进程发布 provider 之后,服务端才能获得。
- IBinder connection 代表客户端访问 provider 的一次链接。
- boolean noReleaseNeeded 表示 provider 是否需要释放。
- boolean mLocal 代表客户端要访问的 ContentProvider 是否在本地。
现在来看下服务端构造的,返回给客户端的数据到底有哪些
// ContentProviderRecord.java
public ContentProviderHolder newHolder(ContentProviderConnection conn, boolean local) {
// info 是从 PKMS 查询到的 provider 信息
ContentProviderHolder holder = new ContentProviderHolder(info);
// provider 是 Binder 接口,由于 provider 还没有发布
// 因此 provider 此时为 null
holder.provider = provider;
// noReleaseNeeded 表示 provider 在没有链接时是否需要释放
// 通常只有 SYSTEM 用户下的 ContentProvider 是不需要释放的
holder.noReleaseNeeded = noReleaseNeeded;
// provider 链接,它是 Binder 子类
// 客户端和服务端,通过这个链接,来建立计数
holder.connection = conn;
// local 表示 ContentProvider 是否在客户端 app 中
// 目前 local 值为 false
holder.mLocal = local;
return holder;
}
现在就明白了服务端返回客户端的数据有哪些了,那么客户端处理这些数据的过程也就清晰起来了,读者可以回头再看看客户端是如何判断 provider 正在发布的代码。
创建宿主进程
客户端向服务端获取 provider 时,服务端发现 provider 宿主进程还没有启动,于是就去 fork 一个进程。当进程创建后,会执行 App 的入口函数,也就是 ActivityThread#main()
// ActivityThread.java
public static void main(String[] args) {
// ...
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// ...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private void attach(boolean system, long startSeq) {
// ...
// 参数 system 为 false
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
// ...
} else {
// ...
}
// ...
}
很简单,App 进程创建的时候,向服务端发起 attach application,其实就是向服务端注册一个代表 app 进程的 Binder ,它的类型为 IApplicationThread。
服务端处理 attach application
// ActivityManagerService.java
public final void attachApplication(IApplicationThread thread, long startSeq) {
if (thread == null) {
throw new SecurityException("Invalid application interface");
}
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
private void attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
long bindApplicationTimeMillis;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
// 1.获取进程实例 ProcessRecord
// fork 进程后,mPidsSelfLocked 就保存了进程实例,因此这里获取到的进程实例不为null
app = mPidsSelfLocked.get(pid);
}
if (app != null && (app.getStartUid() != callingUid || app.getStartSeq() != startSeq)) {
// ...
}
} else {
// ...
}
// ...
// system ready 时,设置 mProcessesReady 为 true
// 这里表示正常启动完成,或者允许 app 在系统启动过程创建进程
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
// 2. 获取 app 所有 ContentProvider 的信息,
// 并且 ContentProviderHelper#mProviderMap 以及 ProcessRecord#mProviders 保存了 provider 数据
List<ProviderInfo> providers = normalMode
? mCpHelper.generateApplicationProvidersLocked(app)
: null;
// 如果进程是否正在启动 provider,那么发送一个超时消息,用于检测 provider 发布超时
// ContentProvider 超时 ANR 就是来自于此
if (providers != null && mCpHelper.checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg,
ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
}
// ...
try {
// ...
// provider info 新建一个包装类,用于传递给 app 端
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
if (app.getIsolatedEntryPoint() != null) {
// ...
} else if (instr2 != null) {
// ....
} else {
// 3. 通知 app 进程 bind application
// app 端会创建 Application,同时也会创建 ContentProvider
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());
}
// ...
// Make app active after binding application or client may be running requests (e.g
// starting activities) before it is ready.
synchronized (mProcLock) {
// 4. 进程 ProcessRecord 与代表 app 的 IApplicationThread 相关联
// ProcessRecord#mThread 和 mWindowProcessController.mThread 此时才保存 IApplicationThread
app.makeActive(thread, mProcessStats);
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
}
// ...
// mConstants.mEnableWaitForFinishAttachApplication 默认为 false
if (!mConstants.mEnableWaitForFinishAttachApplication) {
// 5.完成 attach application,
// 这个过程会拉起那些等待启动的组件,
// 例如 Activity, Service, 但是不包括 ContentProvider
finishAttachApplicationInner(startSeq, callingUid, pid);
} else {
// ...
}
} catch (Exception e) {
// ....
}
}
服务端处理 attach application 的过程
- 在 fork 完进程后,进程实例是会保存到 mPidsSelfLocked,因此,attach application 时,是可以根据 pid 获取到进程实例的。
- 从 ContentProviderHelper 获取 app 所有的 provider 信息,并且使用 ContentProviderHelper#mProviderMap 和 ProcessRecord#mProviders 保存 provider 数据。这个过程比较简单,请读者自行分析。
- 向 app 端发起 bind application,注意,传递的参数中包含了 app 所有 provier 信息。而 app 端会创建 Application,同时也会创建并启动 ContentProvider。
- 建立进程与 app 的关联。ProcessRecord 代表继承,IApplicationThread 是 app 端注册的 Binder,它代表 app。ProcessRecord#mThread 指向 app。
- 完成 attach application。这个过程主要拉起待启动的组件,例如 Activity, Service,但是不包括 ContentProvider。
bind application
现在来看下 app 端的 bind application 过程,提醒下,此时分析的是宿主进程的 bind application 过程
// ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
public final void bindApplication(String processName, ApplicationInfo appInfo,
String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
SharedMemory serializedSystemFontMap,
long startRequestedElapsedTime, long startRequestedUptime) {
// ...
// AppBindData 保存服务端执行 bind application 传入的数据
AppBindData data = new AppBindData();
// ...
// AppBindData 保存 provider 数据
data.providers = providerList.getList();
// ...
// 发送消息,执行 bind application
sendMessage(H.BIND_APPLICATION, data);
}
}
宿主 app 收到 bind application 指令,首先把服务端传入的数据保存到 AppBindData 中,其中就包括 app 所有 provider 信息,然后在主线程 Handler 中执行 bind application
// ActivityThread.java
private void handleBindApplication(AppBindData data) {
// ...
Application app;
try {
// 1. 通过反射创建 Application
// 但是由于最后一个参数是 null,因此不会调用 Application#onCreate()
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
// ...
// 保存初始的 Application
mInitialApplication = app;
// ...
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
// 2. 宿主 app 安装 ContentProvider,并向服务端发布 ContentProvider 数据
installContentProviders(app, data.providers);
}
}
// ...
try {
// 3.调用 Application#onCreate()
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
// ...
}
} finally {
// ...
}
// ...
try {
// 4.通知服务端去执行 finish attach application
// 在服务端,默认是不需要等待 app 端完成 bind application 后再执行 finish attach application
// 而是通知 app 端去执行 bind application 后,立即执行 finish attach application
mgr.finishAttachApplication(mStartSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
现在看下宿主 app 的 bind application 过程
- 通过反射创建 Application。
- 安装和发布 provider。其实就是创建并启动 ContentProvider,然后把 provider 数据发布给服务端。
- 调用 Application 的 onCreate() 方法。
- 通知服务端去执行 finish attach application。其实,默认地,服务端在通知 app 端去执行 bind application 后,立马就执行了 finish attach application,因此这一步其实没用。
宿主进程安装与发布 provider
我们现在来看下,在 bind application 中,与 provider 相关的部分,那就是安装与发布 provider
// ActivityThread.java
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());
}
// 1. 安装 provider
// 其实就是创建并启动 ContentProvider,然后用数据结构保存 provider 数据
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 2. 发布已安装的 provider 数据
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
很直观,先安装所有 ContentProvider,然后返回一个 provider 数据集合,最后把这些 provider 数据集合发布给服务端。
来看下安装 provider 的过程
// ActivityThread.java
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 此时 holder 参数为 null
if (holder == null || holder.provider == null) {
// ...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
// 1.通过反射创建 ContentProvider 对象
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
// 获取 ContentProvider 用于通讯的 Binder
provider = localProvider.getIContentProvider();
// ...
// 2. ContentProviderd attach info,
// 其实就是 ContentProvider 对象保存 provider 信息,
// 并调用 ContentProvider#onCreate()
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
// ...
}
} else {
// ...
}
ContentProviderHolder retHolder;
synchronized (mProviderMap) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ " / " + info.name);
IBinder jBinder = provider.asBinder();
// localProvider 就是前面刚创建的 ContentProvider 对象
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
// ...
} else {
// 3.构建 ContentProviderHolder,用于保存要发布的 provider 数据
holder = new ContentProviderHolder(info);
// 保存 provider binder
holder.provider = provider;
// 不需要释放
holder.noReleaseNeeded = true;
// 4.使用 mProviderMap 和 mLocalProviders 保存 provider 数据
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
// 这个就是刚刚构建的 ContentProviderHolder
retHolder = pr.mHolder;
} else {
// ...
}
}
// 4.返回构建的 ContentProviderHolder
return retHolder;
}
宿主 app 进程,在 bind application 时,安装 provider 过程如下
- 通过反射创建 ContentProvider 对象。
- ContentProvider 的 attach info ,其实就是解析并保存 provider 的一些信息,然后调用 ContentProvider#onCreate() 方法。
- 构建用于发布 provider 的数据 ContentProviderHolder。注意,此时用 ContentProviderHolder#provider 保存了 provider Binder。这是发布给服务端最重要的一个数据,因为服务端要把这个 Binder 传给给客户端。
- 使用 ActivityThread#mProviderMap 和 ActivityThread#mLocalProviders 保存 provider 数据
- 返回构建的 ContentProviderHolder 作为发布 provider 的数据。
服务端处理 provider 发布
现在宿主进程已经发布了 provider 数据,来看下服务端是如何处理这些数据的
// ActivityManagerService.java
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
// ...
try {
// 发布 provider 的工作交给 ContentProviderHelper
mCpHelper.publishContentProviders(caller, providers);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
// ContentProviderHelper.java
void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
mService.enforceNotIsolatedOrSdkSandboxCaller("publishContentProviders");
synchronized (mService) {
// 这里获取的是宿主进程
final ProcessRecord r = mService.getRecordForAppLOSP(caller);
// ...
final long origId = Binder.clearCallingIdentity();
boolean providersPublished = false;
// 遍历宿主进程发布的provider数据
for (int i = 0, size = providers.size(); i < size; i++) {
// 1.获取宿主进程发布的 provider 数据
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
// 2.获取进程 ProcessRecord 中保存的 provider 数据
// 在 attach application 过程中会生成 provider 数据,这个期间数据会保存到 ProcessRecord#mProviders
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;
// 3.mProviderMap 保存新的 provider
// 因此 dst 马上会更新 provider 数据,所以 dst 就是代表最新的 provider
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 确实处于启动中
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--;
}
}
// 4.provider 之前处于启动中,那么移除超时消息,
if (wasInLaunchingProviders) {
// 这个是客户端等待provider的超时消息
mService.mHandler.removeMessages(
ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
// 这个是宿主发布provider超时消息
mService.mHandler.removeMessages(
ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
// 进程 ProcessRecord 保存 package
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mService.mProcessStats);
// 5.把 app 端提供的 provider 数据,复制给服务端
synchronized (dst) {
// provider 接口
dst.provider = src.provider;
// 此时 ContentProviderRecord 正式绑定 ProcessRecord
dst.setProcess(r);
dst.notifyAll();
// 6.通知客户端,provider 已经成功发布
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, 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);
}
}
直接看第5步,服务端处理发布的 provider 数据,其实就是客户端发布的 provider 数据,来更新服务端保存的 provider 数据,也就是 ContentProviderRecord。其中最重要的就是更新 ContentProviderRecord#provider ,它是 provider 接口。
服务端保存完 provider 接口后,就通知客户端 provider 的发布状态,此时的状态时发布成功
// ContentProviderRecord.java
void onProviderPublishStatusLocked(boolean status) {
final int numOfConns = connections.size();
// 遍历 provider 中保存的链接
for (int i = 0; i < numOfConns; i++) {
final ContentProviderConnection conn = connections.get(i);
// conn.waiting 表示客户端正在等待 provider
// conn.client 不为null,表示客户端进程存在
if (conn.waiting && conn.client != null) {
// 从链接中获取客户端进程
final ProcessRecord client = conn.client;
// 此时 status 为 true
if (!status) {
// ...
}
// 从进程中获取客户端app注册的Binder回调
final IApplicationThread thread = client.getThread();
if (thread != null) {
try {
// 通知客户端 provider 的发布状态
thread.notifyContentProviderPublishStatus(
newHolder(status ? conn : null, false),
info.authority, conn.mExpectedUserId, status);
} catch (RemoteException e) {
}
}
}
conn.waiting = false;
}
}
很简单,遍历 ContentProviderRecord 保存的 provider 链接,如果这个链接的客户端进程在等待 provider 发布,那么通知客户端 provider 发布成功,并附带了最新 provider 数据。
客户端处理 provider 发布状态
OK,现在服务端通知客户端 provider 发布成功,那么来看下客户端如何处理的
// ActivityThread
public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
@NonNull String authorities, int userId, boolean published) {
final String auths[] = authorities.split(";");
for (String auth: auths) {
// 根据参数生成 KEY,然后从 mGetProviderKeys 获取 VALUE 值
final ProviderKey key = getGetProviderKey(auth, userId);
synchronized (key.mLock) {
// 保存服务端传过来的 provider 数据
key.mHolder = holder;
// 唤醒等待的线程
key.mLock.notifyAll();
}
}
}
当客户端收到 provider 发布状态时,就是保存服务端传过来的 provider 数据,然后唤醒线程。
此时,你可能有点摸不着头脑,好像也没有做点实际的事情,那么请看客户端访问 provider 时阻塞等待 provider 的代码
// ActivityThread.java
// 参数 stable 此时为 false
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// ...
// 1.从 mGetProviderKeys 获取 VALUE,也就是 ProviderKey
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
// 2. 从服务端获取 provider
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
// 需要等待 provider 发布
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
if (key.mHolder != null) {
if (DEBUG_PROVIDER) {
Slog.i(TAG, "already received provider: " + auth);
}
} else {
// 3.如果 provider 正在发布,那么必须阻塞当前线程以等待发布结果
key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
}
// 4.线程被唤醒,首先要保存新的 provider 数据
// 因为客户端收到 provider 发布状态时,同时也会收到一个新的 provider 数据,数据被保存到 key.mHolder
holder = key.mHolder;
}
// 检测是否是 provider 发布超时
// 客户端收到 provider 的发布状态,有两种情况,一种是发布成功,另外一种发布超时
// 如果 holder.provider 为 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;
}
}
// ...
// 4.如果获取到 provider,那么安装它
// 主要是用数据结构保存 provider 数据
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
// 5.返回 provider 接口
return holder.provider;
}
前面分析过,客户端在获取 provider 接口时,由于 provider 还没有发布,因此阻塞线程等待,而使用的锁是 ProviderKey#mLock。
再对比前面处理 provider 发布状态的代码,就不用我多说了吧。 此时,由于等待 provider 发布而阻塞的线程被唤醒,也就是走到第4步,保存新的 provider 数据,最后第5步返回 provider 接口。至此,客户端终于获取到 provider 接口,后面通过这个接口与 ContentProvider 进行 Binder 通讯,就可以完成查询 provider 数据的目的。
provider Binder 如何转化为 provider 接口
有的读者可能会好奇,服务端与客户端的通信中,传递是 provider Binder。那么客户端是如何把 provider Binder 转化为 provider 接口的呢?
由于 ContentProvider 没有使用 AIDL 建立 Binder 架构,那么真相肯定就隐藏在 ContentProviderHolder 的反序列化过程中
public class ContentProviderHolder implements Parcelable {
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
// source.readStrongBinder() 获取的是 Binder 对象
// ContentProviderNative.asInterface() 将 Biner 转化为接口
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
mLocal = source.readInt() != 0;
}
}
// ContentProviderNative.java
static public IContentProvider asInterface(IBinder obj)
{
// ...
// 客户端获取的 provider 接口是 ContentProviderProxy
return new ContentProviderProxy(obj);
}
看到这里,各位铁子们懂了吗,客户端获取的 provider 接口其实是 ContentProviderProxy。