Android應用程序組件Content Provider的啟動過程源代碼分析

通過前面的學習,我們知道在Android系統中,Content Provider可以為不同的應用程序訪問相同的數據提供統一的入口。Content Provider一般是運行在獨立的進程中的,每一個Content Provider在系統中只有一個實例存在,其它應用程序首先要找到這個實例,然後才能訪問它的數據。那麼,系統中的Content Provider實例是由誰來負責啟動的呢?本文將回答這個問題。

Content Provider和應用程序組件Activity、Service一樣,需要在AndroidManifest.xml文件中配置之後才能使用。系統在安裝包含Content Provider的應用程序的時候,會把這些Content Provider的描述信息保存起來,其中最重要的就是Content Provider的Authority信息,Android應用程序的安裝過程具體可以參考Android應用程序安裝過程源代碼分析一文。注意,安裝應用程序的時候,並不會把相應的Content Provider載入到內存中來,系統採取的是懶載入的機制,等到第一次要使用這個Content Provider的時候,系統才會把它載入到內存中來,下次再要使用這個Content Provider的時候,就可以直接返回了。

本文以前面一篇文章Android應用程序組件Content Provider應用實例中的例子來詳細分析Content Provider的啟動過程。在Android應用程序組件Content Provider應用實例這篇文章介紹的應用程序Article中,第一次使用ArticlesProvider這個Content Provider的地方是ArticlesAdapter類的getArticleCount函數,因為MainActivity要在ListView中顯示文章信息列表時, 首先要知道ArticlesProvider中的文章信息的數量。從ArticlesAdapter類的getArticleCount函數調用開始,一直到ArticlesProvider類的onCreate函數被調用,就是ArticlesProvider的完整啟動過程,下面我們就先看看這個過程的序列圖,然後再詳細分析每一個步驟:

Step 1. ArticlesAdapter.getArticleCount

這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序Artilce源代碼工程目錄下,在文件為packages/experimental/Article/src/shy/luo/article/ArticlesAdapter.java中:

[java] view plaincopy

  1. publicclassArticlesAdapter{
  2. ......
  3. privateContentResolverresolver=null;
  4. publicArticlesAdapter(Contextcontext){
  5. resolver=context.getContentResolver();
  6. }
  7. ......
  8. publicintgetArticleCount(){
  9. intcount=0;
  10. try{
  11. IContentProviderprovider=resolver.acquireProvider(Articles.CONTENT_URI);
  12. Bundlebundle=provider.call(Articles.METHOD_GET_ITEM_COUNT,null,null);
  13. count=bundle.getInt(Articles.KEY_ITEM_COUNT,0);
  14. }catch(RemoteExceptione){
  15. e.printStackTrace();
  16. }
  17. returncount;
  18. }
  19. ......
  20. }

這個函數通過應用程序上下文的ContentResolver介面resolver的acquireProvider函數來獲得與Articles.CONTENT_URI對應的Content Provider對象的IContentProvider介面。常量Articles.CONTENT_URI是在應用程序ArticlesProvider中定義的,它的值為「content://shy.luo.providers.articles/item」,對應的Content Provider就是ArticlesProvider了。

Step 2. ContentResolver.acqireProvider

這個函數定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:

[java] view plaincopy

  1. publicabstractclassContentResolver{
  2. ......
  3. publicfinalIContentProvideracquireProvider(Uriuri){
  4. if(!SCHEME_CONTENT.equals(uri.getScheme())){
  5. returnnull;
  6. }
  7. Stringauth=uri.getAuthority();
  8. if(auth!=null){
  9. returnacquireProvider(mContext,uri.getAuthority());
  10. }
  11. returnnull;
  12. }
  13. ......
  14. }

函數首先驗證參數uri的scheme是否正確,即是否是以content://開頭,然後取出它的authority部分,最後調用另外一個成員函數acquireProvider執行獲取ContentProvider介面的操作。在我們這個情景中,參數uri的authority的內容便是「shy.luo.providers.articles」了。

從ContentResolver類的定義我們可以看出,它是一個抽象類,兩個參數版本的acquireProvider函數是由它的子類來實現的。回到Step 1中,這個ContentResolver介面是通過應用程序上下文Context對象的getContentResolver函數來獲得的,而應用程序上下文Context是由ContextImpl類來實現的,它定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:

[java] view plaincopy

  1. classContextImplextendsContext{
  2. ......
  3. privateApplicationContentResolvermContentResolver;
  4. ......
  5. finalvoidinit(LoadedApkpackageInfo,
  6. IBinderactivityToken,ActivityThreadmainThread,
  7. Resourcescontainer){
  8. ......
  9. mContentResolver=newApplicationContentResolver(this,mainThread);
  10. ......
  11. }
  12. ......
  13. @Override
  14. publicContentResolvergetContentResolver(){
  15. returnmContentResolver;
  16. }
  17. ......
  18. }

ContextImpl類的init函數是在應用程序啟動的時候調用的,具體可以參考Android應用程序啟動過程源代碼分析一文中的Step 34。

因此,在上面的ContentResolver類的acquireProvider函數裡面接下來要調用的ApplicationContentResolver類的acquireProvider函數。

