CSipSimple短信發送機制探究


CSipSimple是運行在android設備上的一個開源的sip協議應用程序,介紹就不多說了網上一堆,有興趣的童鞋可以研究下,項目地址為:http://code.google.com/p/csipsimple/ 

本文將就其中的短信發送機制進行大致分析。

項目中,短信發送利用了AIDL方法來實現。aidl是 Android Interface definition language的縮寫,它是一種android內部進程通信接口的描述語言,通過它來定義進程間的通信接口,完成IPC(Inter-Process Communication,進程間通信)。

  • 創建.aidl文件

ISipService.aidl內容如下:
 1 /**
 2  * Copyright (C) 2010-2012 Regis Montoya ( aka r3gis - www.r3gis.fr)
 3  * This file is part of CSipSimple.
 4  *
 5  *  CSipSimple is free software: you can redistribute it and/or modify
 6  *  it under the terms of the GNU General Public License as published by
 7  *  the Free Software Foundation, either version 3 of the License, or
 8  *  (at your option) any later version.
 9  *  If you own a pjsip commercial license you can also redistribute it
10  *  and/or modify it under the terms of the GNU Lesser General Public License
11  *  as an android library.
12  *
13  *  CSipSimple is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
20  * 
21  *  This file and this file only is also released under Apache license as an API file
22  */
23 package com.csipsimple.api;
24 import com.csipsimple.api.SipProfileState;
25 import com.csipsimple.api.SipCallSession;
26 import com.csipsimple.api.MediaState;
27 
28 interface ISipService{
29    ……
30    // SMS
31    void sendMessage(String msg, String toNumber, long accountId);
32    ……
33 }

ISipService.aidl中定義了包含sendMessage方法的接口ISipService。

  • 自動編譯生成java文件

eclipse中的ADT插件會自動在aidl文件中聲明的包名目錄下生成java文件,如下圖所示:

ISipService.java
 1 /*
 2  * This file is auto-generated.  DO NOT MODIFY.
 3  * Original file: C:\\Users\\wayne\\workspace\\SipHome\\src\\com\\ csipsimple\\api \\ISipService.aidl
 4  */
 5 package com.csipsimple.api;
 6 public interface ISipService extends android.os.IInterface
 7 {
 8 ……
 9 // SMS
10 
11 public void sendMessage(java.lang.String msg, java.lang.String toNumber, long accountId) throws android.os.RemoteException;
12 }

接下來就是實現ISipService.aidl中定義的接口,提供接口的實例供客戶端調用。這里以調用關系為主線,依次進行分析。

  • IPC實現

首先在項目中,發送短信調用方法

