本文目标:以MediaProvider为例,想搞清楚调用ContentResolver访问各个ContentProvider的调用过程。
Java code:
getContentResolver().query(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,null,null)
具体调用过程是
1.通过ContentResolver先查找对应给定Uri的ContentProvider,返回对应的BinderProxy
如果该Provider尚未被调用进程使用过:
a.通过ServiceManager查找activityservice得到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 theProvider):
源代码调用路径:
第1,2步:
frameworks/base/core/java/android/app/ContextImpl.java
[java] viewplaincopyprint? - 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
[java] viewplaincopyprint? - 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
[java] viewplaincopyprint? - 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。
frameworks/base/core/java/android/app/ActivityManagerNative.java
[java] viewplaincopyprint? - 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
[java] viewplaincopyprint? - 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
[java] viewplaincopyprint? - 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 );
- 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
[java] viewplaincopyprint? - 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
[java] viewplaincopyprint? - public Cursor query(Uri uri, String[] projectionIn, String selection,
- String[] selectionArgs, String sort) { //调用到真正做事情的地方