ContentProvider啟動流程分析(二)

0x01 扯東扯西的前言&概述


本片博客對應時序圖上的step6—14上接第一篇博客:ContentProvider啟動流程分析一! 下接第三篇ContentProvider啟動流程分析三!

同時此系列博客同步在博客園發布:ContentProvider啟動流程分析系列!詳情戳這里即可訪問~

0x02 ContentProvider啟動流程分析


step6: ActivityManagerProxy#getContentProvider()

代理類ActivityManagerProxy位于ActivityManagerNative.java文件中,其getContentProvider()成員函數的源碼如下:

class ActivityManagerProxy implements IActivityManager {
    ....
    public ContentProviderHolder getContentProvider(IApplicationThread caller,
            String name, int userId, boolean stable) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(name);
        data.writeInt(userId);
        data.writeInt(stable ? 1 : 0);
        mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        ContentProviderHolder cph = null;
        if (res != 0) {
            cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
        }
        data.recycle();
        reply.recycle();
        return cph;
    }
}
  • getContentProvider()函數中,前面7行代碼,將傳進來的參數封裝到一個Parcel對象中;
  • 然后再通過ActivityManagerProxy內部的一個Binder代理對象mRemote,向ActivityManagerService發送一個類型為GET_CONTENT_PROVIDER_TRANSACTION的進程間通信的請求;
  • 最后,再將ActivityManagerService返回的對象封裝成一個ContentProviderHolder對象返回給調用者。

需要注意:以上step1—6是在應用程序AxxApp所在進程中執行的;接下來的step7—9在ActivityManagerService中執行,主要用來處理MainActivity組件發出的類型為GET_CONTENT_PROVIDER_TRANSACTION的進程間通信的請求!

step7: ActivityManagerService#getContentProvider()

ActivityManagerService類的成員函數getContentProvider()源碼如下:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    ....
    @Override
    public final ContentProviderHolder getContentProvider(
            IApplicationThread caller, String name, int userId, boolean stable) {
        ....
        return getContentProviderImpl(caller, name, null, stable, userId);
    }
    ....
}
  • 成員函數getContentProvider(),是用來處理類型為 GET_CONTENT_PROVIDER_TRANSACTION 的進程間通信的請求的;
  • 其內部直接調用getContentProviderImpl(),來進一步獲得參數name對應的ContentProviderHolder對象。

step8: ActivityManagerService#getContentImpl()

getCOntentImpl()函數的作用是,獲得參數name對應的ContentProviderHolder對象,這個對象用來描述一個ContentProvider組件的代理對象;getCOntentImpl()函數源碼較長,我做了部分刪減后如下:

AMS#getContentProviderImpl()
  • 第10行,調用成員函數getRecordForAppLocked(),用來獲取發出訪問請求的應用程序所在進程的相關信息,在我們的場景中,也就是AxxApp應用程序所在進程的相關信息。這些信息,用一個ProcessRecord對象 r 來描述!
  • 在ActivityManagerService中,每一個已經啟動的ContentProvider組件都使用一個ContentProviderRecord對象來描述。這些ContentProviderRecord對象保存在ActivityManagerService類的成員變量mProviderMap中!
  • mProviderMap實質上是一個HashMap,可通過兩種方式進行查找,查找結果是一個ContentProviderRecord對象cpr:
    1. mProviderMap.getProviderByName()函數,通過參數name和參數userId進行查找;參數name指向了將要啟動的ContentProvider組件的android:authorities屬性值,也就是URI值對應的權限部分;參數userId則是調用方應用程序的Linux用戶ID;
    2. mProviderMap.getProviderByClass()函數,通過ContentProvider組件的類名進行查找;

需要注意的是,mProvidersByName和mProvidersByClass的內部實現是哈希表,雖然都是保存的ContentProviderRecord對象,但是mProvidersByName變量是以ContentProvider組件的android:authorities屬性值為關鍵字的,而mProvidersByClass變量是以ContentProvider組件的類名作為關鍵字的!

