Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android系統篇之----Binder機制和遠程服務調用機制分析

Android系統篇之----Binder機制和遠程服務調用機制分析

編輯:關於Android編程

一、前景概要

最近要實現Android中免注冊Activity就可以運行的問題,那麼結果是搞定了,就是可以不用在AndroidManifest.xml中聲明這個Activity即可運行,主要是通過騙取系統,偷龍轉鳳技術的,這個知識點後面會詳細講解的,因為在研究了這個問題過程中遇到了很多知識點,當然最重要也是最根本的就是Android中的Binder機制和遠程服務調用機制,而關於Binder機制的話,在Android中算是一個非常大的系統架構模塊了,光這篇文章是肯定不能講解到全部的,而且本人也不是非常的熟悉Binder機制,只是發表個人的理解,並且會用最簡單的語言講解最核心的知識,因為現在有很多知識都在介紹Binder機制,但是大部分說的都太抽象了大部分都看不懂。

 

二、具體需求

接下來幾篇主要是介紹關於Android中的應用啟動流程,通過Hook機制攔截Activity的啟動流程,達到我們想要的功能,同時可以實現Activity無需在AndroidManifest.xml中聲明即可運行的效果。

 

三、Android中遠程服務調用分析

我們如果做過多進程之間通信的話都了解遠程服務的使用了,使用也是非常簡單的,下面來看一下如何定義自己的遠程服務:

1、定義一個AIDL文件

類似於定義接口類型,這個AIDL文件將在本地和遠端都要使用到

\

 

2、定義遠端服務

在遠程服務中的onBind方法,實現AIDL接口的具體方法,並且返回Binder對象

\

 

3、本地創建連接對象

本地創建一個服務連接對象,實現ServiceConnection接口,在連接成功之後,會得到一個遠端傳遞過來的Binder對象,就是上面的遠端服務onBind方法返回的,得到Binder對象之後在進行轉化就可以得到AIDL對象,然後即可調用方法

\

 

4、連接服務

連接服務也是比較簡單的,這時候把上面的連接對象傳遞進去即可

\

 

到這裡我們就看到完成了本地端和遠端的通信了,如果把DemoService遠端服務定義在另外一個進程中,那麼這裡就可以實現多進程通信了。看到上面的步驟很簡單,但是有一個核心的地方就是Demo.Stub類,這個類起著重要的作用,下面來分析一下他的實現,每次定義了AIDL接口文件之後,編譯一下就會在gen目錄中產生對應的java文件了:

\

這裡的實現稍微有點復雜了,但是這些內容其實都是編譯工具幫我們實現的,而這個實現都是有一定規則的:

1、AIDL接口必須實現IInterface接口

關於IInterface接口的實現也很簡單:

\

這裡可以把當前的AIDL對象轉化成一個IBinder類型對象

2、AIDL接口中肯定有一個靜態實現類Stub

這個類必須實現Binder類,以及本身的AIDL接口類型。那麼這個類就具備了Binder類中的四個功能:

1》可以將Binder對象轉化成AIDL對象,調用asInterface方法,可以看到這個方法其實和上面的asBinder方法對立的

2》通信方法onTransact實現,這個方法是最核心的用於通信之間的邏輯實現

3》通過queryLocalInterface方法可以根據類的描述符(字符串可以唯一標識這個遠端服務的名稱即可)獲取到對應的AIDL對象(其實是IInterface類型的)

4》在構造方法中必須調用Binder中的attachInterface方法把當前服務對象和描述符進行關聯

上面看完了Stub類之後,發現他其實是遠端服務Binder對象的一個中間者,用來和客戶端進行交互的,下面再來看一下Proxy類:

\

這個代理對象其實就是遠端傳遞過來Binder對象的一個代理,他是客戶端這邊用戶和服務端交互的中間者。我們在前面的Stub類中可以看到:

\

這裡會把遠端傳遞過來的Binder對象轉化成一個本地對象,發現先是通過queryLocalInterface方法借助服務描述符來獲取對象:

\

而這個mOwner和mDescriptor之間的關系就在attachInterface方法中進行初始化的,也就是在Stub類的構造方法中

\

那麼現在就清楚了,如果客戶端和服務端是在一個進程中,那麼其實queryLocalInterface獲取的就是Stub對象,如果不在一個進程queryLocalInterface查詢的對象肯定為null,因為不同進程有不同虛擬機,肯定查不到mOwner對象的,所以這時候其實是返回的Proxy對象了。通過上面的講解之後,發現多進程服務通信基准就是借助Binder對象,先傳遞Binder對象,然後在把Binder轉成可以使用的原生對象即可調用了,而對於Stub類和Proxy類其實就是相當於是服務端和客戶端的中間者,把一些邏輯封裝起來,這種設計也會顯得不是那麼凌亂:

