从源码角度看ContentProvider
简介
本文原文在我的博客CheapTalks,欢迎大家去看看~
ContentProvider相信大家都耳熟能详了,在安卓系统越来越注重系统安全的前提下,不知道大家好不好奇provider是如何在Android层层安全措施之下使进程之间实现数据增删改查操作的。仔细想来,我们大概能猜到,安卓进程间通信惯用的"伎俩"是binder call,ContentProvider的实现很有可能就使用AMS当作provider的客户端与服务端的中间人。本篇文章,我将详细分析客户端app是如何请求到在其他应用进程运行的ContentProvider的,文章最后会附带着这期间AMS和APP两端涉及到的数据结构。
由于篇幅有限,ContentProvider涉及到的其它知识我没有细讲。例如,数据的query很有可能涉及到大量的数据,而安卓的binder同步缓冲区也才1016K这么大,所以provider的查找操作实际上是混合了ashmem与binder这两种跨进程通信技术,其中binder的主要作用是传送ashmem的fd;又如,应用开发常常使用到的ContentObserver,这块的分析可以在我以前写的ContentService的分析找到。
从CRUD之query说起
一般我们写APP时,是通过ContentResolver来进行CRUD调用的。而这个操作一般是通过context.getContentResolver().xxx这样的调用链过来的。Context这块是个装饰者模式,真实的执行者是ContextImp, 这块我不多言了,我们从ContextImpl这块的源码开始分析。
ContextImpl.java
private final ApplicationContentResolver mContentResolver;
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration, int createDisplayWithId) {
...
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
可以看到,最终ApplicationContentResolver负责了我们的APP ContentProvider CRUD这块的操作。
ContentResolver.java
public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
...
// 获取一个unstableProvider,这个stable与unstable的概念在于AMS中会维护unstable, stable这两个计数
// 如果当stable的计数大于0,当ContentProvider服务端进程死亡时,那么使用这个服务的客户端进程也会受牵连被杀死
// 这里query先尝试使用unstableProvider,表示AMS那边只会增加unstable的计数,客户端不会收到联级诛杀的牵连
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
...
try {
// binder call到服务端,返回了一个cursor对象
// 当调用cursor.close时,会调用调releaseProvider来释放ContentProvider服务端与客户端之间的引用
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
// 第一次使用unstable尝试,服务端进程可能死亡了抛了异常
// 先释放unstableProvider相关的引用
unstableProviderDied(unstableProvider);
// 第二次进行尝试时,将使用stableProvider
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
// 注意,失败一次后将使用stableProvider,这次如果服务端进程被杀
// 并且cursor还没有调用close之前,那么客户端的进程会受到牵连也被杀死
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
...
// 装饰一下
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
stableProvider != null ? stableProvider : acquireProvider(uri));
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
// 如果不为空才close
// 上面的操作如果成功了,qCursor是不会是空的,所以这个关闭操作交给了APP端来做
if (qCursor != null) {
qCursor.close();
}
...
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
有一点需要提一下,当binder call时抛出DeadObjectException时,不一定是对端进程死亡了,有可能是对端binder缓冲区被占满之类的异常;这块之前问过Google的工程师,他们貌似有细化这个DeadObjectException的计划,不过现在来看DeadObjectException的抛出并不代表者对端进程就死亡了。
代码注释很详细,大体流程可以看看图。

核心操作acquireProvider
客户端操作
在ContentResolver中调用acquireStableProvider和acquireUnstableProvider之后都会调用到ApplicationContextResolver这个子类中,之后再调用ActivityThread的acquireProvider的方法,区别stable与unstable的Provider在于这个stable字段,若是true则是获取stableProvider,反之是获取unstableProvider。咱们看代码:
ApplicationContentResolver.java
private static final class ApplicationContentResolver extends ContentResolver {
private final ActivityThread mMainThread;
private final UserHandle mUser;
// stableProvider
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
// unstableProvider
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
...
ApplicationContentResolver其实是ActivityThread的内部类,这里为了方便看,这两块的代码还是分开分析吧
ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 现在本地寻找provider,如果没有的话才像AMS去请求
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
...
IActivityManager.ContentProviderHolder holder = null;
try {
// 向AMS请求ContentProvider,这块是核心方法
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// "安装"provider,说白了就是新建实例,增减引用这类操作
// 这块的代码放到后面的scheduleInstallProvider再分析
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
AMS.getContentProviderImpl (1)
ActivityThread binder call 到AMS之后,紧接着就会调用getContentProviderImpl,这个方法比较大,分拆进行分析
ActivityManagerService.java
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized (this) {
long startTime = SystemClock.elapsedRealtime();
// 获取调用者的ProcessRecord对象
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
...
}
boolean checkCrossUser = true;
...
// 通过uri authority name来获取ContentProviderRecord
cpr = mProviderMap.getProviderByName(name, userId);
...
if (cpr == null && userId != UserHandle.USER_OWNER) {
// 检查userId=0是否已存有ContentProviderRecord
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_OWNER;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
这一段AMS会在providerMap中寻找正在运行的provider。如果找到,那么说明这个provider已经被启动了,随后增加引用即可;如果没有找到,那么就需要调用installProvider在provider客户端中进行provider的"安装",随后AMS将等待这个客户端publishProvider。
AMS.getContentProviderImpl (2)
// 根据先前的查询,判断当前的provider是否正在运行
boolean providerRunning = cpr != null;
if (providerRunning) {
cpi = cpr.info;
String msg;
...
// 如果provider能够在客户端进行直接运行,那么在这里就返回provider给客户端
if (r != null && cpr.canRunHere(r)) {
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null;
return holder;
}
final long origId = Binder.clearCallingIdentity();
...
// 增加引用
conn = incProviderCountLocked(r, cpr, token, stable);
// 如何stable和unstable的总引用计数为1,那么更新LruProcess列表
if (conn != null && (conn.stableCount + conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
...
updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
}
}
if (cpr.proc != null) {
...
// 更新provider进程的adj
boolean success = updateOomAdjLocked(cpr.proc);
maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
...
if (!success) {
...
// 如果不成功,那么减少引用计数并杀死provider进程
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
checkTime(startTime, "getContentProviderImpl: before appDied");
appDiedLocked(cpr.proc);
checkTime(startTime, "getContentProviderImpl: after appDied");
if (!lastRef) {
// This wasn't the last ref our process had on
// the provider... we have now been killed, bail.
return null;
}
providerRunning = false;
conn = null;
}
}
Binder.restoreCallingIdentity(origId);
}
AMS.getContentProviderImpl (3)
boolean singleton;
// 如果provider没有正在运行
if (!providerRunning) {
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
// 获取ProviderInfo
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
// 如果为空,则说明没有找到这个provider,直接返回空给客户端
if (cpi == null) {
return null;
}
// Provider是否为单例
singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_OWNER;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
...
// 通过ComponentName获取providerMap中的cpr
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
...
// 该provider是否是第一次被创建
final boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
...
ai = getAppInfoForUser(ai, userId);
// 创建一个cpr
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
...
if (r != null && cpr.canRunHere(r)) {
return cpr.newHolder(null);
}
...
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// provider还没有被运行
if (i >= N) {
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);
}
// 获取到运行provider的进程
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
// 如果这个进程已经启动,那么binder call给这个进程,创建provider
if (proc != null && proc.thread != null) {
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
// 如果进程没有启动,那么就启动这个进程
// 需要说的一点是,进程启动完毕后,创建provider的操作将会在ActivityThread初始化时进行
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
// 进程没有创建成功,直接返回空给客户端
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);
}
}
...
// 如果第一次创建这个provider的实例,在providerMap中进行缓存
if (firstClass) {
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
// 增加引用
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
}
AMS.getContentProviderImpl (4)
此时getContentProviderImpl的分析已经接近尾声。我们看到,如果provider此时尚未在其进程中被创建,那么AMS将会对这个provider进行实例化,也就是"publish"发布。AMS会在这里等待APP进程的完成,随后才会返回。
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
...
return null;
}
try {
...
if (conn != null) {
conn.waiting = true;
}
// 等待provider"发布"完成
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;

scheduleInstallProvider
应用provider的实例化有两个入口,一个是当进程已经存在时,AMS binder call到APP实例化某个provider;一个是当进程与Application互相绑定时,批量对provider进行安装。
ActivityThread.java
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
...
// 对provider进行实例化
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 完成实例化,通知AMS,进行provider"发布"
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
installProvider (1)
如果provider尚未实例化,则需要在这个宿主进程中进行"安装"
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
// 如果向AMS.getContentProviderImpl返回NULL或者需要app"安装"provider,就会对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) {
// Ignore
}
}
...
try {
// 利用反射进行provider实例化
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
...
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);
}
installProvider (2)
进行引用方面的操纵;在宿主进程进行provider的缓存
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
// 获取provider代理
IBinder jBinder = provider.asBinder();
// 如果这个provider在上一个操作刚被创建
if (localProvider != null) {å
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
// 本地缓存
if (pr != null) {
...
provider = pr.mProvider;
} else {
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
// 根据Uri authority name进行分类缓存
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 {
ActivityManagerNative.getDefault().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;
AMS.publishContentProviders
在provider宿主进程进行实例化成功之后,就需要通知AMS,告诉它不需要再等待了。此后,访问provider的应用进程的getContnetProviderImpl才真正的结束
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
...
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
// 获取provider宿主进程
final ProcessRecord r = getRecordForAppLocked(caller);
...
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
// 根据类进行缓存
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
// 根据uri authority name进行缓存
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
// 这里移除provider ANR的"定时炸弹"
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
// 通知AMS provider已经"发布"成功
dst.notifyAll();
}
updateOomAdjLocked(r);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}

ContentProvider隐藏陷阱之联级诛杀
因为ContentProvider的设计,当provider的宿主进程死亡时,访问它的进程如果正在做CRUD,那么这个进程也会受到牵连。
进程被杀后
以下从ProcessRecord.kill为入口点进行分析
ProcessRecord.java
void kill(String reason, boolean noisy) {
if (!killedByAm) {
...
// 杀死进程
Process.killProcessQuiet(pid);
Process.killProcessGroup(uid, pid);
if (!persistent) {
killed = true;
killedByAm = true;
}
...
}
}
当进程死亡后,将会调用当初在attachApplication时注册的死亡回调
ActivityManagerService.java
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
final int mPid;
final IApplicationThread mAppThread;
AppDeathRecipient(ProcessRecord app, int pid,
IApplicationThread thread) {
if (DEBUG_ALL) Slog.v(
TAG, "New death recipient " + this
+ " for thread " + thread.asBinder());
mApp = app;
mPid = pid;
mAppThread = thread;
}
@Override
public void binderDied() {
// 进程死亡后,将会调用到里面
synchronized (ActivityManagerService.this) {
appDiedLocked(mApp, mPid, mAppThread, true);
}
}
}
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
...
handleAppDiedLocked(app, false, true);
...
}
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);
...
}
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index) {
...
// 删除和该进程有关的已"发布"的provider
for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(i);
final boolean always = app.bad || !allowRestart;
boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
if ((inLaunching || always) && cpr.hasConnectionOrHandle()) {
...
restart = true;
}
cpr.provider = null;
cpr.proc = null;
}
app.pubProviders.clear();
进行provider级联诛杀
private final boolean removeDyingProviderLocked(ProcessRecord proc,
ContentProviderRecord cpr, boolean always) {
final boolean inLaunching = mLaunchingProviders.contains(cpr);
// 如果这个provider尚正在运行
if (!inLaunching || always) {
synchronized (cpr) {
cpr.launchingApp = null;
cpr.notifyAll();
}
mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
}
}
// 遍历这个进程所有的Connection
for (int i = cpr.connections.size() - 1; i >= 0; i--) {
ContentProviderConnection conn = cpr.connections.get(i);
if (conn.waiting) {
// If this connection is waiting for the provider, then we don't
// need to mess with its process unless we are always removing
// or for some reason the provider is not currently launching.
if (inLaunching && !always) {
continue;
}
}
// 获取到这个provider的客户端
ProcessRecord capp = conn.client;
conn.dead = true;
// 这个connection的stable计数大于0才会杀死其客户端
if (conn.stableCount > 0) {
// 如果这个app不是常驻进程且正在运行中,那么将会进行联级诛杀
if (!capp.persistent && capp.thread != null
&& capp.pid != 0
&& capp.pid != MY_PID) {
capp.kill("depends on provider "
+ cpr.name.flattenToShortString()
+ " in dying proc " + (proc != null ? proc.processName : "??"), true);
}
} else if (capp.thread != null && conn.provider.provider != null) {
try {
capp.thread.unstableProviderDied(conn.provider.provider.asBinder());
} catch (RemoteException e) {
}
// In the protocol here, we don't expect the client to correctly
// clean up this connection, we'll just remove it.
cpr.connections.remove(i);
if (conn.client.conProviders.remove(conn)) {
stopAssociationLocked(capp.uid, capp.processName, cpr.uid, cpr.name);
}
}
}
if (inLaunching && always) {
mLaunchingProviders.remove(cpr);
}
return inLaunching;
}
provider中,query操作首次是使用unstableProvider,失败一次后会使用stableProvider;其余insert, update, delete操作直接使用的是stableProvider
联级存在的意义在于保护provider客户端与服务端的数据一致性;因为插入,删除这些操作会涉及到数据更新,所以如果provider出现了异常,为了保证客户端维护的数据是正确了,只能强迫客户端进程直接死亡再重新启动恢复数据