第15行,首先通過參數name和userId,mProviderMap.getProviderByName()檢查是否存在對應的ContentProvider組件?接下來的邏輯很簡單,分為兩種情況:

  1. 第18行到27行代碼,就是表示這個ContentProvider組件已經發布(啟動)了,此時調用方(AxxApp)就可以直接通過ContentProviderRecord對象cpr來創建一個ContentProviderHolder對象;需要做兩點說明:
    • ContentProviderRecord對象用來記錄ContentProvider組件的發布(啟動)信息,通過其對象cpr的provider成員變量來記錄ContentProvider組件,它們是一對一的關系;
    • ContentProviderHolder是ContentProvider組件的代理對象,它持有一個ContentProvider對象;
  2. 第29到93行代碼,就是表示這個ContentProvider組件還沒有發布,需要進一步驗證,第31行到35行通過mProviderMap.getProviderByClass()方式進行二次查找。如果還是沒有找到?接下來就要啟動這個ContentProvider組件了!
    • 第37到43行,先根據name參數和userId參數創建一個ContentProviderRecord對象;
    • 在啟動cpr描述的ContentProvider組件之前,先判斷這個COntentProvider組件的android:multiprocess屬性值!如果為true,表示這個ContentProvider組件需要在訪問它的應用程序進程中啟動;為false表示需要創建第三方進程并在第三方進程中啟動這個ContentProvider組件;也即第44到53行;
    • 第57行,關注常量mLaunchingProviders,ActivityManagewrService就是把正在啟動的ContentProvider組件保存在mLaunchingProviders中;
    • 第59到65行,通過for循環判斷目標ContentProvider組件的狀態是否處于正在啟動中。如果是,則等待它繼續啟動完成;
    • 如果不是,即66到83行,ActivityManagerService會調用成員函數startProcessLocked()新建一個應用程序進程,專門用來啟動目標ContnrtProvider組件
    • 第85到89行,分別通過兩種方式mProviderMap.putProviderByClass()和mProviderMap.putProviderByName()把啟動的ContentProvider組件保存在mProviderMap變量中;
    • 最后,第93到99行,等待ContentProvider組件發布完成后,直接返回其對應的ContentProviderRecord對象即可;

step9: ActivityMangerService#startProcessLocked()

在step8中,目標ContentProvider組件是通過一個新建的第三方應用程序進程來啟動!而這個第三方應用程序進程,就是通過ActivityMangerService的成員函數startProcessLocked()新建的。它主要通過調用Process的靜態成員函數start()創建一個新的應用程序進程,并且把這個新建的應用程序進程的入口設置為ActivityThread類的靜態成員函數main()!

以上三步都是在ActivityManagerService類中執行的!接下來我們分析新創建的應用程序進程,啟動ContentProvider組件的過程,所以step10和step11是在第三方應用程序進程中完成的。

step10: ActivityThread#main()

ActivityThread.main()函數,會在新創建的應用程序進程中,創建一個ActivityThread對象和一個ApplicationThread對象,然后調用ActivityManagerProxy.attchApplication()函數,將ApplicationThread對象傳遞給ActivityManagerService!

step11: ActivityManagerProxy#attchApplication()

ActivityManagerProxy.attchApplication()函數,先向ActivityManagerService發出一個類型為ATTACH_APPLICATION_TRANSACTION的進程間通信請求,這個請求通過Binder機制處理!然后,就是把step10創建的ApplicationThread對象傳遞給ActivityManagerService!

接下來step12—14又回到ActivityManagerService中執行!主要用來處理新建應用程序進程發出的類型為ATTACH_APPLICATION_TRANSACTION的進程間通信請求。

step12: ActivityManagerService#attachApplication()

ActivityManagerService.attachApplication()函數,它調用了ApplicationManagerService類的成員函數attachApplicationLocked()來處理類型為ATTACH_APPLICATION_TRANSACTION的進程間通信請求,用來執行啟動目標ContentProvider組件的操作!

接下來重點看看ApplicationManagerService類的成員函數attachApplicationLocked()!

step13: ApplicationManagerService#attachApplicationLocked()

ApplicationManagerService類的成員函數attachApplicationLocked()較長,刪減后如下:

private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {app = null;}

    try {
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
    }
    
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
    try {
        ProfilerInfo profilerInfo = profileFile == null ? null :
                                    new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                               profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                               app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                               isRestrictedBackupMode || !normalMode, app.persistent,
                               new Configuration(mConfiguration), app.compat,
                               getCommonServicesLocked(app.isolated),
                               mCoreSettingsObserver.getCoreSettingsLocked());
    }
    return true;
}
  • 注意到,參數pid指向前面所創建的應用程序進程PID,也即第三方應用進程PID,ProcessRecord對象app就是用來記錄新建應用的進程信息的。
  • 在前面的step8中,ActivityManagerService以這個PID為關鍵字將一個ProcessRecord對象保存在了mPidsSelfLocked變量中,這里,首先通過參數pid取回ProcessRecord對象并保存在局部變量app中,然后調用generateApplicationProvidersLocked(app)函數需要在app所描述的第三方進程中啟動的ContentProvider組件!