Step 3.ApplicationContentResolve.acquireProvider

這個函數定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:

[java] view plaincopy

  1. classContextImplextendsContext{
  2. ......
  3. privatestaticfinalclassApplicationContentResolverextendsContentResolver{
  4. ......
  5. @Override
  6. protectedIContentProvideracquireProvider(Contextcontext,Stringname){
  7. returnmMainThread.acquireProvider(context,name);
  8. }
  9. ......
  10. privatefinalActivityThreadmMainThread;
  11. }
  12. ......
  13. }

它調用ActivityThread類的acquireProvider函數進一步執行獲取Content Provider介面的操作。

Step 4. ActivityThread.acquireProvider

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. publicfinalIContentProvideracquireProvider(Contextc,Stringname){
  4. IContentProviderprovider=getProvider(c,name);
  5. if(provider==null)
  6. returnnull;
  7. ......
  8. returnprovider;
  9. }
  10. ......
  11. }

它又是調用了另外一個成員函數getProvider來進一步執行獲取Content Provider介面的操作。

Step 5.ActivityThread.getProvider

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalIContentProvidergetExistingProvider(Contextcontext,Stringname){
  4. synchronized(mProviderMap){
  5. finalProviderClientRecordpr=mProviderMap.get(name);
  6. if(pr!=null){
  7. returnpr.mProvider;
  8. }
  9. returnnull;
  10. }
  11. }
  12. ......
  13. privatefinalIContentProvidergetProvider(Contextcontext,Stringname){
  14. IContentProviderexisting=getExistingProvider(context,name);
  15. if(existing!=null){
  16. returnexisting;
  17. }
  18. IActivityManager.ContentProviderHolderholder=null;
  19. try{
  20. holder=ActivityManagerNative.getDefault().getContentProvider(
  21. getApplicationThread(),name);
  22. }catch(RemoteExceptionex){
  23. }
  24. IContentProviderprov=installProvider(context,holder.provider,
  25. holder.info,true);
  26. ......
  27. returnprov;
  28. }
  29. ......
  30. }

這個函數首先會通過getExistingProvider函數來檢查本地是否已經存在這個要獲取的ContentProvider介面,如果存在,就直接返回了。本地已經存在的ContextProvider介面保存在ActivityThread類的mProviderMap成員變數中,以ContentProvider對應的URI的authority為鍵值保存。在我們這個情景中,因為是第一次調用ArticlesProvider介面,因此,這時候通過getExistingProvider函數得到的IContentProvider介面為null,於是下面就會調用ActivityManagerService服務的getContentProvider介面來獲取一個ContentProviderHolder對象holder,這個對象就包含了我們所要獲取的ArticlesProvider介面,在將這個介面返回給調用者之後,還會調用installProvider函數來把這個介面保存在本地中,以便下次要使用這個ContentProvider介面時,直接就可以通過getExistingProvider函數獲取了。

我們先進入到ActivityManagerService服務的getContentProvider函數中看看它是如何獲取我們所需要的ArticlesProvider介面的,然後再返回來看看installProvider函數的實現。

Step 6.ActivityManagerService.getContentProvider

這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityManagerServiceextendsActivityManagerNative
  2. implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
  3. ......
  4. publicfinalContentProviderHoldergetContentProvider(
  5. IApplicationThreadcaller,Stringname){
  6. ......
  7. returngetContentProviderImpl(caller,name);
  8. }
  9. ......
  10. }

它調用getContentProviderImpl函數來進一步執行操作。

Step 7.ActivityManagerService.getContentProviderImpl