Stub類是服務端的中間者,一般是實現了AIDL接口類型和繼承了Binder類,具備將Binder對象轉化成原生對象的能力

Proxy類是客戶端的中間者,一般是實現了AIDL接口類型

\

 

四、分析系統服務調用流程

這種圖就夠形象了,所以後面會看到系統中的一些服務使用的時候其實也是跨進程使用,比如下面來看一下著名的PackageManager,IPackageManager,PackageManagerService體系:

\

這個就是IPackageManager.aidl文件,因為我們還沒有編譯源碼,所以這裡可能需要AIDL工具單獨編譯才能看到IPackageManager.java了:

\

這裡看到了熟悉的遠端服務中間者Stub和本地端的中間者Proxy類了,而這兩個類的規則都和上面一樣的。

下面來看一下遠端服務實現代碼PackageManagerService.java:

\

實現了上面的的Stub類功能。

下面我們再走一遍獲取PackageManager的流程:

\

而這個getPackageManager方法是在ContextImpl.java中實現的:

\

看到了吧,這裡先從ServiceManager的getService中獲取到一個PackageManager遠端的IBinder對象,然後在使用Stub的asInterface方法進行轉化成本地的PackageManager對象,其實就是那個Proxy對象。然後就可以通過PackageManager來調用方法和遠端的PackageManagerService服務進行通信了。

 

通過上面的PackageManager案例可以分析,我們在使用系統中的服務的時候的流程都是如此:

\

每個應用在使用系統服務的時候,都會走這麼幾步:

1、調用getSystemService(String serviceName)方法獲取服務對象

2、而getSystemService一般都是在ContextImpl類中實現的,其實是調用了ServiceManager的getService方法

3、調用ServiceManager的getService方法獲取遠端服務的IBinder對象

4、有了遠端服務的IBinder對象之後,在使用遠端服務的中間者類Stub進行轉化對象asInterface方法

5、因為系統中的服務獲取都是肯定是跨進程的,遠端服務都是在system_server進程中的,所以asInterface方法中返回的是Proxy代理對象,也就是本地端的中間者。

6、最後返回的對象其實就是這個Proxy對象,而這個對象內部使用了靜態代理方式,內部有一個來自遠端的mRemote變量即IBinder對象。然後直接調用方法其實就是調用mRemote的transact方法進行通信了。

所以在這個過程中可以看到有兩個對象很重要,一個是ServiceManager,一個是IBinder對象。下面再來一一介紹

 

五、服務大管家ServiceManager

那麼現在又有幾個問題了,這個ServiceManager是干嘛的?如何能夠通過服務描述符就可以獲取到對應服務的IBinder對象?這個就要看一下ServiceManager的功能了:

首先看看他獲取遠端服務的IBinder對象方法,他本身會維護一個IBinder緩存池,也是為了效率高考慮,對於一個應用頻繁的使用一些服務的話效率就會高很多。

\

然後最核心的獲取服務的方法是getIServiceManager方法:

\

這個方法看到了,又是一個遠程通信獲取Binder對象,而這次是IServiceManager對象了:

\

看到這裡的ServiceManager也是通過遠端服務獲取到他的IBinder對象,然後在轉化成本地對象進行使用的。那麼剛剛看到系統的服務都是通過ServiceManager管理獲取的,而現在ServiceManager本身是怎麼獲取到的IBinder對象的呢?這個就要從系統啟動的時機看了,眾所周知系統啟動的時候是根據init.rc文件進行操作的:

\

這裡會啟動一個servicemanager服務,那麼就要去service_manager.c程序中的入口程序看了:

\

這個入口其實包含了Binder機制的重要信息,而主要就是三件事:

1、打開底層的Binder驅動程序,這個後面介紹Binder機制在介紹

2、通過向binder程序發送命令:BINDER_SET_CONTEXT_MGR,告訴binder程序,我要成為大管家

3、進入循環監聽上層應用的服務請求處理,所以這裡可以看到其實ServiceManager是一個守護進程在後台默默監聽

在第二步中成為大管家的代碼深入看一看:

\

其實這裡的邏輯也是比較簡單的,首先創建一個屬於servicemanager的binder節點,然後在創建一個binder鏈表,而這個鏈表的作用就是存放上層中需要系統服務的所有binder對象的節點,這樣ServiceManager就可以實現了服務的增加和查詢操作了。

再來看看ServiceManager的添加服務操作:

\

添加服務比較復雜,首先查看這個服務有沒有注冊權限限制,不是所有的服務都能注冊的,然後在查看這個服務是不是已經被注冊過了,最後在通知binder驅動程序注冊一個服務即可。

然後在來看看ServiceManager的查找服務功能:

\

