Hi3516的SAMGR--系統服務框架子系統-11-Client與Client的IPC來往
進程A中的線程Aa(也就是service Aa)想要調用某個service/feature的接口,這個接口可能是進程A自己的線程Ab(也就是service Ab)提供的,也可能是進程B的線程Bc(也就是service Bc)提供的。
如果是同進程的不同服務提供的接口,那Aa只需要向自己進程的SamgrLiteImpl g_samgrImpl查詢service/feature名字就可以拿到對應的IUnknown *iUnknown接口,因為是同進程內的相同虛擬地址空間,所以可以直接跨線程調用。如《系統服務框架子系統-4-面向服務架構的實現》中的測試程序所做的那樣。
也就是通過調用SAMGR_GetInstance()->GetDefaultFeatureApi(SERVICE_NAME) 或者SAMGR_GetInstance()->FeatureApi(SERVICE_NAME,FEATURE_NAME)來查詢接口。
如果Aa想調用的service/feature接口是B進程提供的,那首先這個service,肯定在Aa所在進程的SamgrLiteImpl g_samgrImpl里面是找不到記錄的,也就是serviceImpl = NULL:

所以,跨進程的服務/特性的調用,入口就在上面的SAMGR_FindServiceApi(service, feature)。
結合我們的log,搜索一下“%%%%%%%%%%%”這樣一個字符串,這是我標記的一處入口(實際上搜索“SAMGR_FindServiceApi”也差不多)。
看一下log:
- {[bundle_daemon_client]} Initialize: GetDefaultFeatureApi(bundle_daemon)
這是“bundle_daemon_client”在Initialize的時候,向自己所在進程的SamgrLiteImpl g_samgrImpl查詢名字為“bundle_daemon”的服務的DefaultFeatureApi。
通過前后的log,我們可以知道bundle_daemon_client所在進程的信息“pid[ 5]/uid[ 7]/(*)handle[61](*)”,5號進程是“foundation”,而“bundle_daemon”服務是6號進程提供的。
所以會通過SAMGR_FindServiceApi(S[bundle_daemon],F[(null)])來查詢“bundle_daemon”服務提供的接口。
SAMGR_FindServiceApi()及其輔助函數,涉及到了g_remoteRegister.clients 這個字段,我們先來理解一下。
這是一個向量,在本進程的 g_remoteRegister 初始化的時候,也對這個向量做了配置:
- g_remoteRegister.clients = VECTOR_Make((VECTOR_Key)SAMGR_GetSAName, (VECTOR_Compare)SAMGR_CompareSAName);
向量的element:data[x]是一個IUnknown *proxy類型的指針,指向的是一個客戶端代理的接口entry.iUnknown,通過這個指針,可以轉換回IClientProxy類對象、IClientEntry類對象、IDefaultClient類對象,再通過IClientProxy類對象指針去訪問后面的Invoke函數,或者通過IDefaultClient類對象指針去訪問上面的IClientHeader header字段內的key、target等字段。
向量中每一個element,記錄了本進程EP調用過的別的進程提供的服務接口相關的重要身份信息如名字、handle、token等,這樣下次再調用的時候,直接在這里查詢就能獲得這些信息,不需要再次通過IPC去向samgr EP查詢了,它的展開圖如下:

SAMGR_FindServiceApi()的流程如下:


我們接著上面的log往下看一下,

這是第一次bundle_daemon_client 向samgr EP查詢FeatureAPI,但是返回的 handle 是 -1,從接下來的DbgParse_g_server的信息來看,這個時候提供bundle_daemon服務的6號進程,還沒有向samge EP注冊EP和Feature,所以samgr EP自己也查不到。
再看第二次bundle_daemon_client 向samgr EP查詢FeatureAPI,這回查到了,handle是38,token是0,也就是說,SAMGR_CreateIProxy()的第一步,拿到了SvcIdentity identity= {38, 0, 0},跟著接下來的流程跑,就會得到一個完整有效的IDefaultClient對象,把這個對象中的 .iUnknown 地址先保存到g_remoteRegister.clients 向量中去,然后通過這個.iUnknown 地址,就可以調用客戶端代理的Invoke()接口,從而再通過IPC去遠程調用bundle_daemon服務了。

Privider: Pid[6]/Uid[8]/handle[38]:bundle_daemon -> (null)Consumer: Pid[5]/Uid[7]/Tid[53]
從這兩句log可以看出,6號進程的bundle_daemon 服務是provider,查詢并使用該服務的5號進程是consumer,這也是面向服務的架構的一種實現。
我們再看另外一個更明顯的跨進程調用服務的例子:第一個應用進程launcher的孵化啟動。
Log搜索“AppAppAppApp”,這是5號進程通過app_manager準備調用7號進程提供的“appspawn”服務來孵化launcher應用,它拿到了“appspawn”的handle 74、token 0和.iUnknown地址,并把該地址以及相關的重要信息添加到自己的clients向量中。


接下來app_spawn_client調用上面的Invoke{0x249cc84c},這個Invoke{0x249cc84c}就是ProxyInvoke()函數,通過它發送IPC消息給IpcMsg Rceiver::handle[74],token[0],讓它的EP收到和處理該消息,這就到了上一小節提到的Dispatch()和HandleIpc()對IPC消息的處理流程了。

經過消息的轉發和處理,最后是appspawn_service的Invoke()函數開始孵化launcher應用進程,桌面應用開始提供服務。
跨進程的服務接口調用到此結束。
剛好剛才去瀏覽標準系統的 //base/startup/appspawn_standard/目錄,看了一下README_zh,看到下面這張圖就貼過來了,除了通信方式不一樣之外,大體流程是一樣的。