這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityManagerServiceextendsActivityManagerNative
  2. implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
  3. ......
  4. privatefinalContentProviderHoldergetContentProviderImpl(
  5. IApplicationThreadcaller,Stringname){
  6. ContentProviderRecordcpr;
  7. ProviderInfocpi=null;
  8. synchronized(this){
  9. ProcessRecordr=null;
  10. if(caller!=null){
  11. r=getRecordForAppLocked(caller);
  12. ......
  13. }
  14. //Firstcheckifthiscontentproviderhasbeenpublished...
  15. cpr=mProvidersByName.get(name);
  16. if(cpr!=null){
  17. ......
  18. }else{
  19. try{
  20. cpi=AppGlobals.getPackageManager().
  21. resolveContentProvider(name,
  22. STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS);
  23. }catch(RemoteExceptionex){
  24. }
  25. ......
  26. }
  27. cpr=mProvidersByClass.get(cpi.name);
  28. finalbooleanfirstClass=cpr==null;
  29. if(firstClass){
  30. try{
  31. ApplicationInfoai=
  32. AppGlobals.getPackageManager().
  33. getApplicationInfo(
  34. cpi.applicationInfo.packageName,
  35. STOCK_PM_FLAGS);
  36. ......
  37. cpr=newContentProviderRecord(cpi,ai);
  38. }catch(RemoteExceptionex){
  39. //pmisinsameprocess,thiswillneverhappen.
  40. }
  41. }
  42. if(r!=null&&cpr.canRunHere(r)){
  43. //Ifthisisamultiprocessprovider,thenjustreturnits
  44. //infoandallowthecallertoinstantiateit.Onlydo
  45. //thisiftheprovideristhesameuserasthecaller"s
  46. //process,orcanrunasroot(socanbeinanyprocess).
  47. returncpr;
  48. }
  49. ......
  50. //Thisissingleprocess,andourappisnowconnectingtoit.
  51. //Seeifwearealreadyintheprocessoflaunchingthis
  52. //provider.
  53. finalintN=mLaunchingProviders.size();
  54. inti;
  55. for(i=0;i<N;i++){
  56. if(mLaunchingProviders.get(i)==cpr){
  57. break;
  58. }
  59. }
  60. //Iftheproviderisnotalreadybeinglaunched,thengetit
  61. //started.
  62. if(i>=N){
  63. finallongorigId=Binder.clearCallingIdentity();
  64. ProcessRecordproc=startProcessLocked(cpi.processName,
  65. cpr.appInfo,false,0,"contentprovider",
  66. newComponentName(cpi.applicationInfo.packageName,
  67. cpi.name),false);
  68. ......
  69. mLaunchingProviders.add(cpr);
  70. ......
  71. }
  72. //Makesuretheproviderispublished(thesameproviderclass
  73. //maybepublishedundermultiplenames).
  74. if(firstClass){
  75. mProvidersByClass.put(cpi.name,cpr);
  76. }
  77. cpr.launchingApp=proc;
  78. mProvidersByName.put(name,cpr);
  79. ......
  80. }
  81. //Waitfortheprovidertobepublished...
  82. synchronized(cpr){
  83. while(cpr.provider==null){
  84. ......
  85. try{
  86. cpr.wait();
  87. }catch(InterruptedExceptionex){
  88. }
  89. }
  90. }
  91. returncpr;
  92. }
  93. ......
  94. }

這個函數比較長,我們一步一步地分析。 函數首先是獲取調用者的進程記錄塊信息:[java] view plaincopy

  1. ProcessRecordr=null;
  2. if(caller!=null){
  3. r=getRecordForAppLocked(caller);
  4. ......
  5. }

在我們這個情景中,要獲取的就是應用程序Article的進程記錄塊信息了,後面會用到。

在ActivityManagerService中,有兩個成員變數是用來保存系統中的Content Provider信息的,一個是mProvidersByName,一個是mProvidersByClass,前者是以Content Provider的authoriry值為鍵值來保存的,後者是以Content Provider的類名為鍵值來保存的。一個Content Provider可以有多個authority,而只有一個類來和它對應,因此,這裡要用兩個Map來保存,這裡為了方便根據不同條件來快速查找而設計的。下面的代碼就是用來檢查要獲取的Content Provider是否已經加存在的了:

[java] view plaincopy

  1. //Firstcheckifthiscontentproviderhasbeenpublished...
  2. cpr=mProvidersByName.get(name);
  3. if(cpr!=null){
  4. ......
  5. }else{
  6. try{
  7. cpi=AppGlobals.getPackageManager().
  8. resolveContentProvider(name,
  9. STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS);
  10. }catch(RemoteExceptionex){
  11. }
  12. ......
  13. }
  14. cpr=mProvidersByClass.get(cpi.name);
  15. finalbooleanfirstClass=cpr==null;
  16. if(firstClass){
  17. try{
  18. ApplicationInfoai=
  19. AppGlobals.getPackageManager().
  20. getApplicationInfo(
  21. cpi.applicationInfo.packageName,
  22. STOCK_PM_FLAGS);
  23. ......
  24. cpr=newContentProviderRecord(cpi,ai);
  25. }catch(RemoteExceptionex){
  26. //pmisinsameprocess,thiswillneverhappen.
  27. }
  28. }

在我們這個情景中,由於是第一次調用ArticlesProvider介面,因此,在mProvidersByName和mProvidersByClass兩個Map中都不存在ArticlesProvider的相關信息,因此,這裡會通過AppGlobals.getPackageManager函數來獲得PackageManagerService服務介面,然後分別通過它的resolveContentProvider和getApplicationInfo函數來分別獲取ArticlesProvider應用程序的相關信息,分別保存在cpi和cpr這兩個本地變數中。這些信息都是在安裝應用程序的過程中保存下來的,具體可以參考Android應用程序安裝過程源代碼分析一文。

接下去這個代碼判斷當前要獲取的Content Provider是否允許在客戶進程中載入,即查看一個這個Content Provider否配置了multiprocess屬性為true,如果允許在客戶進程中載入,就直接返回了這個Content Provider的信息了:

[java] view plaincopy

  1. if(r!=null&&cpr.canRunHere(r)){
  2. //Ifthisisamultiprocessprovider,thenjustreturnits
  3. //infoandallowthecallertoinstantiateit.Onlydo
  4. //thisiftheprovideristhesameuserasthecaller"s
  5. //process,orcanrunasroot(socanbeinanyprocess).
  6. returncpr;
  7. }

在我們這個情景中,要獲取的ArticlesProvider設置了要在獨立的進程中運行,因此,繼續往下執行:[java] view plaincopy

  1. //Thisissingleprocess,andourappisnowconnectingtoit.
  2. //Seeifwearealreadyintheprocessoflaunchingthis
  3. //provider.
  4. finalintN=mLaunchingProviders.size();
  5. inti;
  6. for(i=0;i<N;i++){
  7. if(mLaunchingProviders.get(i)==cpr){
  8. break;
  9. }
  10. }