查找服務就比較簡單了,直接通過服務的描述符名稱遍歷binder鏈表節點即可。

1、Service Manager能集中管理系統內的所有服務,它能被施加權限控制,並不是任何進程都能注冊服務的。
2、Service Manager支持通過字符串名稱來查找對應的Service。
3、由於各種原因的影響,Server進程可能生死無常。如果有了Service Manager做統一的管理,那麼Client只要向Service Manager做查詢,就能得到Server的最新信息。

 

六、系統服務注冊流程分析

下面來看一下一些系統服務是如何進程注冊的,這裡用MediaService來進行查看吧:

系統中的MediaService服務的啟動也是在init.rc中的

\

查看main_mediaserver.cpp源碼的main函數:

\

看到了系統的MediaServer依賴很多其他服務的,而這些服務必須要注冊的,他們的注冊操作都是在各自的初始化方法中的,這裡用MediaPlayerService來看看注冊操作:

\

看到熟悉的代碼了把,這裡通過ServiceManager來進行服務注冊了,那麼這裡是如何獲取到ServiceManager的?

\

看看ProcessState.cpp的源碼:

\

看看getStrongProxyForHandle方法實現:

\

這裡看到了,會使用IPCThreadState的transact方法和底層的Binder進行通信的,然後使用一個句柄handle構造一個BpBinder對象,而BpBinder對象其實就是native層實現的Binder對象,以後只要看到Bp開頭的就是代理對象對應Java層的Proxy對象,Bn開頭的就是native對象對應Java層的Stub對象。

在上面分析servicemanager的時候知道會維護一個binder節點鏈表,那裡其實就有一個每個binder對應句柄handle,而後續進行通信的話都是通過這個句柄來標識是哪個服務的binder對象了,這樣也就在通信的時候不會發生紊亂了,而servicemanager的句柄handle就是0。還有一個知識點就是可以看到IPC通信的時候傳輸數據使用的就是Parcel類,這個類就是為了跨進程通信產生的,他有一個方法readStrongBinder,就是可以從Parcel的數據中獲取到Binder對象,這個也是在跨進程中傳遞Binder對象的核心地方。

\

好了,上面就通過系統的mediaserver服務來講解了系統服務的注冊流程:

\

 

到這裡就分析完了Android中的遠程服務調用機制邏輯以及ServiceManager這個服務大管家的作用:

\

1、首先跨進程通信的話,肯定會有兩個對象:一個是本地端的中間者Proxy對象,一個是遠程端的中間者Stub對象

2、Proxy對象通過靜態代理模式維持一個遠端傳遞過來的Binder對象,而Stub對象可以把遠端傳遞過來的Binder對象轉化成一個實際服務對象給應用使用

3、Android中在使用系統服務的時候通過getSystemService方法獲取到的其實都是Stub把遠端的Binder轉化的對象,因為系統服務都是在system_server進程中,所以肯定是跨進程獲取對象的,那麼這個Binder對象其實就是上面的Proxy對象

4、系統的服務都是在一個指定的系統進程中system_server

5、服務大管家ServiceManager在系統啟動的時候也是先獲取自生的Binder對象,然後轉化成實際操作對象,然後才可以操作系統服務的注冊和查詢功能

下面是系統一些服務的注冊流程:

\

 

 

上面已經介紹了遠程五福調用機制以及ServiceManager的實現原理,下面就要看看另外一個重點,也是上面提到的一個重要對象Binder,准確來說這個是Binder機制,在Android中Binder機制最復雜的一個架構系統了,它的設計很復雜,所以有很多同學在了解Binder機制的時候,總是看著看著就暈了,今天我們就直說重點,而且說得要相對明了簡單。

 

七、Binder機制解析

第一、Android中的IPC為何要采用Binder機制

Binder是Android系統進程間通信(IPC)方式之一。Linux已經擁有的進程間通信IPC手段包括(Internet Process Connection):管道(Pipe)、信號(Signal)和跟蹤(Trace)、插口(Socket)、報文隊列(Message)、共享內存(Share Memory)和信號量(Semaphore)。

Binder基於Client-Server通信模式,傳輸過程只需一次拷貝,為發送發添加UID,PID身份,既支持實名Binder也支持匿名Binder,安全性高。對Binder而言,Binder可以看成Server提供的實現某個特定服務的訪問接入點, Client通過這個‘地址’向Server發送請求來使用該服務;對Client而言,Binder可以看成是通向Server的管道入口,要想和某個Server通信首先必須建立這個管道並獲得管道入口。

 

第二、Android中的Binder實現原理

其實Android中的Binder通信都是通過虛擬驅動設備程序/dev/binder來實現的,我們知道一些硬件都會對應一個驅動程序,而binder驅動程序沒有對應的硬件,所以叫做虛擬驅動設備程序,其實他就是一個字符驅動設備,或者叫做miscdevice混雜設備驅動。

