Android2.3 Sip簡單分析

sip在sdk中有三個目錄:server、net、telephony;

以下是針對net目錄下Sip的分析:

net目錄包含九個目標文件(SimpleSessionDescription、SipAudioCall、SipErrorCode、SipException、SipManager、SipProfile、SipRegistrationListener、SipSession、SipSessionAdapter)。

[cpp]  

public class SipErrorCode 該類定義瞭Sip錯誤代碼,定義如下:  

    /** Not an error. 未定義錯誤 */  

    public static final int NO_ERROR = 0;  

  

    /** When some socket error occurs. 當一些發生套接字錯誤。 */  

    public static final int SOCKET_ERROR = -1;  

  

    /** When server responds with an error. 當服務器響應一個錯誤。*/  

    public static final int SERVER_ERROR = -2;  

  

    /** When transaction is terminated unexpectedly. 當交易被意外終止。*/  

    public static final int TRANSACTION_TERMINTED = -3;  

  

    /** When some error occurs on the device, possibly due to a bug. 當一些錯誤發生在設備上,這可能是由於一個錯誤。*/  

    public static final int CLIENT_ERROR = -4;  

  

    /** When the transaction gets timed out. 當事務被超時。*/  

    public static final int TIME_OUT = -5;  

  

    /** When the remote URI is not valid. 當遠程URI是無效的。*/  

    public static final int INVALID_REMOTE_URI = -6;  

  

    /** When the peer is not reachable. 當對端不可達。*/  

    public static final int PEER_NOT_REACHABLE = -7;  

  

    /** When invalid credentials are provided. 當無效的憑證。*/  

    public static final int INVALID_CREDENTIALS = -8;  

  

    /** The client is in a transaction and cannot initiate a new one. 客戶在交易中不能啟動一個新的。*/  

    public static final int IN_PROGRESS = -9;  

  

    /** When data connection is lost. 當數據連接丟失。*/  

    public static final int DATA_CONNECTION_LOST = -10;  

  

    /** Cross-domain authentication required. 跨域認證。*/  

    public static final int CROSS_DOMAIN_AUTHENTICATION = -11;  

  

    /** When the server is not reachable. 當服務器不可達。*/  

    public static final int SERVER_UNREACHABLE = -12;  

  

  

public interface SipRegistrationListener SIP註冊事件監聽器  

該類包含瞭三個回調函數:  

void onRegistering(String localProfileUri)  發送的註冊請求時調用  

void onRegistrationDone(String localProfileUri, long expiryTime)  註冊成功時調用  

onRegistrationFailed(String localProfileUri, int errorCode, String errorMessage) 註冊失敗時調用  

  

  

public class SipException extends Exception  表示一般的SIP相關的異常。  

  

public class SipSessionAdapter extends ISipSessionListener.Stub   會話適配器類 會話監聽  

該類定義瞭一些通話事件,如下:  

    public void onCalling(ISipSession session) { // 正通話中  

    }  

  

    public void onRinging(ISipSession session, SipProfile caller, String sessionDescription) { // 響鈴  

    }  

  

    public void onRingingBack(ISipSession session) {  

    }  

  

    public void onCallEstablished(ISipSession session, String sessionDescription) { //建立通話  

    }  

  

    public void onCallEnded(ISipSession session) {  //通話結束  

    }  

  

    public void onCallBusy(ISipSession session) {   //通話忙  

    }  

  

    public void onCallTransferring(ISipSession session, String sessionDescription) {    //呼叫轉移  

    }  

  

    public void onCallChangeFailed(ISipSession session, int errorCode, String message) { //打電話失敗  

    }  

  

    public void onError(ISipSession session, int errorCode, String message) {  //錯誤  

    }  

  

    public void onRegistering(ISipSession session) {    //正在註冊  

    }  

  

    public void onRegistrationDone(ISipSession session, int duration) { //註冊完成  

    }  

  

    public void onRegistrationFailed(ISipSession session, int errorCode, String message) { //註冊失敗  

    }  

  

    public void onRegistrationTimeout(ISipSession session) { //註冊超時  

    }  

  

  

public class SipProfile implements Parcelable, Serializable, Cloneable  定義一個SIP配置文件  

包括一個SIP帳戶、傳輸協議、端口、密碼、文件、id、域和服務器等的信息!  

  

public static class Builder  輔助類,輔助構造一個SIP配置文件  

  

public final class SipSession  Sip會話信息  