generateApplicationProvidersLocked()函數源碼如下:

private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord         app) {
    List<ProviderInfo> providers = null;
    try {
        ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager().
            queryContentProviders(app.processName, app.uid,
                    STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
        providers = slice != null ? slice.getList() : null;
    }
    int userId = app.userId;
    if (providers != null) {
        int N = providers.size();
        app.pubProviders.ensureCapacity(N + app.pubProviders.size());
        for (int i=0; i<N; i++) {
            ProviderInfo cpi =
                (ProviderInfo)providers.get(i);
            boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
                    cpi.name, cpi.flags);
            if (singleton && UserHandle.getUserId(app.uid) != UserHandle.USER_OWNER) {
                // This is a singleton provider, but a user besides the
                // default user is asking to initialize a process it runs
                // in...  well, no, it doesn't actually run in this process,
                // it runs in the process of the default user.  Get rid of it.
                providers.remove(i);
                N--;
                i--;
                continue;
            }

            ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
            ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId);
            if (cpr == null) {
                cpr = new ContentProviderRecord(this, cpi, app.info, comp, singleton);
                mProviderMap.putProviderByClass(comp, cpr);
            }
            app.pubProviders.put(cpi.name, cpr);
            if (!cpi.multiprocess || !"android".equals(cpi.packageName)) {
                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode, mProcessStats);
            }
            ensurePackageDexOpt(cpi.applicationInfo.packageName);
        }
    }
    return providers;
}
  • 先去PackageMangerService找目標ContentProvider組件,并保存到列表providers中;
  • 然后,for循環檢查ActivityManagerService是否已經為這些ContentProvider組件創建過對應的ContnetProviderRecord對象來記錄每一個ContentProvider組件;
  • 如果沒有,則分別為它們創建一個ContentProviderRecord對象,并以它們的類名作為關鍵字保存在AMS的全局變量mProviderMap中!

從這里可以看出,一個應用程序進程在啟動的時候,會將它所需要的所有ContentProvider組件全部啟動起來!

最后,繼續關注thread.bindApplication()函數,也即ActivityThread.bindApplication()函數!

step14: ActivityThreadProxy#bindApplication()

ActivityThreadProxy.bindApplication()函數源碼如下:

public final void bindApplication(String packageName, ApplicationInfo info,
        List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
        Bundle testArgs, IInstrumentationWatcher testWatcher,
        IUiAutomationConnection uiAutomationConnection, int debugMode,
        boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
        Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
        Bundle coreSettings) throws RemoteException {
    Parcel data = Parcel.obtain();
    data.writeInterfaceToken(IApplicationThread.descriptor);
    data.writeString(packageName);
    info.writeToParcel(data, 0);
    data.writeTypedList(providers);
    if (testName == null) {
        data.writeInt(0);
    } else {
        data.writeInt(1);
        testName.writeToParcel(data, 0);
    }
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    data.writeBundle(testArgs);
    data.writeStrongInterface(testWatcher);
    data.writeStrongInterface(uiAutomationConnection);
    data.writeInt(debugMode);
    data.writeInt(openGlTrace ? 1 : 0);
    data.writeInt(restrictedBackupMode ? 1 : 0);
    data.writeInt(persistent ? 1 : 0);
    config.writeToParcel(data, 0);
    compatInfo.writeToParcel(data, 0);
    data.writeMap(services);
    data.writeBundle(coreSettings);
    mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);
    data.recycle();
}
  • 先創建一個Parcel對象data用來保存接收的參數;
  • 然后,mRemote是ActivityThreadProxy內部的一個Binder代理對象,通過mRemote向新建的應用程序進程(也即,第三方進程)發送一個類型為BIND_APPLICATION_TRANSACTION的進程間通信請求。

0x03 參考文獻與簡單的結語


以上三步都是在ActivityManagerService中執行的,接下來的step15—24在新建的應用程序進程中執行,主要用來處理類型為BIND_APPLICATION_TRANSACTION的進程間通信請求!對應時序圖上的step15—24

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容