ContentProvider 原理分析一

本文目标:以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

  1. private static final class ApplicationContentResolver extends ContentResolver {  
  2.     public ApplicationContentResolver(Context context, ActivityThread mainThread) {  
  3.         super(context);  
  4.         mMainThread = mainThread;  
  5.     }  
  6.   
  7.     @Override  
  8.     protected IContentProvider acquireProvider(Context context, String name) {  
  9.         return mMainThread.acquireProvider(context, name);  
  10.     }  
    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

  1. public final IContentProvider acquireProvider(Context c, String name) {  
  2.     IContentProvider provider = getProvider(c, name);  
  3.     if(provider == null)  
  4.         return null;  
  5.     IBinder jBinder = provider.asBinder();  
  6.     synchronized(mProviderMap) {  
  7.         ProviderRefCount prc = mProviderRefCountMap.get(jBinder);  
  8.         if(prc == null) {  
  9.             mProviderRefCountMap.put(jBinder, new ProviderRefCount(1)); //创建对此Provider的引用计数   
  10.         } else {  
  11.             prc.count++; //计数+1   
  12.         } //end else   
  13.     } //end synchronized   
  14.     return provider;  
  15. }  
    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

  1. private final IContentProvider getProvider(Context context, String name) {  
  2.     IContentProvider existing = getExistingProvider(context, name);  
  3.     if (existing != null) {   
  4.         return existing; //Provider已经publish,直接返回   
  5.     }  
  6.   
  7.     IActivityManager.ContentProviderHolder holder = null;  
  8.     try {  
  9.         holder = ActivityManagerNative.getDefault().getContentProvider(  
  10.             getApplicationThread(), name);  
  11.     } catch (RemoteException ex) {  
  12.     }  
  13.     if (holder == null) {  
  14.         Slog.e(TAG, "Failed to find provider info for " + name);  
  15.         return null;  
  16.     }  
  17.   
  18.     IContentProvider prov = installProvider(context, holder.provider,  
  19.             holder.info, true);  
  20.     if (holder.noReleaseNeeded || holder.provider == null) {  
  21.         // We are not going to release the provider if it is an external   
  22.         // provider that doesn't care about being released, or if it is   
  23.         // a local provider running in this process.   
  24.         //Slog.i(TAG, "*** NO RELEASE NEEDED");   
  25.         synchronized(mProviderMap) {  
  26.             mProviderRefCountMap.put(prov.asBinder(), new ProviderRefCount(10000)); //为何holder.provider == null??   
  27.         }  
  28.     }  
  29.     return prov;  
  30. }  
    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

  1. static public IActivityManager getDefault()  
  2. {  
  3.     if (gDefault != null) {  
  4.         return gDefault;  
  5.     }  
  6.     IBinder b = ServiceManager.getService("activity");  
  7.     gDefault = asInterface(b);  
  8.     return gDefault;  
  9. }  
    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

  1.     public final ContentProviderHolder getContentProvider(  
  2.             IApplicationThread caller, String name) {  
  3.         if (caller == null) {  
  4.             String msg = "null IApplicationThread when getting content provider "  
  5.                     + name;  
  6.              throw new SecurityException(msg);  
  7.         }  
  8.   
  9.         return getContentProviderImpl(caller, name);  
  10.     }  
  11.   
  12.     private final ContentProviderHolder getContentProviderImpl(  
  13.         IApplicationThread caller, String name) {  
  14.         ContentProviderRecord cpr;  
  15.         ProviderInfo cpi = null;  
  16.   
  17.         synchronized(this) {  
  18.             ProcessRecord r = null;  
  19.             if (caller != null) {  
  20.                 r = getRecordForAppLocked(caller); //caller app must be registered   
  21.                 if (r == null) {  
  22.                     throw new SecurityException(  
  23.                             "Unable to find app for caller " + caller  
  24.                           + " (pid=" + Binder.getCallingPid()  
  25.                           + ") when getting content provider " + name);  
  26.                 }  
  27.             }  
  28.   
  29.             // First check if this content provider has been published...   
  30.             cpr = mProvidersByName.get(name);  
  31.             if (cpr != null) {  
  32.                 cpi = cpr.info;  
  33.                 String msg;  
  34.                 if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) { //检查app是否有访问权限   
  35.                     throw new SecurityException(msg);  
  36.                 }  
  37.   
  38.                 if (r != null && cpr.canRunHere(r)) {  
  39.                     // This provider has been published or is in the process   
  40.                     // of being published...  but it is also allowed to run   
  41.                     // in the caller's process, so don't make a connection   
  42.                     // and just let the caller instantiate its own instance.   
  43.                     if (cpr.provider != null) {  
  44.                         // don't give caller the provider object, it needs   
  45.                         // to make its own.   
  46.                         cpr = new ContentProviderRecord(cpr);  
  47.                     }  
  48.                     return cpr;  
  49.                 }  
  50.   
  51.                 final long origId = Binder.clearCallingIdentity();  
  52.   
  53.                 // In this case the provider instance already exists, so we can   
  54.                 // return it right away.   
  55.                 if (r != null) {  
  56.                     if (DEBUG_PROVIDER) Slog.v(TAG,  
  57.                             "Adding provider requested by "  
  58.                             + r.processName + " from process "  
  59.                             + cpr.info.processName);  
  60.                     Integer cnt = r.conProviders.get(cpr);  
  61.                     if (cnt == null) {  
  62.                         r.conProviders.put(cpr, new Integer(1));  
  63.                     } else {  
  64.                         r.conProviders.put(cpr, new Integer(cnt.intValue()+1));  
  65.                     }  
  66.                     cpr.clients.add(r);  
  67.                     if (cpr.app != null && r.setAdj <= PERCEPTIBLE_APP_ADJ) {  
  68.                         // If this is a perceptible app accessing the provider,   
  69.                         // make sure to count it as being accessed and thus   
  70.                         // back up on the LRU list.  This is good because   
  71.                         // content providers are often expensive to start.   
  72.                         updateLruProcessLocked(cpr.app, falsetrue);  
  73.                     }  
  74.                 } else {  
  75.                     cpr.externals++;  
  76.                 }  
  77.   
  78.                 if (cpr.app != null) {  
  79.                     updateOomAdjLocked(cpr.app);  
  80.                 }  
  81.   
  82.                 Binder.restoreCallingIdentity(origId);  
  83.   
  84.             }   
  85. ...  
  86.         }  
  87.   
  88.         // Wait for the provider to be published...   
  89.         synchronized (cpr) {  
  90.             while (cpr.provider == null) {  
  91.                 if (cpr.launchingApp == null) {  
  92.                     Slog.w(TAG, "Unable to launch app "  
  93.                             + cpi.applicationInfo.packageName + "/"  
  94.                             + cpi.applicationInfo.uid + " for provider "  
  95.                             + name + ": launching app became null");  
  96.                     EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,  
  97.                             cpi.applicationInfo.packageName,  
  98.                             cpi.applicationInfo.uid, name);  
  99.                     return null;  
  100.                 }  
  101.                 try {  
  102.                     cpr.wait(); //publishContentProvider结束后会notify   
  103.                 } catch (InterruptedException ex) {  
  104.                 }  
  105.             }  
  106.         }  
  107.         return cpr;  
  108.     }  
    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

  1. final class ContentProviderProxy implements IContentProvider  
  2. {  
  3.     public Cursor query(Uri url, String[] projection, String selection,  
  4.             String[] selectionArgs, String sortOrder) throws RemoteException {  
  5.         //TODO make a pool of windows so we can reuse memory dealers   
  6.         CursorWindow window = new CursorWindow(false /* window will be used remotely */);  
  7.         BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();  
  8.         IBulkCursor bulkCursor = bulkQueryInternal(  
  9.             url, projection, selection, selectionArgs, sortOrder,  
  10.             adaptor.getObserver(), window,  
  11.             adaptor);  
  12.         return adaptor;  
  13.     }  
  14.     private IBulkCursor bulkQueryInternal(  
  15.         Uri url, String[] projection,  
  16.         String selection, String[] selectionArgs, String sortOrder,  
  17.         IContentObserver observer, CursorWindow window,  
  18.         BulkCursorToCursorAdaptor adaptor) throws RemoteException {  
  19.         Parcel data = Parcel.obtain();  
  20.         Parcel reply = Parcel.obtain();  
  21.         data.writeInterfaceToken(IContentProvider.descriptor);  
  22.   
  23.         url.writeToParcel(data, 0);  
  24.         int length = 0;  
  25.         if (projection != null) {  
  26.             length = projection.length;  
  27.         }  
  28.         data.writeInt(length);  
  29.         for (int i = 0; i < length; i++) {  
  30.             data.writeString(projection[i]);  
  31.         }  
  32.         data.writeString(selection);  
  33.         if (selectionArgs != null) {  
  34.             length = selectionArgs.length;  
  35.         } else {  
  36.             length = 0;  
  37.         }  
  38.         data.writeInt(length);  
  39.         for (int i = 0; i < length; i++) {  
  40.             data.writeString(selectionArgs[i]);  
  41.         }  
  42.         data.writeString(sortOrder);  
  43.         data.writeStrongBinder(observer.asBinder());  
  44.         window.writeToParcel(data, 0);  
  45.   
  46.         // Flag for whether or not we want the number of rows in the   
  47.         // cursor and the position of the "_id" column index (or -1 if   
  48.         // non-existent).  Only to be returned if binder != null.   
  49.         final boolean wantsCursorMetadata = (adaptor != null);  
  50.         data.writeInt(wantsCursorMetadata ? 1 : 0);  
  51.         mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);  
  52.   
  53.         DatabaseUtils.readExceptionFromParcel(reply);  
  54.   
  55.         IBulkCursor bulkCursor = null;  
  56.         IBinder bulkCursorBinder = reply.readStrongBinder();  
  57.         if (bulkCursorBinder != null) {  
  58.             bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);  
  59.   
  60.             if (wantsCursorMetadata) {  
  61.                 int rowCount = reply.readInt();  
  62.                 int idColumnPosition = reply.readInt();  
  63.                 if (bulkCursor != null) {  
  64.                     adaptor.set(bulkCursor, rowCount, idColumnPosition);  
  65.                 }  
  66.             }  
  67.         }  
  68.   
  69.         data.recycle();  
  70.         reply.recycle();  
  71.   
  72.         return bulkCursor;  
  73.     }  
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

  1. class Transport extends ContentProviderNative {  
  2.   
  3.     public Cursor query(Uri uri, String[] projection,  
  4.             String selection, String[] selectionArgs, String sortOrder) {  
  5.         enforceReadPermission(uri);  
  6.         return ContentProvider.this.query(uri, projection, selection,  
  7.                 selectionArgs, sortOrder);  
  8.     }  
    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

  1. public Cursor query(Uri uri, String[] projectionIn, String selection,  
  2.         String[] selectionArgs, String sort) {   //调用到真正做事情的地方  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值