系統中所有正在載入的Content Provider都保存在mLaunchingProviders成員變數中。在載入相應的Content Provider之前,首先要判斷一下它是可否正在被其它應用程序載入,如果是的話,就不用重複載入了。在我們這個情景中,沒有其它應用程序也正在載入ArticlesProvider這個Content Provider,繼續往前執行:[java] view plaincopy

  1. //Iftheproviderisnotalreadybeinglaunched,thengetit
  2. //started.
  3. if(i>=N){
  4. finallongorigId=Binder.clearCallingIdentity();
  5. ProcessRecordproc=startProcessLocked(cpi.processName,
  6. cpr.appInfo,false,0,"contentprovider",
  7. newComponentName(cpi.applicationInfo.packageName,
  8. cpi.name),false);
  9. ......
  10. mLaunchingProviders.add(cpr);
  11. ......
  12. }

這裡的條件i >= N為true,就表明沒有其它應用程序正在載入這個Content Provider,因此,就要調用startProcessLocked函數來啟動一個新的進程來載入這個Content Provider對應的類了,然後把這個正在載入的信息增加到mLaunchingProviders中去。我們先接著分析這個函數,然後再來看在新進程中載入Content Provider的過程,繼續往下執行:[java] view plaincopy

  1. //Makesuretheproviderispublished(thesameproviderclass
  2. //maybepublishedundermultiplenames).
  3. if(firstClass){
  4. mProvidersByClass.put(cpi.name,cpr);
  5. }
  6. cpr.launchingApp=proc;
  7. mProvidersByName.put(name,cpr);

這段代碼把這個Content Provider的信息分別保存到mProvidersByName和mProviderByCalss兩個Map中去,以方便後續查詢。

因為我們需要獲取的Content Provider是在新的進程中載入的,而getContentProviderImpl這個函數是在系統進程中執行的,它必須要等到要獲取的Content Provider是在新的進程中載入完成後才能返回,這樣就涉及到進程同步的問題了。這裡使用的同步方法是不斷地去檢查變數cpr的provider域是否被設置了。當要獲取的Content Provider在新的進程載入完成之後,它會通過Binder進程間通信機制調用到系統進程中,把這個cpr變數的provider域設置為已經載入好的Content Provider介面,這時候,函數getContentProviderImpl就可以返回了。下面的代碼就是用來等待要獲取的Content Provider是在新的進程中載入完成的:

[java] view plaincopy

  1. //Waitfortheprovidertobepublished...
  2. synchronized(cpr){
  3. while(cpr.provider==null){
  4. ......
  5. try{
  6. cpr.wait();
  7. }catch(InterruptedExceptionex){
  8. }
  9. }
  10. }

下面我們再分析在新進程中載入ArticlesProvider這個Content Provider的過程。

Step 8. ActivityManagerService.startProcessLocked

Step 9. Process.start

Step 10. ActivityThread.main

Step 11. ActivityThread.attach

Step 12. ActivityManagerService.attachApplication

這五步是標準的Android應用程序啟動步驟,具體可以參考Android應用程序啟動過程源代碼分析一文中的Step 23到Step 27,或者Android系統在新進程中啟動自定義服務過程(startService)的原理分析一文中的Step 4到Step 9,這裡就不再詳細描述了。

Step 13.ActivityManagerService.attachApplicationLocked

這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityManagerServiceextendsActivityManagerNative
  2. implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
  3. ......
  4. privatefinalbooleanattachApplicationLocked(IApplicationThreadthread,
  5. intpid){
  6. //Findtheapplicationrecordthatisbeingattached...eithervia
  7. //thepidifwearerunninginmultipleprocesses,orjustpullthe
  8. //nextapprecordifweareemulatingprocesswithanonymousthreads.
  9. ProcessRecordapp;
  10. if(pid!=MY_PID&&pid>=0){
  11. synchronized(mPidsSelfLocked){
  12. app=mPidsSelfLocked.get(pid);
  13. }
  14. }elseif(mStartingProcesses.size()>0){
  15. ......
  16. }else{
  17. ......
  18. }
  19. ......
  20. app.thread=thread;
  21. app.curAdj=app.setAdj=-100;
  22. app.curSchedGroup=Process.THREAD_GROUP_DEFAULT;
  23. app.setSchedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;
  24. app.forcingToForeground=null;
  25. app.foregroundServices=false;
  26. app.debugging=false;
  27. ......
  28. booleannormalMode=mProcessesReady||isAllowedWhileBooting(app.info);
  29. Listproviders=normalMode?generateApplicationProvidersLocked(app):null;
  30. try{
  31. ......
  32. thread.bindApplication(processName,app.instrumentationInfo!=null
  33. app.instrumentationInfo:app.info,providers,
  34. app.instrumentationClass,app.instrumentationProfileFile,
  35. app.instrumentationArguments,app.instrumentationWatcher,testMode,
  36. isRestrictedBackupMode||!normalMode,
  37. mConfiguration,getCommonServicesLocked());
  38. ......
  39. }catch(Exceptione){
  40. ......
  41. }
  42. ......
  43. returntrue;
  44. }
  45. ......
  46. privatefinalListgenerateApplicationProvidersLocked(ProcessRecordapp){
  47. Listproviders=null;
  48. try{
  49. providers=AppGlobals.getPackageManager().
  50. queryContentProviders(app.processName,app.info.uid,
  51. STOCK_PM_FLAGS|PackageManager.GET_URI_PERMISSION_PATTERNS);
  52. }catch(RemoteExceptionex){
  53. }
  54. if(providers!=null){
  55. finalintN=providers.size();
  56. for(inti=0;i<N;i++){
  57. ProviderInfocpi=
  58. (ProviderInfo)providers.get(i);
  59. ContentProviderRecordcpr=mProvidersByClass.get(cpi.name);
  60. if(cpr==null){
  61. cpr=newContentProviderRecord(cpi,app.info);
  62. mProvidersByClass.put(cpi.name,cpr);
  63. }
  64. app.pubProviders.put(cpi.name,cpr);
  65. app.addPackage(cpi.applicationInfo.packageName);
  66. ensurePackageDexOpt(cpi.applicationInfo.packageName);
  67. }
  68. }
  69. returnproviders;
  70. }
  71. ......
  72. }

