本文目标:以MediaProvider为例,想搞清楚调用ContentResolver访问各个ContentProvider的调用过程。
Java code:
getContentResolver().query(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,null,null)
具体调用过程是
1.通过ContentResolver先查找对应给定Uri的ContentProvider,返回对应的BinderProxy
如果该Provider尚未被调用进程使用过:
a.通过ServiceManager查找activity service得到ActivityManagerService对应BinderProxy
b.调用BinderProxy的transcat方法发送GET_CONTENT_PROVIDER_TRANSACTION命令,得到对应ContentProvider的BinderProxy。
如果该Provider已被调用进程使用过,则调用进程会保留使用过provider的HashMap。此时直接从此表查询即得。
2.调用BinderProxy的query()
通过下图,可以清楚的看到,getContentResolver().query调用时首先得到Actiivity服务,再次查询Activity服务中记录的对应ContentProviderRecord.如果发现此ContentProvider尚未publish则引发publish该ContentProvider,详见分析二一文。查询到ContentProviderRecord后返回对应MediaProvider的IBinder并返回给调用者。
整个调用过程中需要经过两次Binder调用以实现跨进程访问,即:
Calling Process -> ActivityManagerService Process -> MediaProvider process
Detailed call sequence(If calling process doesn't ever used the Provider):
源代码调用路径:
第1,2步:
frameworks/base/core/java/android/app/ContextImpl.java
- private static final class ApplicationContentResolver extends ContentResolver {
- public ApplicationContentResolver(Context context, ActivityThread mainThread) {
- super(context);
- mMainThread = mainThread;
- }
- @Override
- protected IContentProvider acquireProvider(Context context, String name) {
- return mMainThread.acquireProvider(context, name);
- }
private static final class ApplicationContentResolver extends ContentResolver {
public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = mainThread;
}
@Override
protected IContentProvider acquireProvider(Context context, String name) {
return mMainThread.acquireProvider(context, name);
}
ActivityThread
- public final IContentProvider acquireProvider(Context c, String name) {
- IContentProvider provider = getProvider(c, name);
- if(provider == null)
- return null;
- IBinder jBinder = provider.asBinder();
- synchronized(mProviderMap) {
- ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
- if(prc == null) {
- mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); //创建对此Provider的引用计数
- } else {
- prc.count++; //计数+1
- } //end else
- } //end synchronized
- return provider;
- }
public final IContentProvider acquireProvider(Context c, String name) {
IContentProvider provider = getProvider(c, name);
if(provider == null)
return null;
IBinder jBinder = provider.asBinder();
synchronized(mProviderMap) {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if(prc == null) {
mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); //创建对此Provider的引用计数
} else {
prc.count++; //计数+1
} //end else
} //end synchronized
return provider;
}
得到名字为name的Provider
- private final IContentProvider getProvider(Context context, String name) {
- IContentProvider existing = getExistingProvider(context, name);
- if (existing != null) {
- return existing; //Provider已经publish,直接返回
- }
- IActivityManager.ContentProviderHolder holder = null;
- try {
- holder = ActivityManagerNative.getDefault().getContentProvider(
- getApplicationThread(), name);
- } catch (RemoteException ex) {
- }
- if (holder == null) {
- Slog.e(TAG, "Failed to find provider info for " + name);
- return null;
- }
- IContentProvider prov = installProvider(context, holder.provider,
- holder.info, true);
- if (holder.noReleaseNeeded || holder.provider == null) {
- // We are not going to release the provider if it is an external
- // provider that doesn't care about being released, or if it is
- // a local provider running in this process.
- //Slog.i(TAG, "*** NO RELEASE NEEDED");
- synchronized(mProviderMap) {
- mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000)); //为何holder.provider == null??
- }
- }
- return prov;
- }
private final IContentProvider getProvider(Context context, String name) {
IContentProvider existing = getExistingProvider(context, name);
if (existing != null) {
return existing; //Provider已经publish,直接返回
}
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), name);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + name);
return null;
}
IContentProvider prov = installProvider(context, holder.provider,
holder.info, true);
if (holder.noReleaseNeeded || holder.provider == null) {
// We are not going to release the provider if it is an external
// provider that doesn't care about being released, or if it is
// a local provider running in this process.
//Slog.i(TAG, "*** NO RELEASE NEEDED");
synchronized(mProviderMap) {
mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000)); //为何holder.provider == null??
}
}
return prov;
}
得到ActivityManagerService。
- static public IActivityManager getDefault()
- {
- if (gDefault != null) {
- return gDefault;
- }
- IBinder b = ServiceManager.getService("activity");
- gDefault = asInterface(b);
- return gDefault;
- }
static public IActivityManager getDefault()
{
if (gDefault != null) {
return gDefault;
}
IBinder b = ServiceManager.getService("activity");
gDefault = asInterface(b);
return gDefault;
}
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
- public final ContentProviderHolder getContentProvider(
- IApplicationThread caller, String name) {
- if (caller == null) {
- String msg = "null IApplicationThread when getting content provider "
- + name;
- throw new SecurityException(msg);
- }
- return getContentProviderImpl(caller, name);
- }
- private final ContentProviderHolder getContentProviderImpl(
- IApplicationThread caller, String name) {
- ContentProviderRecord cpr;
- ProviderInfo cpi = null;
- synchronized(this) {
- ProcessRecord r = null;
- if (caller != null) {
- r = getRecordForAppLocked(caller); //caller app must be registered
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when getting content provider " + name);
- }
- }
- // First check if this content provider has been published...
- cpr = mProvidersByName.get(name);
- if (cpr != null) {
- cpi = cpr.info;
- String msg;
- if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) { //检查app是否有访问权限
- throw new SecurityException(msg);
- }
- if (r != null && cpr.canRunHere(r)) {
- // This provider has been published or is in the process
- // of being published... but it is also allowed to run
- // in the caller's process, so don't make a connection
- // and just let the caller instantiate its own instance.
- if (cpr.provider != null) {
- // don't give caller the provider object, it needs
- // to make its own.
- cpr = new ContentProviderRecord(cpr);
- }
- return cpr;
- }
- final long origId = Binder.clearCallingIdentity();
- // In this case the provider instance already exists, so we can
- // return it right away.
- if (r != null) {
- if (DEBUG_PROVIDER) Slog.v(TAG,
- "Adding provider requested by "
- + r.processName + " from process "
- + cpr.info.processName);
- Integer cnt = r.conProviders.get(cpr);
- if (cnt == null) {
- r.conProviders.put(cpr, new Integer(1));
- } else {
- r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
- }
- cpr.clients.add(r);
- if (cpr.app != null && r.setAdj <= PERCEPTIBLE_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.
- updateLruProcessLocked(cpr.app, false, true);
- }
- } else {
- cpr.externals++;
- }
- if (cpr.app != null) {
- updateOomAdjLocked(cpr.app);
- }
- Binder.restoreCallingIdentity(origId);
- }
- ...
- }
- // Wait for the provider to be published...
- 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");
- EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
- cpi.applicationInfo.packageName,
- cpi.applicationInfo.uid, name);
- return null;
- }
- try {
- cpr.wait(); //publishContentProvider结束后会notify
- } catch (InterruptedException ex) {
- }
- }
- }
- return cpr;
- }
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name) {
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name);
}
private final ContentProviderHolder getContentProviderImpl(
IApplicationThread caller, String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
synchronized(this) {
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller); //caller app must be registered
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
// First check if this content provider has been published...
cpr = mProvidersByName.get(name);
if (cpr != null) {
cpi = cpr.info;
String msg;
if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) { //检查app是否有访问权限
throw new SecurityException(msg);
}
if (r != null && cpr.canRunHere(r)) {
// This provider has been published or is in the process
// of being published... but it is also allowed to run
// in the caller's process, so don't make a connection
// and just let the caller instantiate its own instance.
if (cpr.provider != null) {
// don't give caller the provider object, it needs
// to make its own.
cpr = new ContentProviderRecord(cpr);
}
return cpr;
}
final long origId = Binder.clearCallingIdentity();
// In this case the provider instance already exists, so we can
// return it right away.
if (r != null) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName);
Integer cnt = r.conProviders.get(cpr);
if (cnt == null) {
r.conProviders.put(cpr, new Integer(1));
} else {
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
}
cpr.clients.add(r);
if (cpr.app != null && r.setAdj <= PERCEPTIBLE_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.
updateLruProcessLocked(cpr.app, false, true);
}
} else {
cpr.externals++;
}
if (cpr.app != null) {
updateOomAdjLocked(cpr.app);
}
Binder.restoreCallingIdentity(origId);
}
...
}
// Wait for the provider to be published...
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");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
cpr.wait(); //publishContentProvider结束后会notify
} catch (InterruptedException ex) {
}
}
}
return cpr;
}
frameworks/base/core/java/android/content/ContentProviderNative.java
- final class ContentProviderProxy implements IContentProvider
- {
- public Cursor query(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) throws RemoteException {
- //TODO make a pool of windows so we can reuse memory dealers
- CursorWindow window = new CursorWindow(false /* window will be used remotely */);
- BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
- IBulkCursor bulkCursor = bulkQueryInternal(
- url, projection, selection, selectionArgs, sortOrder,
- adaptor.getObserver(), window,
- adaptor);
- return adaptor;
- }
- private IBulkCursor bulkQueryInternal(
- Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder,
- IContentObserver observer, CursorWindow window,
- BulkCursorToCursorAdaptor adaptor) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IContentProvider.descriptor);
- url.writeToParcel(data, 0);
- int length = 0;
- if (projection != null) {
- length = projection.length;
- }
- data.writeInt(length);
- for (int i = 0; i < length; i++) {
- data.writeString(projection[i]);
- }
- data.writeString(selection);
- if (selectionArgs != null) {
- length = selectionArgs.length;
- } else {
- length = 0;
- }
- data.writeInt(length);
- for (int i = 0; i < length; i++) {
- data.writeString(selectionArgs[i]);
- }
- data.writeString(sortOrder);
- data.writeStrongBinder(observer.asBinder());
- window.writeToParcel(data, 0);
- // Flag for whether or not we want the number of rows in the
- // cursor and the position of the "_id" column index (or -1 if
- // non-existent). Only to be returned if binder != null.
- final boolean wantsCursorMetadata = (adaptor != null);
- data.writeInt(wantsCursorMetadata ? 1 : 0);
- mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
- IBulkCursor bulkCursor = null;
- IBinder bulkCursorBinder = reply.readStrongBinder();
- if (bulkCursorBinder != null) {
- bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
- if (wantsCursorMetadata) {
- int rowCount = reply.readInt();
- int idColumnPosition = reply.readInt();
- if (bulkCursor != null) {
- adaptor.set(bulkCursor, rowCount, idColumnPosition);
- }
- }
- }
- data.recycle();
- reply.recycle();
- return bulkCursor;
- }
final class ContentProviderProxy implements IContentProvider
{
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
//TODO make a pool of windows so we can reuse memory dealers
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
IBulkCursor bulkCursor = bulkQueryInternal(
url, projection, selection, selectionArgs, sortOrder,
adaptor.getObserver(), window,
adaptor);
return adaptor;
}
private IBulkCursor bulkQueryInternal(
Uri url, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
IContentObserver observer, CursorWindow window,
BulkCursorToCursorAdaptor adaptor) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IContentProvider.descriptor);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
length = projection.length;
}
data.writeInt(length);
for (int i = 0; i < length; i++) {
data.writeString(projection[i]);
}
data.writeString(selection);
if (selectionArgs != null) {
length = selectionArgs.length;
} else {
length = 0;
}
data.writeInt(length);
for (int i = 0; i < length; i++) {
data.writeString(selectionArgs[i]);
}
data.writeString(sortOrder);
data.writeStrongBinder(observer.asBinder());
window.writeToParcel(data, 0);
// Flag for whether or not we want the number of rows in the
// cursor and the position of the "_id" column index (or -1 if
// non-existent). Only to be returned if binder != null.
final boolean wantsCursorMetadata = (adaptor != null);
data.writeInt(wantsCursorMetadata ? 1 : 0);
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
IBulkCursor bulkCursor = null;
IBinder bulkCursorBinder = reply.readStrongBinder();
if (bulkCursorBinder != null) {
bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
if (wantsCursorMetadata) {
int rowCount = reply.readInt();
int idColumnPosition = reply.readInt();
if (bulkCursor != null) {
adaptor.set(bulkCursor, rowCount, idColumnPosition);
}
}
}
data.recycle();
reply.recycle();
return bulkCursor;
}
frameworks/base/core/java/android/content/ContentProvider.java
- class Transport extends ContentProviderNative {
- public Cursor query(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String sortOrder) {
- enforceReadPermission(uri);
- return ContentProvider.this.query(uri, projection, selection,
- selectionArgs, sortOrder);
- }
class Transport extends ContentProviderNative {
...
public Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
enforceReadPermission(uri);
return ContentProvider.this.query(uri, projection, selection,
selectionArgs, sortOrder);
}
MediaProvider.java
- public Cursor query(Uri uri, String[] projectionIn, String selection,
- String[] selectionArgs, String sort) { //调用到真正做事情的地方