該類中定義瞭一個狀態類State(,如“註冊”,“呼出”,和“呼叫) 定義如下:  

 /** When session is ready to initiate a call or transaction. 當會話已準備好發起呼叫或交易。*/  

        public static final int READY_TO_CALL = 0;  

  

        /** When the registration request is sent out. 當註冊請求被發送出去。*/  

        public static final int REGISTERING = 1;  

  

        /** When the unregistration request is sent out. 當註銷請求發送出去。*/  

        public static final int DEREGISTERING = 2;  

  

        /** When an INVITE request is received. 當INVITE請求被接受。*/  

        public static final int INCOMING_CALL = 3;  

  

        /** When an OK response is sent for the INVITE request received.  

         * 當一個OK響應發送的INVITE請求。*/  

        public static final int INCOMING_CALL_ANSWERING = 4;  

  

        /** When an INVITE request is sent. 

         * 當INVITE請求被發送。 */  

        public static final int OUTGOING_CALL = 5;  

  

        /** When a RINGING response is received for the INVITE request sent. 

         * RINGING響應當接收到發送的INVITE請求。 */  

        public static final int OUTGOING_CALL_RING_BACK = 6;  

  

        /** When a CANCEL request is sent for the INVITE request sent. 

         * 當一個CANCEL請求被發送的INVITE請求發送。 */  

        public static final int OUTGOING_CALL_CANCELING = 7;  

  

        /** When a call is established. 當一個呼叫建立。*/  

        public static final int IN_CALL = 8;  

  

        /** When an OPTIONS request is sent. 當一個OPTIONS請求被發送。*/  

        public static final int PINGING = 9;  

  

        /** When ending a call. @hide 結束一個呼叫時*/  

        public static final int ENDING_CALL = 10;  

  

        /** Not defined. 沒有定義*/  

        public static final int NOT_DEFINED = 101;  

  

還定義瞭一個監聽類Listener 一個SIP會話有關的事件 如下:  

public void onCalling(SipSession session) 當INVITE請求被發送到發起新的呼叫調用  

public void onRinging(SipSession session, SipProfile caller,String sessionDescription) 收到INVITE請求時調用  

public void onRingingBack(SipSession session) RINGING收到響應發送的INVITE請求時,調用  

public void onCallEstablished(SipSession session,String sessionDescription) 在會話建立時調用  

public void onCallEnded(SipSession session)  在會話終止時調用  

public void onCallBusy(SipSession session)  所謂同行是忙時會話初始化過程中  

public void onCallTransferring(SipSession newSession,String sessionDescription) 呼叫時,呼叫被轉移到一個新的  

public void onError(SipSession session, int errorCode,String errorMessage) 會話初始化和終止過程中發生錯誤時調用  

public void onCallChangeFailed(SipSession session, int errorCode,String errorMessage) 更改會話協商過程中發生錯誤時調用  

public void onRegistering(SipSession session)  發送的註冊請求時調用  

public void onRegistrationDone(SipSession session, int duration) 註冊成功完成時調用  

public void onRegistrationFailed(SipSession session, int errorCode,String errorMessage) 註冊失敗時調用  

public void onRegistrationTimeout(SipSession session)  登記被超時時調用  

  

會話過程中,定義瞭一些回調如:  

private ISipSessionListener createListener()  

  

public class SimpleSessionDescription 使用的對象來處理消息的會話描述協議(SDP)。它主要是設計用於的會話發起協議(SIP)的用途。  

該類中還定義瞭Media 表示一個媒體描述,會話描述(類型、端口、協議、格式、解碼/編碼)!  

  

public class SipAudioCall 處理SIP的互聯網音頻呼叫  

  

public class SipManager 提供瞭API,SIP的任務,如啟動SIP連接,並提供相關的SIP服務的訪問。 如下方法:  

public static boolean isApiSupported(Context context)  系統是否支持SIP API  

public static boolean isVoipSupported(Context context)  系統是否支持基於SIP協議的VOIP API  

public static boolean isSipWifiOnly(Context context)   SIP是否僅適用於WIFI  

public SipAudioCall makeAudioCall(SipProfile localProfile, SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)  創建一個撥打  

  

電話  

public SipAudioCall takeAudioCall(Intent incomingCallIntent,SipAudioCall.Listener listener)  創建一個接聽來電  

public static boolean isIncomingCallIntent(Intent intent)   如果目的是檢查呼入廣播意圖  

public static String getOfferSessionDescription(Intent incomingCallIntent)  從指定的來電廣播意圖獲取提供會話描述  

public void register(SipProfile localProfile, int expiryTime,SipRegistrationListener listener) 手動註冊的配置文件,相應的SIP提供商接聽電話。  

public void unregister(SipProfile localProfile,SipRegistrationListener listener)  手動註銷相應的SIP為進一步呼籲停止接收供應商的檔案,這可能會  

  

幹擾自動註冊的SIP服務​​的過程中,如果在配置文件中啟用瞭自動註冊選項。  