這個函數首先是根據傳進來的進程ID找到相應的進程記錄塊,注意,這個進程ID是應用程序ArticlesProvider的ID,然後對這個進程記錄塊做一些初傾始化的工作。再接下來通過調用generateApplicationProvidersLocked獲得需要在這個過程中載入的Content Provider列表,在我們這個情景中,就只有ArticlesProvider這個Content Provider了。最後調用從參數傳進來的IApplicationThread對象thread的bindApplication函數來執行一些應用程序初始化工作。從Android應用程序啟動過程源代碼分析一文中我們知道,在Android系統中,每一個應用程序進程都載入了一個ActivityThread實例,在這個ActivityThread實例裡面,有一個成員變數mAppThread,它是一個Binder對象,類型為ApplicationThread,實現了IApplicationThread介面,它是專門用來和ActivityManagerService服務進行通信的。因此,調用下面語句:[java] view plaincopy

  1. thread.bindApplication(processName,app.instrumentationInfo!=null
  2. app.instrumentationInfo:app.info,providers,
  3. app.instrumentationClass,app.instrumentationProfileFile,
  4. app.instrumentationArguments,app.instrumentationWatcher,testMode,
  5. isRestrictedBackupMode||!normalMode,
  6. mConfiguration,getCommonServicesLocked());

就會進入到應用程序ArticlesProvider進程中的ApplicationThread對象的bindApplication函數中去。在我們這個情景場,這個函數調用中最重要的參數便是第三個參數providers了,它是我們要處理的對象。

Step 14. ApplicationThread.bindApplication

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalclassApplicationThreadextendsApplicationThreadNative{
  4. ......
  5. publicfinalvoidbindApplication(StringprocessName,
  6. ApplicationInfoappInfo,List<ProviderInfo>providers,
  7. ComponentNameinstrumentationName,StringprofileFile,
  8. BundleinstrumentationArgs,IInstrumentationWatcherinstrumentationWatcher,
  9. intdebugMode,booleanisRestrictedBackupMode,Configurationconfig,
  10. Map<String,IBinder>services){
  11. if(services!=null){
  12. //SetuptheservicecacheintheServiceManager
  13. ServiceManager.initServiceCache(services);
  14. }
  15. AppBindDatadata=newAppBindData();
  16. data.processName=processName;
  17. data.appInfo=appInfo;
  18. data.providers=providers;
  19. data.instrumentationName=instrumentationName;
  20. data.profileFile=profileFile;
  21. data.instrumentationArgs=instrumentationArgs;
  22. data.instrumentationWatcher=instrumentationWatcher;
  23. data.debugMode=debugMode;
  24. data.restrictedBackupMode=isRestrictedBackupMode;
  25. data.config=config;
  26. queueOrSendMessage(H.BIND_APPLICATION,data);
  27. }
  28. ......
  29. }
  30. ......
  31. }

這個函數把相關的信息都封裝成一個AppBindData對象,然後以一個消息的形式發送到主線程的消息隊列中去等等待處理。這個消息最終是是在ActivityThread類的handleBindApplication函數中進行處理的。

Step 15. ActivityThread.handleBindApplication

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalvoidhandleBindApplication(AppBindDatadata){
  4. ......
  5. List<ProviderInfo>providers=data.providers;
  6. if(providers!=null){
  7. installContentProviders(app,providers);
  8. ......
  9. }
  10. ......
  11. }
  12. ......
  13. }

這個函數的內容比較多,我們忽略了其它無關的部分,只關注和Content Provider有關的邏輯,這裡主要就是調用installContentProviders函數來在本地安裝Content Providers信息。