releaseProvider
前面我们已经分析,acquireProvider会增加stable的引用计数,而provider服务端死亡时,如果stable计数大于0,那么provider客户端也会收到波及被杀死。那什么时候会stable的计数会减少呢,答案在releaseProvider这个方法中
触发时机
ContentResolver.java
public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider unstableProvider = acquireUnstableProvider(uri);
...
try {
...
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
...
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
...
// 返回cursor,待客户端调用close后才会调用到releaseProvider
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
...
}
}
public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
Preconditions.checkNotNull(url, "url");
IContentProvider provider = acquireProvider(url);
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
} catch (RemoteException e) {
return null;
} finally {
// 直接在最后调用releaseProvider
releaseProvider(provider);
}
}
public final int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String where, @Nullable String[] selectionArgs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
try {
long startTime = SystemClock.uptimeMillis();
int rowsUpdated = provider.update(mPackageName, uri, values, where, selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, uri, "update", where);
return rowsUpdated;
} catch (RemoteException e) {
return -1;
} finally {
releaseProvider(provider);
}
}
public final int delete(@NonNull Uri url, @Nullable String where,
@Nullable String[] selectionArgs) {
Preconditions.checkNotNull(url, "url");
IContentProvider provider = acquireProvider(url);
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
int rowsDeleted = provider.delete(mPackageName, url, where, selectionArgs);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "delete", where);
return rowsDeleted;
} catch (RemoteException e) {
return -1;
} finally {
releaseProvider(provider);
}
}
public final @Nullable Bundle call(@NonNull Uri uri, @NonNull String method,
@Nullable String arg, @Nullable Bundle extras) {
Preconditions.checkNotNull(uri, "uri");
Preconditions.checkNotNull(method, "method");
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
try {
return provider.call(mPackageName, method, arg, extras);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
releaseProvider(provider);
}
}
以上,除了query外,其它操作都会在最后调用releaseProvider释放provider的计数;query比较特殊,会在调用cursor的close后才调用;
这里有个特殊的方法:call,它的不同之处在于数据传输的方式。其它的query, insert, update, delete这些操作是使用binder+ashmem结合的方式进行数据传输,而call纯粹使用的是binder进行