內部含有:private static class ListenerRelay extends SipSessionAdapter 類負責監聽註冊過程的回調!  

 

以上是針對net目錄下Sip的分析,好瞭我們轉到server目錄下Sip的分析。server目錄下有六個目標文件(SipHelper、SipService、SipSessionGroup、SipSessionListenerProxy、SipWakeLock、SipWakeupTimer)。

[cpp]  

class SipHelper  SIP協議棧相關的類和各種低級別的SIP任務,如發送消息的Helper類,產生會話需要的消息頭/體的各種信息,定義如下:  

private FromHeader createFromHeader(SipProfile profile, String tag) //構造來自源頭信息  

private ToHeader createToHeader(SipProfile profile) throws ParseException { //構造去向源信息  

private ToHeader createToHeader(SipProfile profile, String tag) //構造去向源信息  

private CallIdHeader createCallIdHeader() {//構造會話id信息  

private CSeqHeader createCSeqHeader(String method) //構造會話CSeq信息  

private MaxForwardsHeader createMaxForwardsHeader() //構造會話MaxForwards信息  

private List<ViaHeader> createViaHeaders()    //構造通道信息  

private ContactHeader createContactHeader(SipProfile profile) //構造聯系頭  

private ContactHeader createWildcardContactHeader() { //創建通配符  

private SipURI createSipUri(String username, String transport,  

            ListeningPoint lp) throws ParseException { //構造sip地址  

public ClientTransaction sendOptions(SipProfile caller, SipProfile callee,  

            String tag) throws SipException {  //發送選擇  

public ClientTransaction sendRegister(SipProfile userProfile, String tag,  

            int expiry) throws SipException { //發送註冊  

private Request createRequest(String requestType, SipProfile userProfile,  

            String tag) throws ParseException, SipException { //創建請求  

public ClientTransaction handleChallenge(ResponseEvent responseEvent,  

            AccountManager accountManager) throws SipException { //處理請求  

private Request createRequest(String requestType, SipProfile caller,  

            SipProfile callee, String tag) throws ParseException, SipException {  //創建請求  

public ClientTransaction sendInvite(SipProfile caller, SipProfile callee,  

            String sessionDescription, String tag, ReferredByHeader referredBy,  

            String replaces) throws SipException {  //發送邀請  

public ClientTransaction sendReinvite(Dialog dialog,  

            String sessionDescription) throws SipException { //發送再次邀請  

public ServerTransaction getServerTransaction(RequestEvent event)  

            throws SipException { //得到服務器中交易信息  

public ServerTransaction sendRinging(RequestEvent event, String tag)  

            throws SipException { //發送響鈴  

public ServerTransaction sendInviteOk(RequestEvent event,  

            SipProfile localProfile, String sessionDescription,  

            ServerTransaction inviteTransaction, String externalIp,  

            int externalPort) throws SipException {  //邀請OK  

public void sendInviteBusyHere(RequestEvent event,  

            ServerTransaction inviteTransaction) throws SipException { //邀請忙  

public void sendInviteAck(ResponseEvent event, Dialog dialog)  

            throws SipException { //發送邀請ack  

public void sendReferNotify(Dialog dialog, String content)  

            throws SipException { //發送參閱通知  

public void sendInviteRequestTerminated(Request inviteRequest,  

            ServerTransaction inviteTransaction) throws SipException { //發送邀請終止  

  

public final class SipService extends ISipService.Stub  管理Sip的服務  

class SipSessionGroup implements SipListener  管理的一個SIP帳戶  

class SipSessionListenerProxy extends ISipSessionListener.Stub  以幫助安全地運行在不同的線程中的回調,實現會話過程中事件  

class SipWakeLock 定義瞭sip同步鎖   

class SipWakeupTimer extends BroadcastReceiver 定時器,它可安排事件的發生,甚至當設備處於睡眠。  

該類是繼承瞭廣播,類中還定義瞭  

private static class MyEvent  事件以及回調  

private static class MyEventComparator implements Comparator<MyEvent>   用於對準的事件與較大的期間,對事件排序  

以上是針對server目錄下Sip的分析,好瞭我們轉到telephony目錄下Sip的分析。telephony目錄下有六個目標文件(SipCallBase、SipCommandInterface、SipConnectionBase、SipPhone、SipPhoneBase、SipPhoneFactory)。

[cpp]  

public class SipPhoneFactory  sip電話生成廠  

abstract class SipPhoneBase extends PhoneBase 記錄/管理Sip電話狀態類  

public class SipPhone extends SipPhoneBase   同上  

abstract class SipConnectionBase extends Connection 記錄Sip電話時間等信息  

class SipCommandInterface extends BaseCommands implements CommandsInterface SIP不需要CommandsInterface的。類什麼也不做,但PhoneBase的構造函數  

  

所做的工作。  

abstract class SipCallBase extends Call 抽象類,記錄連接信息。  

 

以上是針對telephony目錄下Sip的分析。其它引用分析如下:

如下是SipPhone實現相關類圖。

SipPhone對象雖然也派生自PhoneBase,但實現機制及實例化過程與其它Phone對象大大不同。

SipPhone對象的實例化通過PhoneFactory的makeSipPhone的接口調用SipPhoneFactory的makePhone進行實例化。

SipPhone對象的實例化調用也不是在PhoneApp對象中進行,而是在默認電話應用的SipBroadcastReceiver對象的onReceive回調函數或者SipCallOptionHandler對象的createSipPhoneIfNeeded函數中被調用,另外不同的是對SipPhone對象的創建個數也沒有限制。

雖然實例化過程和CDMAPhone、GSMPhone對象不同,但實例化的SipPhone對象和CDMAPhone、GSMPhone對象一樣也被添加到CallManager對象中進行統一管理。

SipPhone實現機制是通過IP通道實現電話功能,而不是通過無線通訊模塊,因此沒有采用RIL和RIL daemon,因此框架實現層與CDMAPhone、GSMPhone有很大不同,SipPhone框架實現主要是通過SipService系統服務及調用第三方協議棧來完成。

SipService系統服務通過JNI來調用本地nist SIP協議堆棧。

SipPhone對象屬於SipService服務的客戶端對象,其通過SipManager對象的ISipService接口調用SipService。

每個SipPhone對象都對應一個SipProfile對象(包括SIP帳戶,地址連接信息以及服務信息等信息)用來標識通話一方。

SipProfile對象在SipPhoneFactory調用makePhone實例化SipPhone對象前根據網絡通話的sipUri構建,並作為makePhone的參數傳給SipPhone對象。

每個SipPhone對象和其它具體Phone對象相同也包括三個Call對象(SipCall):ringingCall、foregroundCall、backgroundCall,每個SipCall對象也包含一個Connection類型的ArrayList對象,用來維護每個CALL擁有的通話連接。

不過和其它Phone對象不同SipPhone對象對每個Call對象擁有的connection個數沒有限制,SipCall擁有的connection對象對應具體的SipConnection對象。

SipConnection對象包括主動和被動兩種類型,主動類型的SipConnection對象在SipCall對象的dial函數調用時創建,被動類型的SipConnection對象在SipCall對象接收到輸入CALL時,其initIncomingCall函數調用時創建。

創建的SipConnection對象添加到SipCall對象的Connection類型的ArrayList數組列表中進行管理。

每個SipConnection對象也包括一個SipProfile對象和一個SipAudioCall對象。

被動SipConnection類型的SipProfile對象和SipAudioCall對象從對方獲得。

被動類型的SipAudioCall對象和initIncomingCall函數傳進來的SipAudioCall對象相同,SipProfile對象通過調用SipAudioCall的getPeerProfile函數獲得發起通話的對方的SipProfile。

主動SipConnection類型的SipProfile對象在dial函數中調用SipProfile.Builder對象的build函數根據發起的通話URL構建,用來標識本地通話方,SipAudioCall對象在調用dial函數時通過調用SipManager對象的makeAudioCall函數創建。

SipAudioCall對象中包括一個客戶端SipSession對象,管理客戶端的每一個會話過程,SipSession對象中有一個ISipSession接口成員,通過該接口調用服務端對應的會話對象SipSessionImpl對象(一個實現ISipSession接口的會話樁對象),共同完成通話一方的會話過程,客戶端通過ISipSession接口向服務端的SipSessionImpl對象發起IPC調用。

主動類型的SipAudioCall對象對應的SipSession對象在makeAudioCall函數中調用createSipSession進行實例化,在makeAudioCall函數調用新創建SipAudioCall對象的makeCall函數時,SipSession對象作為makeCall函數的參數傳給SipAudioCall對象。

主動類型的SipSession對象中的ISipSession類型的接口成員在createSipSession中通過調用SipService的createSession函數創建,返回服務端SipSessionImpl對象的遠程調用對象接口。

SipService的createSession函數創建一個Sip會話對象的整個過程:

1、首先調用createGroup函數實例化一個SipSessionGroupExt對象,SipSessionGroupExt對象派生自SipSessionAdapter,而SipSessionAdapter是一個實現ISipSessionListener接口的樁類;

2、SipSessionGroupExt對象實例化時又調用createSipSessionGroup函數實例化一個SipSessionGroup對象;

3、SipSessionGroup對象實例化時由SipFactory工廠對象實例化底層協議棧類對象SipStack,並有SipStack對象創建一個SipProvider對象和一個SipHelper對象,SipSessionGroup對象本身登記為SipProvider對象的監聽對象;

4、然後調用SipSessionGroupExt對象的createSession函數,內部實際調用SipSessionGroup對象的createSession函數,實例化SipSessionImpl對象,並返回實例化後的SipSessionImpl對象引用。

5、SipSessionImpl屬於SipSessionGroup類的內部類,客戶端發起的通話請求都通過服務端的SipSessionImpl對象封裝成EventObject類型的命令發起異步處理請求(調用SipSessionImpl對象的doCommandAsync函數,doCommandAsync函數的參數是一個EventObject類型的對象),最終通過SipSessionGroup對象的SipHelper對象調用SIP協議棧的接口與通話對方交互。

6、每個SipSessionImpl會話對象被放到SipSessionGroup對象的HashMap中進行管理。每個實例化的SipSessionGroupExt對象放到SipService的HashMap中進行管理。

 

客戶端也可以通過SipManager對象的open函數發起一個被動會話請求,過程為:

1、 SipManager對象的open函數調用SipService的open3函數,同時也實例化一個PendingIntent對象通過SipService的open3函數傳給服務端的SipSessionGroupExt對象中;

2、 在open3函數中調用createGroup函數實例化SipSessionGroupExt對象,並進一步實例化SipSessionGroup對象;

3、 接著調用SipSessionGroup對象的openToReceiveCalls函數來實例化一個SipSessionCallReceiverImpl對象作為接收會話,SipSessionCallReceiverImpl派生自SipSessionImpl,SipSessionGroupExt對象作為函數參數傳進openToReceiveCalls函數,並在SipSessionImpl構造函數時調用內部對象成員mProxy(SipSessionListenerProxy類,一個實現ISipSessionListener接口的樁類)的setListener函數,為內部ISipSessionListener類型的成員賦值。

4、 當接收到底層協議發來的會話請求時,監聽底層事件的對象SipSessionGroup的processRequest函數被調用;

5、 接著調用接收會話對象SipSessionCallReceiverImpl的process函數,在process函數中判斷接收到的事件請求是Request.INVITE時就調用processNewInviteRequest函數;

6、 在processNewInviteRequest函數中調用createNewSession實例化一個SipSessionImpl對象作為新的會話,新的會話狀態為INCOMING_CALL。接著調用接收會話SipSessionCallReceiverImpl對象內部成員mProxy的onRinging函數,在onRinging函數中調用其成員mListener(ISipSessionListener類型)的onRinging函數,實際調用SipSessionGroupExt對象的onRinging函數;

7、 在SipSessionGroupExt對象的onRinging函數中通過SipSessionGroupExt對象內部客戶端傳進來的PendingIntent對象向客戶端發送ACTION_SIP_INCOMING_CALL類型廣播消息;

8、 客戶端在接收到這個廣播調用SipManager的takeAudioCall函數。takeAudioCall函數首先通過SipService接口getPendingSession獲得一個服務端會話對象引用;接著實例化一個SipAudioCall對象(SipAudioCall對象對應的SipProfile對象調用服務端會話對象的getLocalProfile函數獲得);然後根據服務端會話對象引用實例化一個客戶端SipSession對象;並調用SipAudioCall對象的attachCall實現SipSession對象與SipAudioCall對象的綁定;

9、 最後調用現有的SipPhone對象的canTake函數,在SipPhone對象的canTake函數中調用SipPhone對象的ringingCall對象的initIncomingCall函數實例化一個SipConnection對象,完成通話建立過程。

Telephony框架的數據連接模塊負責數據連接通道的建立,使電話能夠提供數據服務,如上網等,數據連接模塊的類圖如下圖。

 

DataConnectionTracker是數據連接功能的核心,GsmDataConnectionTracker、CdmaDataConnectionTracker是DataConnectionTracker的兩個派生類,實現具體網絡的數據連接的建立和管理。

每一個數據連接用一個DataConnection對象表示,DataConnection對應的具體網絡的派生類為GsmDataConnection和CdmaDataConnection。DataConnectionTracker中用一個HashMap類型的變量mDataConnections維護每一個數據連接。CDMA同時隻能建立一路數據連接,GSM網絡則沒有限制。

DataConnection對象是一個是一個狀態機對象,維護連接的狀態,並提供數據連接的LinkProperties和LinkCapabilities等屬性。DataConnection對象的狀態包括DcDefaultState、DcInactiveState、DcActivatingState、DcActiveState、DcDisconnectingState、DcDisconnectionErrorCreatingConnection六種狀態。

對於GsmDataConnectionTracker對象,ApnContext對象提供數據連接的APN上下文,每一個APN類型都對應一個ApnContext對象,ApnContext對象維護對應的APN設置、DataConnectionTracker的狀態、對應的DataConnection和DataConnectionAc等。

 

 

數據連接通道的建立過程:

1、 數據連接的建立最終都通過GsmDataConnectionTracker和CdmaDataConnectionTracker的trySetupData函數啟動數據連接;

2、 trySetupData函數調用setupData函數,設置數據連接參數(如采用的Apn設置參數,連接建立成功響應消息);

3、 然後調用對應的DataConnection對象的bringUp函數發送數據連接消息(EVENT_CONNECT)。

4、 EVENT_CONNECT消息由DataConnection對象的狀態機的相應狀態對象接收處理,對於開始尚未建立數據連接時,DataConnection對象處於DcInactiveState狀態,因此DcInactiveState狀態對象接收處理EVENT_CONNECT事件,調用onConnect函數,並轉變為DcActivatingState狀態。

5、 在具體DataConnection對象的onConnect函數中調用RIL接口setupDataCall函數啟動數據連接;

6、 連接建立後RIL層回應EVENT_SETUP_DATA_CONNECTION_DONE請求應答事件,由DataConnection對象的DcActivatingState狀態對象接收處理EVENT_SETUP_DATA_CONNECTION_DONE事件,調用onSetupConnectionCompleted函數,並過渡到DcActiveState狀態,連接建立成功。

DataConnectionTracker對象采用DataConnectionAc對象(派生自AsyncChannel)與DataConnection對象通訊。

smDataConnectionTracker對象在setupData函數調用createDataConnection函數創建時GsmDataConnection對象和DataConnectionAc對象,並通過DataConnectionAc對象與GsmDataConnection對象建立異步通訊連接。GsmDataConnectionTracker對象對於每類ApnContext都可以建立一個GsmDataConnection對象和一個DataConnectionAc對象。

CdmaDataConnectionTracker對象在實例化時調用createAllDataConnectionList函數創建需要的數據連接對象CdmaDataConnection和DataConnectionAc對象。CdmaDataConnectionTracker對象隻支持創建一個CdmaDataConnection對象和DataConnectionAc對象。

 

整個Telephony事件通知框架包括三層: RIL消息層、框架事件處理層、應用層。類圖如下圖,主要是請求應答模式和觀察者模式的采用。

 

整個框架層以PhoneBase為中心,向上通過PhoneNotifier接口向應用層發送框架層產生的的事件,應用層通過TelephonyRegistry接口提供對特定事件的監聽,由PhoneNotifier接口的默認實現DefaultPhoneNotifier通過TelephonyRegistry對象向應用層發送事件通知。

向下框架層通過CommandsInterface接口註冊Unsolicited事件(主動通知事件)及發起AT命令請求。框架層的事件處理對象包括CDMAPhone和GSMPhone兩個對象本身及其包含的SMSDispatcher、IccFileHandler、DataConnectionTracker、IccRecords類型的對象以及PhoneBase中的SmsStorageMonitor對象。

這些對象都是Handler對象,都能夠向RIL層註冊Unsolicited事件及發起AT命令請求。也能夠接收和處理RIL層產生的Unsolicited事件及AT命令的響應。

CDMAPhone和GSMPhone對象中的事件處理對象除瞭CallTracker及ServiceStateTracker對象外,其它都在PhoneBase中由基類實現。

向RIL層註冊的Unsolicited事件都登記添加到BaseCommands類中的RegistrantList類型的對象中或者設置為BaseCommands類中的Registrant類型的對象(根據設置函數的參數實例化具體類型的Registrant類型的對象)。

RIL層產生的Unsolicited事件通過在BaseCommands類中登記的RegistrantList對象或設置的Registrant對象向框架層的事件處理對象發送Unsolicited事件。

框架層的事件處理對象向RIL層發送的命令直接發送給RIL對象的相應函數,向RIL層發送的命令中都帶有一個Message類型的應答消息,RIL對象的相應函數把接收到的命令攜帶的參數封裝進RILRequest請求中發送給RIL的RILSender對象並marshall後通過LocalSocket發送給rild進程。

RIL的RILReceiver對象通過相同LocalSocket通道收到AT命令的響應unmarshall後調用processSolicited()函數把應答結果封裝進命令攜帶的應答消息中發送回框架層的事件處理對象。

RILSender對象和RILReceiver對象都實現瞭接口,在獨立線程運行。

 

官方提供的實例中有關sip的就一個(SipDemo),下面是對它的分析:

一、基本概念

1、VOIP基於SIP協議,SDK2.3包含一個SIP協議棧和框架API

2、VOIP位於android.net.sip包中,最重要的為SipManager類,可開發基於SIP的VOIP應用。使用時要包含android.permission.INTERNET和android.permission.USE_SIP權限

3、如果在market中顯示僅支持VOIP API幸好的手機的話,發佈時需要在androidManifest.xml中加入<uses_feature android:name = "android.software.sip" android:required = "true">和<uses_feature android:name = "android.software.sip.voip">

4、要支持SIP API

     (1)僅Android2.3或更高版本平臺支持

     (2)不是所有設備都提供SIP支持,確保你的APP隻安裝在支持SIP的裝置上

5、根據GOOGLE官方DEMO項目來擴展的概念

 

二、類及方法描述

1、一個基本的VOIP項目至少需要三個類SIPSettings(對SIP的基本設置身份驗證)、WalkieTalkieActivity(登錄到SIP設備供應商,註冊device去處理來電,撥打電話,在通話過程中用戶界面管理)、IncomingCallReceiver(監聽傳入的SIP電話,然後傳遞這些SIP電話給WalkieTalkieActivity控制)

2、WalkieTalkieActivity

A、SipManager.newInstance()–>此方法中首先判斷context是否支持SIP API,若支持則new SipManager。SipManager構造函數中,實例化瞭一個ISIPService(運用的公式:

IBinder b =ServiceManager.getService(Context.SIP_SERVICE);  //獲取系統相應的服務

ISipService service = ISipService.Stub.asInterface(bIBinder);)

上面這兩句代碼其實是使用瞭AIDL,就以SipService為例,步驟如下

Service端

1、編寫aidl文件:ISipService.aidl,並定義使用的接口(就等同於interface一樣)

2、使用makefile生成與之同名的JAVA文件,SipService.java,此類繼承extends ISipService.Stub並實現接口定義的方法或者在SipService extends Service,並代碼中加入

ISipService.stub sipImpl = new ISipService.stub(){

     //實現其接口方法,在SipService.java中是實現瞭一個名為start()的方法,裡面有句是ServiceManager.addService("sip",newSipService(context));表示SipService已經交給ServiceManager統一管理瞭

}

Client端

一(以SIPService為例)

1、而在需要用到SipService時,也就是我們構造SipManager的時候,就通過ServiceManager.getService(Context.SIP_SERVICE)獲得SIP的服務(類型為IBinder)

2、並調用 ISipService.Stub.asInterface(IBinder);去獲取一個SipService實例(前提是該Service一定是通過ServiceManager.addService的方式添加進去管理的,這樣才能找到此Service)

二(以普通Activity為例)

1、利用Intent intent = new Intent(Activity.this,SipService.class);–>bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);來綁定SERVICE,在serviceConnection的onServiceConnected方法中,使用IService.stub.asIntentface(IBinder);來獲取實例。

B、SipManager創建好後,先從SharedPreference中獲取username,domain及pwd,如果第一次進來沒有設置這些的話則需要先創建賬戶,這裡用EditTextPreference來保存用戶信息,好處是當填寫信息並返回後,EditTextPreference會自動將值放入SharedPreference中。我們假設username="woody";domain="192.168.12.30";pwd="910913"。

 C、這時,我們的SipManager以及用戶信息已經設定好瞭,接下來使用瞭這句SipProfile.Builder builder = new SipProfile.Builder(username, domain);我們去看看SipProfile.Builder中做瞭些什麼:

SipURI mUri =mAddressFactory.createSipURI(username,serverDomain);

SipProfile mProfile.mDomain=serverDomain; //設置domain

(在mAddressFactory.createSipURl方法中,我選取瞭一些核心代碼)

StringBuffer uriString=new StringBuffer("sip:"); 

uriString.append(user); 

uriString.append("@"); 

//if host is an IPv6 string we should enclose it in sq brackets

if(host.indexOf(':') !=host.lastIndexOf(':')&&host.trim().charAt(0) !='[') 

   host='['+host+']'; 

uriString.append(host);

StringMsgParser smp=new StringMsgParser(); 

SipUrl sipUri=smp.parseSIPUrl(uriString.toString()); 

return sipUri;

從以上代碼可以看出其實就是在Format SipURL罷瞭,裡面多加瞭個if host為IPV6的判斷(IPv4為為32位,十進制;IPv6為128位,16進制)。urlString最後為"sip:woody@192.168.12.30",smp.parseSIPUrl()方法中,有關於是如何parse的就不做闡述瞭,總之最後返回瞭一個SipUri

 

D、接下來就是SipProfile  sipProfile = SipProfile.Builder.build(); //返回一個SipProfile object

在SipProfile.Builder.build()中,設置瞭sipProfile的pwd值,刪除瞭之前SipUrl對象裡的password(mUri.setUserPassword(null);)、將sipProfile的address屬性設置為AddressImpl類型的對象值、調用AddressFactory.createURI返回一個SipUri,並sipProfile.mProxyAddress=sipUri.getHost();

E、創建PendingIntent對象:(Intent與PendingIntent區別在於Intent是及時啟動,而PendingIntent是不立刻反應,在特定的情況或通知下才啟動,適用於AlertClock等)

Intent i = new Intent();

i.setAction("android.SipDemo.INCOMING_CALL");

PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);

F、SipManager.open(sipProfile,PendingIntent,null); //(實際是SIPService在做操作)設置localSIPProfile的callingID–>建立SIP連接(算是註冊至SIP Server)–>打開receiveCall

其中建立SIP連接,最後能追溯到是在SipSessionGroup.java的reset()方法中通過是註冊服務器實現的,

註冊服務器的步驟為:

(1)設置服務器的屬性,例如服務器的地址(IP_ADDRESS_PROP)、棧名(javax.sip.STACK_NAME)、發出去的路徑(localProfile中的javax.sip.OUTBOUND_PROXY)、線程池的大小(gov.nist.javax.sip.THREAD_POOL_SIZE)等,並且將這些屬性加載到服務器中.

(2)通過SipFactory的靜態方法取得一個實例,然後通過SipFactory實例sipfactory 

(3)創建一個SipStack實例sipstack(這一步獲得IP_ADDRESS_PROP,String address = Properties.getProperty("javax.sip.IP_ADDRESS");)

(4)用sipstack創建一個SipProvider實例sipProvider

(5)註冊SipListener

 

G、A~F步驟都是在做準備工作,大致的步驟如下:new SIPService–>new SIPManager–>設定用戶信息–>new SIPURI–>new SIPProfile–>new PendingIntent–>set sipProfile callingID–>(if profile.getAutoRegistation)open toReceiveCalls–>register SipService 現在是call someone~呼叫的工作是SipAudioCall類來完成(可用sipManager.makeAudioCall或takeAudioCall來實例化,SipAudioCall.startAudio時需要 RECORD_AUDIO, ACCESS_WIFI_STATE, and WAKE_LOCK permissions,setSpeakerMode() 時需要MODIFY_AUDIO_SETTINGS permission)

【1】當需要呼叫時,使用sipManager.makeAudioCall(String localProfileURI, String peerProfileURI, SipAudioCall.listener,int timeout);來創建一個SipAudioCall,其中timeout以seconds為單位,過瞭timeout表示打電話超時。需要打給別人時使用makeAudioCall創建,接聽電話用takeAudioCall來創建sipAudioCall

【2】SipAudioCall中有一個嵌套的class:SipAudioCall.Listener(此類主要用於監聽SIP CALL,when[呼叫電話 or 接聽電話])

SipAudioCall.Listener listener = new SipAudioCall.Listener() {

                  @Override

                  public void onCallEstablished(SipAudioCall call) { //呼叫建立

                      call.startAudio(); //啟動音頻

                      call.setSpeakerMode(true); //調整為可講話模式

                      call.toggleMute(); //觸發無聲

                      updateStatus(call);

                  }

};

SipAudioCall call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

(以上例子為makeAudioCall)

【3】我們看看makeAudioCall()方法(makeAudioCall requires 2 sipProfile):

SipAudioCall call =new SipAudioCall(mContext, localProfile);

call.setListener(listener);  //這兩句很簡單就是創建一個local的sipAudioCall

SipSession s = createSipSession(localProfile, null); –>mSipService.createSession(localProfile, null);//  sipService來創建session,並保存在SipSessionGroupExt中

call.makeCall(peerProfile,s,null); //這句就是呼叫,最後追溯到實際是SipSession.makecall

總結:在發起通話中

首先是創建SipAudioCall.listener,以便監聽對話建立和對話結束,然後做相應的操作,然後是SipManager.makeAudioCall(localAdd,llistener,XXXX),在makeAudioCall方法中

A、創建一個sipAudioCall(localProfile)

B、創建SipSession以建立起會話

C、SipSession.makeCall(peerProfile,XXXX); //SipSession呼叫遠程profile

【4】關於接電話道理都差不多,takeAudioCall通過之前設置的callingID來查找mSipService.getPendingSession(callId);來獲得SipSession。並創建SipAudioCall,然後attachCall就算接受電話瞭。

 

三、總結

1、VOIP服務位於android.net.sip包中,關鍵類為SipManager。需要用到的permission列表,其中後面三個為使用WIFI獲取IP地址所需要用到的權限:

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

<uses-permission android:name="android.permission.USE_SIP"/>

<uses-feature android:name = "android.hardware.sip.voip" android:required = "true"/>

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<uses-permission android:name="android.permission.WAKE_LOCK" />

<uses-permission android:name="android.permission.RECORD_AUDIO" />

You May Also Like