void com.csipsimple.api.ISipService.sendMessage(String msg, String toNumber, long accountId)
由此入手,我們結合代碼一層層來查看具體的調用。
目錄:src\com\csipsimple\ui\messages
MessageFragment.java
 1     // Service connection
 2     private ISipService service;
 3     private ServiceConnection connection = new ServiceConnection() {
 4         @Override
 5         public void onServiceConnected(ComponentName arg0, IBinder arg1) {
 6             service = ISipService.Stub.asInterface (arg1 );
 7         }
 8 
 9         @Override
10         public void onServiceDisconnected(ComponentName arg0 ) {
11             service = null;
12         }
13     };
14     
15     ...
16     
17     private void sendMessage() {
18         if (service != null) {
19             SipProfile acc = accountChooserButton.getSelectedAccount();
20             if (acc != null && acc .id != SipProfile.INVALID_ID) {
21                 try {
22                     String textToSend = bodyInput.getText(). toString();
23                     if(!TextUtils .isEmpty (textToSend )) {
24                      String userName = remoteFrom.substring( remoteFrom.indexOf(":" )+1, remoteFrom.indexOf( "@"));
25                      String suffix_userName = remoteFrom.substring( remoteFrom.indexOf("@" ), remoteFrom.length());
26                      
27                      String [] userNames = new String [5];
28                      userNames = userName .split (";");
29                       for(int i=0; i <userNames .length ; i ++){
30                          userNames [i ] = "sip:" + userNames [i ] + suffix_userName;
31                          
32                             service .sendMessage (textToSend , userNames[i], (int) acc.id);
33                       }
34                         bodyInput .getText ().clear ();
35                     }
36                 } catch (RemoteException e ) {
37                     Log .e (THIS_FILE , "Not able to send message");
38                 }
39             }
40         }
41     }
這里的調用需要先了解Service的機制,稍后下文會給出說明。
service.sendMessage (textToSend , userNames[ i], (int ) acc .id)方法調用了ISipService的方法,找到它的代碼如下:
目錄:src\com\csipsimple\service
2.服務端
SipService.java
 1 /**
 2  * 繼承 Service發布服務
 3  */
 4 public class SipService extends Service {
 5     ...
 6 
 7     // 為服務實現公共接口, Stub類繼承了Binder
 8     private final ISipService.Stub binder = new ISipService.Stub() {
 9        ...
10     
11        /**
12         * Send SMS using
13         */
14        @Override
15        public void sendMessage(final String message, final String callee, final long accountId) throws RemoteException {
16            SipService.this .enforceCallingOrSelfPermission (SipManager .PERMISSION_USE_SIP , null);
17            //We have to ensure service is properly started and not just binded
18            SipService.this .startService (new Intent(SipService. this, SipService .class));
19            
20            getExecutor().execute(new SipRunnable() {
21               @Override
22               protected void doRun() throws SameThreadException {
23                   Log .d (THIS_FILE , "will sms " + callee);
24                    if(pjService != null) {
25                      ToCall called = pjService.sendMessage( callee, message, accountId );
26                       if(called!=null ) {
27                          SipMessage msg = new SipMessage(SipMessage. SELF,
28                                 SipUri .getCanonicalSipContact (callee ), SipUri.getCanonicalSipContact(called.getCallee()),
29                                 message , "text/plain", System.currentTimeMillis(),
30                                 SipMessage .MESSAGE_TYPE_QUEUED , called .getCallee ());
31                          msg .setRead (true);
32                          getContentResolver ().insert (SipMessage .MESSAGE_URI , msg.getContentValues());
33                          Log .d (THIS_FILE , "Inserted "+msg. getTo());
34                       }else {
35                          SipService .this. notifyUserOfMessage( getString(R. string.invalid_sip_uri)+ " : "+ callee );
36                       }
37                    }else {
38                       SipService .this. notifyUserOfMessage( getString(R. string.connection_not_valid) );
39                    }
40               }
41            });
42        }
43        
44        ...
45     };
46     
47     ...
48     
49     
50     /**
51      * 返回一個實現了接口的類對象,給客戶端接收
52      */
53     @Override
54     public IBinder onBind(Intent intent) {
55 
56        String serviceName = intent.getAction();
57        Log.d(THIS_FILE, "Action is " + serviceName );
58        if (serviceName == null || serviceName.equalsIgnoreCase(SipManager.INTENT_SIP_SERVICE )) {
59            Log.d(THIS_FILE, "Service returned");
60            return binder ;
61        } else if (serviceName. equalsIgnoreCase(SipManager.INTENT_SIP_CONFIGURATION )) {
62            Log.d(THIS_FILE, "Conf returned");
63            return binderConfiguration ;
64        }
65        Log.d(THIS_FILE, "Default service (SipService) returned");
66        return binder;
67     }
68     
69     ...
70 }
上文說過,需要實現ISipService.aidl中定義的接口,來提供接口的實例供客戶端調用。要實現自己的接口,就從ISipService.Stub類繼承,然后實現相關的方法。
Stub類繼承了Binder,因此它的對象就可以被遠程的進程調用了。如果Service中有對象繼承了Stub類,那么這個對象中的方法就可以在Activity等地方中使用,也就是說此時sendMessage就可以被其他Activity訪問調用了。
現在我們通過onBind(Intent intent)方法得到了可供客戶端接收的IBinder對象,就可以回頭看看剛才MessageFragment.java文件中的調用情況了。
2.客戶(調用)端
為方便查看,我們貼一段MessageFragment.java代碼
MessageFragment.java
 1     // Service connection
 2     private ISipService service;
 3     private ServiceConnection connection = new ServiceConnection() {
 4         @Override
 5         public void onServiceConnected(ComponentName arg0, IBinder arg1) {
 6             service = ISipService.Stub.asInterface (arg1 );
 7         }
 8 
 9         @Override
10         public void onServiceDisconnected(ComponentName arg0 ) {
11             service = null;
12         }
13     };
14     
15     ...
16     
17     private void sendMessage() {
18            ...
19            service .sendMessage (textToSend , userNames[i], (int) acc.id);
20            ...
21     }
可以看到,在客戶端(此處也就是調用遠程服務的Activity) 實現ServiceConnection,在ServiceConnection.onServiceConnected()方法中會接收到IBinder對象,調用ISipService.Stub.asInterface((IBinder)service)將返回值轉換為ISipService類型。
語句service.sendMessage (textToSend , userNames[ i], (int ) acc .id );調用接口中的方法,完成IPC方法。
  • 調用關系

接下來繼續回到sendMessage方法的調用關系分析,希望整理得不至太過混亂。
回到剛才的服務端實現,在繼承Service發布服務的代碼中,調用了pjService.sendMessage( callee, message, accountId )方法。
先看看這部分代碼:
目錄:src\com\csipsimple\pjsip
PjSipService.java
 1     /**
 2      * Send sms/message using SIP server
 3      */
 4     public ToCall sendMessage(String callee, String message , long accountId )
 5             throws SameThreadException {
 6         if (!created) {
 7             return null;
 8         }
 9 
10         ToCall toCall = sanitizeSipUri (callee , accountId );
11         if (toCall != null) {
12 
13             pj_str_t uri = pjsua.pj_str_copy( toCall.getCallee());
14             pj_str_t text = pjsua.pj_str_copy (message );
15             /*
16              * Log.d(THIS_FILE, "get for outgoing"); int finalAccountId =
17              * accountId; if (accountId == -1) { finalAccountId =
18              * pjsua.acc_find_for_outgoing(uri); }
19              */
20             // Nothing to do with this values
21             byte[] userData = new byte[ 1];
22 
23             int status = pjsua.im_send( toCall.getPjsipAccountId(), uri, null, text, null, userData);
24             return (status == pjsuaConstants .PJ_SUCCESS ) ? toCall : null ;
25         }
26         return toCall;
27     }

 

由紅色部分的語句,我們找到pjsua類。
目錄:src\org\pjsip\pjsua
pjsua.java
 1 /* ----------------------------------------------------------------------------
 2  * This file was automatically generated by SWIG (http://www.swig.org).
 3  * Version 2.0.4
 4  *
 5  * Do not make changes to this file unless you know what you are doing--modify
 6  * the SWIG interface file instead.
 7  * ----------------------------------------------------------------------------- */
 8 
 9 package org.pjsip.pjsua;
10 
11 public class pjsua implements pjsuaConstants {
12     
13     ...
14     
15     public synchronized static int im_send(int acc_id, pj_str_t to, pj_str_t mime_type, pj_str_t content, pjsua_msg_data msg_data, byte [] user_data ) {
16     return pjsuaJNI.im_send(acc_id, pj_str_t.getCPtr(to), to, pj_str_t.getCPtr( mime_type), mime_type, pj_str_t.getCPtr( content), content, pjsua_msg_data.getCPtr( msg_data), msg_data, user_data );
17     }
18     
19     ...
20 }
這時候大家可以發現,代碼調用來到了org.pjsip.pjsua包部分。這部分代碼在項目中是沒有的,它是通過編譯,同庫文件libpjsipjni.so一起生成的。
CSipSimple編譯后生成libs和pjsua兩個目錄,關於編譯的方法網上資料很多了就不贅述了。
繼續看調用,找到pjsuaJNI文件。
目錄:src\org\pjsip\pjsua
pjsuaJNI.java
 1 /* ----------------------------------------------------------------------------
 2  * This file was automatically generated by SWIG (http://www.swig.org).
 3  * Version 2.0.4
 4  *
 5  * Do not make changes to this file unless you know what you are doing--modify
 6  * the SWIG interface file instead.
 7  * ----------------------------------------------------------------------------- */
 8 
 9 package org.pjsip.pjsua;
10 
11 public class pjsuaJNI {
12 
13     ...
14     
15     public final static native int im_send(int jarg1, long jarg2, pj_str_t jarg2_, long jarg3, pj_str_t jarg3_ , long jarg4, pj_str_t jarg4_, long jarg5, pjsua_msg_data jarg5_ , byte[] jarg6);
16     
17     ...
18     
19 }

 我們看到了native方法im_send(),它調用的是封裝在庫libpjsipjni.so中的函數pjsua_im_send,進一步可以在jni目錄下找到C代碼。

目錄:jni\pjsip\sources\pjsip\src\pjsua-lib
pjsua_im.c
 1 /*
 2  * Send instant messaging outside dialog, using the specified account for
 3  * route set and authentication.
 4  */
 5 PJ_DEF( pj_status_t) pjsua_im_send( pjsua_acc_id acc_id ,
 6                 const pj_str_t *to ,
 7                 const pj_str_t *mime_type ,
 8                 const pj_str_t *content ,
 9                 const pjsua_msg_data *msg_data ,
10                 void *user_data )
11 {
12    ……
13 }

 

到這里,龐大的C實現就不貼出了,感興趣的可以找到相應目錄進行進一步的學習。
通過這篇文章,我們了解到CSipSimple通過aidl方法實現進程間通信,從而實現sendMessage服務在Activity中調用。整個調用過程大家還可以進一步探討,希望能共同學習。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM