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 }
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 }
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中調用。整個調用過程大家還可以進一步探討,希望能共同學習。