App端数据结构

- ProviderKey: 包含URL authority字符串
- ProviderClientRecord: 与AMS端的ContentProviderRecord对应,主要引用了provider proxy句柄
- ProviderRefCount: 封装了stable, unstable两种引用
- ContentProviderHolder: 主要引用了ContentProviderConnection proxy句柄,provider proxy句柄
- ProviderInfo: provider的抽象存储类
- ComponentName: 组件类的抽象类
system_server端数据结构

- ProviderMap: AMS端用来缓存ContentProviderRecord的对象,提供四种不同的查询方式
- ContentProviderRecord: provider在AMS中的封装类,主要引用了provider binder proxy、进程信息
- Association: 两个进程相互关联的抽象
- ProcessRecord: 进程的抽象,包含了进程优先级、四大组件等等信息
- ContentProviderConnection: 主要引用了provider客户端的进程抽象
总结
本篇博客详细分析了provider代理对象是如果被获取的,其中涉及了provider客户端,system_server的AMS,provider服务端三个进程的通信。同时也分析了contentprovider的联级诛杀原理,这块的知识点客户端开发需要格外注意,否则自己的app无故死亡了都不知道。
关于provider客户端如何同服务端使用ashmem与binder结合的方式来传输数据,因为本篇实在已经太长了,我没有细讲,以后再作分析。
关于ContentProvider的监听,这块感兴趣的可以看我的博客从源码层解析ContentService如何实现数据变化监听
转载自:https://juejin.cn/post/6844903511621107719