Step 16. ActivityThread.installContentProviders

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalvoidinstallContentProviders(
  4. Contextcontext,List<ProviderInfo>providers){
  5. finalArrayList<IActivityManager.ContentProviderHolder>results=
  6. newArrayList<IActivityManager.ContentProviderHolder>();
  7. Iterator<ProviderInfo>i=providers.iterator();
  8. while(i.hasNext()){
  9. ProviderInfocpi=i.next();
  10. StringBuilderbuf=newStringBuilder(128);
  11. buf.append("Pub");
  12. buf.append(cpi.authority);
  13. buf.append(":");
  14. buf.append(cpi.name);
  15. Log.i(TAG,buf.toString());
  16. IContentProvidercp=installProvider(context,null,cpi,false);
  17. if(cp!=null){
  18. IActivityManager.ContentProviderHoldercph=
  19. newIActivityManager.ContentProviderHolder(cpi);
  20. cph.provider=cp;
  21. results.add(cph);
  22. //Don"teverunloadthisproviderfromtheprocess.
  23. synchronized(mProviderMap){
  24. mProviderRefCountMap.put(cp.asBinder(),newProviderRefCount(10000));
  25. }
  26. }
  27. }
  28. try{
  29. ActivityManagerNative.getDefault().publishContentProviders(
  30. getApplicationThread(),results);
  31. }catch(RemoteExceptionex){
  32. }
  33. }
  34. ......
  35. }

這個函數主要是做了兩件事情,一是調用installProvider來在本地安裝每一個Content Proivder的信息,並且為每一個Content Provider創建一個ContentProviderHolder對象來保存相關的信息。ContentProviderHolder對象是一個Binder對象,是用來把Content Provider的信息傳遞給ActivityManagerService服務的。當這些Content Provider都處理好了以後,還要調用ActivityManagerService服務的publishContentProviders函數來通知ActivityManagerService服務,這個進程中所要載入的Content Provider,都已經準備完畢了,而ActivityManagerService服務的publishContentProviders函數的作用就是用來喚醒在前面Step 7等待的線程的了。我們先來看installProvider的實現,然後再來看ActivityManagerService服務的publishContentProviders函數的實現。

Step 17.ActivityThread.installProvider

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalIContentProviderinstallProvider(Contextcontext,
  4. IContentProviderprovider,ProviderInfoinfo,booleannoisy){
  5. ContentProviderlocalProvider=null;
  6. if(provider==null){
  7. ......
  8. Contextc=null;
  9. ApplicationInfoai=info.applicationInfo;
  10. if(context.getPackageName().equals(ai.packageName)){
  11. c=context;
  12. }elseif(mInitialApplication!=null&&
  13. mInitialApplication.getPackageName().equals(ai.packageName)){
  14. c=mInitialApplication;
  15. }else{
  16. try{
  17. c=context.createPackageContext(ai.packageName,
  18. Context.CONTEXT_INCLUDE_CODE);
  19. }catch(PackageManager.NameNotFoundExceptione){
  20. }
  21. }
  22. ......
  23. try{
  24. finaljava.lang.ClassLoadercl=c.getClassLoader();
  25. localProvider=(ContentProvider)cl.
  26. loadClass(info.name).newInstance();
  27. provider=localProvider.getIContentProvider();
  28. ......
  29. //XXXNeedtocreatethecorrectcontextforthisprovider.
  30. localProvider.attachInfo(c,info);
  31. }catch(java.lang.Exceptione){
  32. ......
  33. }
  34. }elseif(localLOGV){
  35. ......
  36. }
  37. synchronized(mProviderMap){
  38. //Cachethepointerfortheremoteprovider.
  39. Stringnames[]=PATTERN_SEMICOLON.split(info.authority);
  40. for(inti=0;i<names.length;i++){
  41. ProviderClientRecordpr=newProviderClientRecord(names[i],provider,
  42. localProvider);
  43. try{
  44. provider.asBinder().linkToDeath(pr,0);
  45. mProviderMap.put(names[i],pr);
  46. }catch(RemoteExceptione){
  47. returnnull;
  48. }
  49. }
  50. if(localProvider!=null){
  51. mLocalProviders.put(provider.asBinder(),
  52. newProviderClientRecord(null,provider,localProvider));
  53. }
  54. }
  55. returnprovider;
  56. }
  57. ......
  58. }

這個函數的作用主要就是在應用程序進程中把相應的Content Provider類載入進來了,在我們這個種情景中,就是要在ArticlesProvider這個應用程序中把ArticlesProvider這個Content Provider類載入到內存中來了:[java] view plaincopy

  1. finaljava.lang.ClassLoadercl=c.getClassLoader();
  2. localProvider=(ContentProvider)cl.
  3. loadClass(info.name).newInstance();

接著通過調用localProvider的getIContentProvider函數來獲得一個Binder對象,這個Binder對象返回給installContentProviders函數之後,就會傳到ActivityManagerService中去,後續其它應用程序就是通過獲得這個Binder對象來和相應的Content Provider進行通信的了。我們先看一下這個函數的實現,然後再回到installProvider函數中繼續分析。

Step 18. ContentProvider.getIContentProvider

這個函數定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:

[java] view plaincopy

  1. publicabstractclassContentProviderimplementsComponentCallbacks{
  2. ......
  3. privateTransportmTransport=newTransport();
  4. ......
  5. classTransportextendsContentProviderNative{
  6. ......
  7. }
  8. publicIContentProvidergetIContentProvider(){
  9. returnmTransport;
  10. }
  11. ......
  12. }