其實混雜驅動設備是字符設備的一種,它們共享一個主設備號(10),但次設備號不同,所有的混雜設備形成一個鏈表,對設備訪問時內核根據次設備號查找到相應的miscdevice設備。例如:觸摸屏,LED,按鍵,串口。即:為了節約主設備號,將某些設備用鏈表的形式連接在一起,最後通過查找次設備區分。這裡用主設備無法匹配出設備驅動,只能找到鏈表,再通過次設備號,才能找到設備驅動。而之前所學的,一般字符設備,通過主設備號,就能找到設備驅動了。我們可以通過命令查看/dev/binder驅動的主設備號:

\

 

第三、Android中Binder通信機制

先來看一張圖,我們可以大體的了解到了客戶端和服務端通過Binder驅動進行通信

\

首先不管是客戶端進程還是服務端進程都是在用戶空間的,而binder驅動是在內核空間的,通信的數據是有規定格式也叫作IPC數據,既然是一種通信機制,肯定是需要協議,數據格式等基礎結構信息的:

\

上面在分析了ServiceManager的啟動的時候說到了,第一步是打開驅動程序,具體打開函數在binder.c中:

\

在使用一個驅動之前,肯定要先打開驅動,然後把驅動程序映射到內存中,接著借助IPCTreadState.cpp和binder驅動進行通信了:

\

所以看到這裡IPCThreadState也是需要進入後台進行監聽的,處理來自客戶端和服務端的數據傳輸消息

\

最後再來看一下通信時序圖。

 

到這裡我們就介紹完了Binder機制了,關於Binder機制最好不要看太深,因為越深你覺得越復雜越難理解,其實你只要了解到他是一個通信工具,通信采用的是驅動操作,通過傳輸IPC數據來進行通信即可。其他的關於他的詳細數據格式和通信協議,感興趣的同學可以了解一下,但是太過復雜而且在實際中也沒多大用途,所以這裡就不介紹了。

 

八、技術點概要

1、理解遠程服務通信機制

通過案例先了解到本地端和服務端跨進程通信,主要就是借助Binder進行功能調用,而在這裡主要有兩個核心類,一個是Stub類,這個類是繼承了Binder類具備了將遠程傳遞的Binder對象轉化成本地實際對象asInterface方法即可,同時實現了IXXX接口,需要實現AIDL中的功能方法,還有一個類就是Proxy類,實現了IXXX接口,同時內部保留著遠端傳遞的Binder對象,然後通過這個對象調用遠端方法。這裡Stub類就是服務端的中間者,而Proxy就是本地端的中間者。

2、系統服務調用流程

通過分析了跨進程通信機制原理之後,再去看看Android系統中在使用一些服務的時候,通過getSystemService方法獲取服務對象,其實這內部就是通過跨進程獲取到了遠端服務的Binder對象,然後轉化成系統服務對象給應用調用,而這些系統服務的Binder對象在系統啟動的時候服務會自動注冊到ServiceManager中。

3、服務大管家ServiceManager

在整個遠程服務調用過程中兩個重要對象,一個是Binder對象,一個就是ServiceManager類,這個類是管理系統服務的類,他可以注冊服務,查詢服務,系統服務在系統啟動的時候會通過addService進行服務注冊,然後應用就可以通過getService進行服務查詢,而在這個過程中,底層會維護一個這些服務的binder鏈表結構,同時每個服務的binder對象都一個句柄handle,通過這個句柄來表示通信標識,這樣通信才不會紊亂。

4、底層通信核心Binder

最後分析了底層真正實現跨進程通信的機制Binder,其實是通過虛擬驅動程序/dev/binder進行通信的。一個通信機制肯定有通信協議,傳輸的數據結構,但是這裡並沒有介紹這些知識,原因是我們後面的需求並不會用到這些,其次是這些知識點太詳細介紹也不好,因為會越看越亂。

 

九、總結

本文介紹的東東有點多,但是如果掌握了Android中的Binder機制和遠程服務調用機制對後面攔截系統api做了鋪墊,說到結束了才告訴大家為什麼要介紹這個知識點,是因為最近在研究如何攔截系統啟動Activity的事,那麼就必須了解Activity的啟動流程,但是在這個過程中有一個對象就是ActivityManagerService,而他就和Binder以及遠程服務調用機制緊密聯系了,如果不了解Binder機制,後面工作是沒辦法進行的,好了,說到最後再來一張神圖算是總結了本文內容:

\

這張圖非常好的表達了Android中應用使用系統服務的一個流程,也是最好的最全的解釋了。看懂這張圖之後,那麼對Android中的binder機制和遠程服務調用機制就可以掌握了,可以進行後續的攔截操作了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved