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 }
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中调用。整个调用过程大家还可以进一步探讨,希望能共同学习。
作者:Wayne
出处:http://www.cnblogs.com/dwayne/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意请保留此段声明,且在文章页面明显位置给出原文链接。