從這裡我們可以看出,ContentProvider類和Transport類的關係就類似於ActivityThread和ApplicationThread的關係,其它應用程序不是直接調用ContentProvider介面來訪問它的數據,而是通過調用它的內部對象mTransport來間接調用ContentProvider的介面,這一點我們在下一篇文章中分析調用Content Provider介面來獲取共享數據時將會看到。 回到前面的installProvider函數中,它接下來調用下面介面來初始化剛剛載入好的Content Provider:[java] view plaincopy

  1. //XXXNeedtocreatethecorrectcontextforthisprovider.
  2. localProvider.attachInfo(c,info);

同樣,我們先進入到ContentProvider類的attachInfo函數去看看它的實現,然後再回到installProvider函數來。

Step 19.ContentProvider.attachInfo

這個函數定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:

[java] view plaincopy

  1. publicabstractclassContentProviderimplementsComponentCallbacks{
  2. ......
  3. publicvoidattachInfo(Contextcontext,ProviderInfoinfo){
  4. /*
  5. *Onlyallowittobesetonce,soafterthecontentservicegives
  6. *thistousclientscan"tchangeit.
  7. */
  8. if(mContext==null){
  9. mContext=context;
  10. mMyUid=Process.myUid();
  11. if(info!=null){
  12. setReadPermission(info.readPermission);
  13. setWritePermission(info.writePermission);
  14. setPathPermissions(info.pathPermissions);
  15. mExported=info.exported;
  16. }
  17. ContentProvider.this.onCreate();
  18. }
  19. }
  20. ......
  21. }

這個函數很簡單,主要就是根據這個Content Provider的信息info來設置相應的讀寫許可權,然後調用它的子類的onCreate函數來讓子類執行一些初始化的工作。在我們這個情景中,這個子類就是ArticlesProvide應用程序中的ArticlesProvider類了。

Step 20. ArticlesProvider.onCreate

這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序ArtilcesProvider源代碼工程目錄下,在文件為packages/experimental/ArticlesProvider/src/shy/luo/providers/articles/ArticlesProvider.java中:

[java] view plaincopy

  1. publicclassArticlesProviderextendsContentProvider{
  2. ......
  3. @Override
  4. publicbooleanonCreate(){
  5. Contextcontext=getContext();
  6. resolver=context.getContentResolver();
  7. dbHelper=newDBHelper(context,DB_NAME,null,DB_VERSION);
  8. returntrue;
  9. }
  10. ......
  11. }

這個函數主要執行一些簡單的工作,例如,獲得應用程序上下文的ContentResolver介面和創建資料庫操作輔助對象,具體可以參考前面一篇文章Android應用程序組件Content Provider應用實例。

回到前面Step 17中的installProvider函數中,它接下來就是把這些在本地中載入的Content Provider信息保存下來了,以方便後面查詢和使用:

[java] view plaincopy

  1. synchronized(mProviderMap){
  2. //Cachethepointerfortheremoteprovider.
  3. Stringnames[]=PATTERN_SEMICOLON.split(info.authority);
  4. for(inti=0;i<names.length;i++){
  5. ProviderClientRecordpr=newProviderClientRecord(names[i],provider,
  6. localProvider);
  7. try{
  8. provider.asBinder().linkToDeath(pr,0);
  9. mProviderMap.put(names[i],pr);
  10. }catch(RemoteExceptione){
  11. returnnull;
  12. }
  13. }
  14. if(localProvider!=null){
  15. mLocalProviders.put(provider.asBinder(),
  16. newProviderClientRecord(null,provider,localProvider));
  17. }
  18. }

和ActivityMangerService類似,在ActivityThread中,以Content Provider的authority為鍵值來把這個Content Provider的信息保存在mProviderMap成員變數中,因為一個Content Provider可以對應多個authority,因此這裡用一個for循環來處理,同時又以這個Content Provider對應的Binder對象provider來鍵值來把這個Content Provider的信息保存在mLocalProviders成員變數中,表明這是一個在本地載入的Content Provider。

函數installProvider執行完成以後,返回到Step 16中的instalContentProviders函數中,執行下面語句:

[java] view plaincopy

  1. try{
  2. ActivityManagerNative.getDefault().publishContentProviders(
  3. getApplicationThread(),results);
  4. }catch(RemoteExceptionex){
  5. }

前面已經提到,這個函數調用的作用就是通知ActivityMangerService,需要在這個進程中載入的Content Provider已經完載入完成了,參數results就包含了這些已經載入好的Content Provider介面。

Step 21. ActivityMangerService.publishContentProviders

這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityManagerServiceextendsActivityManagerNative
  2. implementsWatchdog.Monitor,BatteryStatsImpl.BatteryCallback{
  3. ......
  4. publicfinalvoidpublishContentProviders(IApplicationThreadcaller,
  5. List<ContentProviderHolder>providers){
  6. ......
  7. synchronized(this){
  8. finalProcessRecordr=getRecordForAppLocked(caller);
  9. ......
  10. finalintN=providers.size();
  11. for(inti=0;i<N;i++){
  12. ContentProviderHoldersrc=providers.get(i);
  13. if(src==null||src.info==null||src.provider==null){
  14. continue;
  15. }
  16. ContentProviderRecorddst=r.pubProviders.get(src.info.name);
  17. if(dst!=null){
  18. mProvidersByClass.put(dst.info.name,dst);
  19. Stringnames[]=dst.info.authority.split(";");
  20. for(intj=0;j<names.length;j++){
  21. mProvidersByName.put(names[j],dst);
  22. }
  23. intNL=mLaunchingProviders.size();
  24. intj;
  25. for(j=0;j<NL;j++){
  26. if(mLaunchingProviders.get(j)==dst){
  27. mLaunchingProviders.remove(j);
  28. j--;
  29. NL--;
  30. }
  31. }
  32. synchronized(dst){
  33. dst.provider=src.provider;
  34. dst.app=r;
  35. dst.notifyAll();
  36. }
  37. ......
  38. }
  39. }
  40. }
  41. }
  42. ......
  43. }

在我們這個情景中,只有一個Content Provider,因此,這裡的N等待1。在中間的for循環裡面,最重要的是下面這個語句:[java] view plaincopy

  1. ContentProviderRecorddst=r.pubProviders.get(src.info.name);

從這裡得到的ContentProviderRecord對象dst,就是在前面Step 7中創建的ContentProviderRecord對象cpr了。在for循環中,首先是把這個Content Provider信息保存好在mProvidersByClass和mProvidersByName中:[java] view plaincopy

  1. mProvidersByClass.put(dst.info.name,dst);
  2. Stringnames[]=dst.info.authority.split(";");
  3. for(intj=0;j<names.length;j++){
  4. mProvidersByName.put(names[j],dst);
  5. }

前面已經說過,這兩個Map中,一個是以類名為鍵值保存Content Provider信息,一個是以authority為鍵值保存Content Provider信息。

因為這個Content Provider已經載入好了,因此,把它從mLaunchingProviders列表中刪除:

[java] view plaincopy

  1. intNL=mLaunchingProviders.size();
  2. intj;
  3. for(j=0;j<NL;j++){
  4. if(mLaunchingProviders.get(j)==dst){
  5. mLaunchingProviders.remove(j);
  6. j--;
  7. NL--;
  8. }
  9. }

最後,設置這個ContentProviderRecord對象dst的provider域為從參數傳進來的Content Provider遠程介面:[java] view plaincopy

  1. synchronized(dst){
  2. dst.provider=src.provider;
  3. dst.app=r;
  4. dst.notifyAll();
  5. }

執行了dst.notiryAll語句後,在Step 7中等待要獲取的Content Provider介面載入完畢的線程就被喚醒了。喚醒之後,它檢查本地ContentProviderRecord變數cpr的provider域不為null,於是就返回了。它最終返回到Step 5中的ActivityThread類的getProvider函數中,繼續往下執行:[java] view plaincopy

  1. IContentProviderprov=installProvider(context,holder.provider,
  2. holder.info,true);

注意,這裡是在Article應用程序中進程中執行installProvider函數的,而前面的Step 17的installProvider函數是在ArticlesProvider應用程序進程中執行的。

Step 22. ActivityThread.installProvider

這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plaincopy

  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalIContentProviderinstallProvider(Contextcontext,
  4. IContentProviderprovider,ProviderInfoinfo,booleannoisy){
  5. ......
  6. if(provider==null){
  7. ......
  8. }elseif(localLOGV){
  9. ......
  10. }
  11. synchronized(mProviderMap){
  12. //Cachethepointerfortheremoteprovider.
  13. Stringnames[]=PATTERN_SEMICOLON.split(info.authority);
  14. for(inti=0;i<names.length;i++){
  15. ProviderClientRecordpr=newProviderClientRecord(names[i],provider,
  16. localProvider);
  17. try{
  18. provider.asBinder().linkToDeath(pr,0);
  19. mProviderMap.put(names[i],pr);
  20. }catch(RemoteExceptione){
  21. returnnull;
  22. }
  23. }
  24. ......
  25. }
  26. returnprovider;
  27. }
  28. ......
  29. }

同樣是執行installProvider函數,與Step 17不同,這裡傳進來的參數provider是不為null的,因此,它不需要執行在本地載入Content Provider的工作,只需要把從ActivityMangerService中獲得的Content Provider介面保存在成員變數mProviderMap中就可以了。

這樣,獲取與"shy.luo.providers.artilces"這個uri對應的Content Provider(shy.luo.providers.articles.ArticlesProvider)就完成了,它同時也是啟動Content Provider的完整過程。第三方應用程序獲得了這個Content Provider的介面之後,就可以訪問它裡面的共享數據了。在下面一篇文章中,我們將重點分析Android應用程序組件Content Provider在不同進程中傳輸數據的過程,即Content Provider在不同應用程序中共享數據的原理,敬請關注。

老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關注!


推薦閱讀:

好奇驅使我對XSS平台的一次小研究
滲透某Webshell箱子全過程,
一類混淆變形的Webshell分析

TAG:程序 | Android | 代碼 | 源代碼 | Android應用 | 代碼分析 | 組件 | 分析 |