한국어

스마트폰앱

온누리070 플레이스토어 다운로드
    acrobits softphone
     온누리 070 카카오 프러스 친구추가온누리 070 카카오 프러스 친구추가친추
     카카오톡 채팅 상담 카카오톡 채팅 상담카톡
    
     라인상담
     라인으로 공유

     페북공유
    
     PAYPAL
     
     PRICE
     

pixel.gif

    before pay call 0088 from app



CSipSimple是运行在android设备上的一个开源的sip协议应用程序,本文其中的拨打电话机制进行大致分析。

项目中,拨打电话利用了AIDL方法来实现。aidl是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它来定义进程间的通信接口,完成IPC(Inter-Process Communication,进程间通信)。

创建.aidl文件

2012071008561074.png

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.     * Get the current API version 
  31.     * @return version number. 1000 x major version + minor version 
  32.     * Each major version must be compatible with all versions of the same major version 
  33.     */  
  34.   
  35. .........  
  36. void makeCallWithOptions(in String callee, int accountId, in Bundle options);  
  37. }  

ISipService.aidl中定义了包含makeCallWithOptions
方法的接口ISipService。

自动编译生成java文件

eclipse中的ADT插件会自动在aidl文件中声明的包名目录下生成java文件,如下图所示:

2012071009025992.png
ISipService.java

  1. package com.csipsimple.api;  
  2. public interface ISipService extends android.os.IInterface  
  3. {  
  4. ……  
  5. //Place a call  
  6.   
  7. ublic void makeCallWithOptions(java.lang.String callee, int accountId, android.os.Bundle options) throws android.os.RemoteException;  
  8. }  

接下来就是实现ISipService.aidl中定义的接口,提供接口的实例供客户端调用

IPC实现

项目中拨打电话  

void com.csipsimple.api.ISipService.makeCallWithOptions(String msg, String toNumber, long accountId)

结合代码一层层看调用

目录:src\com\csipsimple\ui\dialpad

DialerFragment.java

  1. private ISipService service;  
  2. private ServiceConnection connection = new ServiceConnection() {  
  3.   
  4.     @Override  
  5.     public void onServiceConnected(ComponentName arg0, IBinder arg1) {  
  6.         service = ISipService.Stub.asInterface(arg1);  
  7.      ........  
  8.     }  
  9.   
  10.     @Override  
  11.     public void onServiceDisconnected(ComponentName arg0) {  
  12.         service = null;  
  13.     }  
  14. };  


  1. <span style="color:#333333;">   @Override  
  2.     public void placeCall() {  
  3.         placeCallWithOption(null);  
  4.     }  
  5.   
  6. private void placeCallWithOption(Bundle b) {  
  7.         if (service == null) {  
  8.             return;  
  9.         }  
  10.         String toCall = "";  
  11.         Long accountToUse = SipProfile.INVALID_ID;  
  12.         // Find account to use  
  13.         SipProfile acc = accountChooserButton.getSelectedAccount();  
  14.         if (acc != null) {  
  15.             accountToUse = acc.id;  
  16.         }  
  17.         // Find number to dial  
  18.         if(isDigit) {  
  19.             toCall = PhoneNumberUtils.stripSeparators(digits.getText().toString());  
  20.         }else {  
  21.             toCall = digits.getText().toString();  
  22.         }  
  23.           
  24.         if (TextUtils.isEmpty(toCall)) {  
  25.             return;  
  26.         }  
  27.   
  28.         // Well we have now the fields, clear theses fields  
  29.         digits.getText().clear();  
  30.   
  31.         // -- MAKE THE CALL --//  
  32.         if (accountToUse >= 0) {  
  33.             // It is a SIP account, try to call service for that  
  34.             try {  
  35.                </span><span style="color:#ff0000;"> service.makeCallWithOptions(toCall, accountToUse.intValue(), b);</span><span style="color:#333333;">  
  36.             } catch (RemoteException e) {  
  37.                 Log.e(THIS_FILE, "Service can't be called to make the call");  
  38.             }  
  39.         } else if (accountToUse != SipProfile.INVALID_ID) {  
  40.             // It's an external account, find correct external account  
  41.             CallHandlerPlugin ch = new CallHandlerPlugin(getActivity());  
  42.             ch.loadFrom(accountToUse, toCall, new OnLoadListener() {  
  43.                 @Override  
  44.                 public void onLoad(CallHandlerPlugin ch) {  
  45.                     placePluginCall(ch);  
  46.                 }  
  47.             });  
  48.         }  
  49.     }  
  50.     </span>  


这里的调用需要先了解Service的机制
service.makeCallWithOptions(toCall, accountToUse.intValue(), b)
方法调用了ISipService的方法,找到它的代码如下:
目录:src\com\csipsimple\service
  1. 2.服务端  
  2. SipService.java  
  3. /** 
  4.   * 继承 Service发布服务 
  5.   */  
  6.  public class SipService extends Service {  
  7.      ...  
  8.    
  9.      // 为服务实现公共接口, Stub类继承了Binder  
  10.      private final ISipService.Stub binder = new ISipService.Stub() {  
  11.         ...  
  12.        @Override  
  13.         public void makeCallWithOptions(final String callee, final int accountId, final Bundle options)  
  14.                 throws RemoteException {  
  15.             SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);  
  16.             //We have to ensure service is properly started and not just binded  
  17.             SipService.this.startService(new Intent(SipService.this, SipService.class));  
  18.               
  19.             if(pjService == null) {  
  20.                 Log.e(THIS_FILE, "Can't place call if service not started");  
  21.                 // TODO - we should return a failing status here  
  22.                 return;  
  23.             }  
  24.               
  25.             if(!supportMultipleCalls) {  
  26.                 // Check if there is no ongoing calls if so drop this request by alerting user  
  27.                 SipCallSession activeCall = pjService.getActiveCallInProgress();  
  28.                 if(activeCall != null) {  
  29.                     if(!CustomDistribution.forceNoMultipleCalls()) {  
  30.                         notifyUserOfMessage(R.string.not_configured_multiple_calls);  
  31.                     }  
  32.                     return;  
  33.                 }  
  34.             }  
  35.             getExecutor().execute(new SipRunnable() {  
  36.                 @Override  
  37.                 protected void doRun() throws SameThreadException {  
  38.                   <span style="color:#ff0000;">  pjService.makeCall(callee, accountId, options);  
  39.                 }</span>  
  40.             });  
  41.         }  
  42.           


/**
      * 返回一个实现了接口的类对象,给客户端接收
      */
     @Override
     public IBinder onBind(Intent intent) {
 
        String serviceName = intent.getAction();
        Log.d(THIS_FILE, "Action is " + serviceName );
        if (serviceName == null || serviceName.equalsIgnoreCase(SipManager.INTENT_SIP_SERVICE )) {
            Log.d(THIS_FILE, "Service returned");
            return binder ;
        } else if (serviceName. equalsIgnoreCase(SipManager.INTENT_SIP_CONFIGURATION )) {
            Log.d(THIS_FILE, "Conf returned");
            return binderConfiguration ;
        }
        Log.d(THIS_FILE, "Default service (SipService) returned");
        return binder;
     }
     
     ...
 }

上文说过,需要实现ISipService.aidl中定义的接口,来提供接口的实例供客户端调用。要实现自己的接口,就从ISipService.Stub类继承,然后实现相关的方法。
Stub类继承了Binder,因此它的对象就可以被远程的进程调用了。如果Service中有对象继承了Stub类,那么这个对象中的方法就可以在Activity等地方中使用,也就是说此时makeCallWithOptions
就可以被其他Activity访问调用了。
现在我们通过onBind(Intent intent)方法得到了可供客户端接收的IBinder对象,就可以回头看看刚才DialerFragment.java文件中的调用情况了。
在客户端(此处也就是调用远程服务的Activity)实现ServiceConnection,在ServiceConnection.onServiceConnected()方法中会接收到IBinder对象,调用ISipService.Stub.asInterface((IBinder)service)将返回值转换为ISipService类型。
语句
  1. <span style="color:#ff0000;">service.makeCallWithOptions(toCall, accountToUse.intValue(), b);</span><span style="font-family:Verdana, Arial, Helvetica, sans-serif;">调用接口中的方法,完成IPC方法。</span>  
回到刚才的服务端实现,在继承Service发布服务的代码中,调用了 pjService.makeCall(callee, accountId, options)方法。
先看看这部分代码:
目录:src\com\csipsimple\pjsip
PjSipService.java
  1. <span style="color:#333333;">public int makeCall(String callee, int accountId, Bundle b) throws SameThreadException {  
  2.         if (!created) {  
  3.             return -1;  
  4.         }  
  5.   
  6.         final ToCall toCall = sanitizeSipUri(callee, accountId);  
  7.         if (toCall != null) {  
  8.             pj_str_t uri = pjsua.pj_str_copy(toCall.getCallee());  
  9.   
  10.             // Nothing to do with this values  
  11.             byte[] userData = new byte[1];  
  12.             int[] callId = new int[1];  
  13.             pjsua_call_setting cs = new pjsua_call_setting();  
  14.             pjsua_msg_data msgData = new pjsua_msg_data();  
  15.             int pjsuaAccId = toCall.getPjsipAccountId();  
  16.               
  17.             // Call settings to add video  
  18.             pjsua.call_setting_default(cs);  
  19.             cs.setAud_cnt(1);  
  20.             cs.setVid_cnt(0);  
  21.             if(b != null && b.getBoolean(SipCallSession.OPT_CALL_VIDEO, false)) {  
  22.                 cs.setVid_cnt(1);  
  23.             }  
  24.             cs.setFlag(0);  
  25.               
  26.             pj_pool_t pool = pjsua.pool_create("call_tmp"512512);  
  27.               
  28.             // Msg data to add headers  
  29.             pjsua.msg_data_init(msgData);  
  30.             pjsua.csipsimple_init_acc_msg_data(pool, pjsuaAccId, msgData);  
  31.             if(b != null) {  
  32.                 Bundle extraHeaders = b.getBundle(SipCallSession.OPT_CALL_EXTRA_HEADERS);  
  33.                 if(extraHeaders != null) {  
  34.                     for(String key : extraHeaders.keySet()) {  
  35.                         try {  
  36.                             String value = extraHeaders.getString(key);  
  37.                             if(!TextUtils.isEmpty(value)) {  
  38.                                 int res = pjsua.csipsimple_msg_data_add_string_hdr(pool, msgData, pjsua.pj_str_copy(key), pjsua.pj_str_copy(value));  
  39.                                 if(res == pjsuaConstants.PJ_SUCCESS) {  
  40.                                     Log.e(THIS_FILE, "Failed to add Xtra hdr (" + key + " : " + value + ") probably not X- header");  
  41.                                 }  
  42.                             }  
  43.                         }catch(Exception e) {  
  44.                             Log.e(THIS_FILE, "Invalid header value for key : " + key);  
  45.                         }  
  46.                     }  
  47.                 }  
  48.             }  
  49.               
  50.             </span><span style="color:#ff0000;">int status = pjsua.call_make_call(pjsuaAccId, uri, cs, userData, msgData, callId);</span><span style="color:#333333;">  
  51.             if(status == pjsuaConstants.PJ_SUCCESS) {  
  52.                 dtmfToAutoSend.put(callId[0], toCall.getDtmf());  
  53.                 Log.d(THIS_FILE, "DTMF - Store for " + callId[0] + " - "+toCall.getDtmf());  
  54.             }  
  55.             pjsua.pj_pool_release(pool);  
  56.             return status;  
  57.         } else {  
  58.             service.notifyUserOfMessage(service.getString(R.string.invalid_sip_uri) + " : "  
  59.                     + callee);  
  60.         }  
  61.         return -1;  
  62.     }</span>  

由红色部分的语句,我们找到pjsua类。
目录:src\org\pjsip\pjsua
pjsua.java
  1. package org.pjsip.pjsua;  
  2.   
  3. public class pjsua implements pjsuaConstants {  
  4. public synchronized static int call_make_call(int acc_id, pj_str_t dst_uri, pjsua_call_setting opt, byte[] user_data, pjsua_msg_data msg_data, int[] p_call_id) {  
  5.    <span style="color:#ff0000;"return pjsuaJNI.call_make_call</span>(acc_id, pj_str_t.getCPtr(dst_uri), dst_uri, pjsua_call_setting.getCPtr(opt), opt, user_data, pjsua_msg_data.getCPtr(msg_data), msg_data, p_call_id);  
  6.   }  
  7. ..........  
  8. }  
继续看调用,找到pjsuaJNI文件。
目录:src\org\pjsip\pjsua
pjsuaJNI.java
/* ----------------------------------------------------------------------------
  * This file was automatically generated by SWIG (http://www.swig.org).
  * Version 2.0.4
  *
  * Do not make changes to this file unless you know what you are doing--modify
  * the SWIG interface file instead.
  * ----------------------------------------------------------------------------- */
 
 package org.pjsip.pjsua;
 
 public class pjsuaJNI {
 
     ...
     
   public final static native int call_make_call(int jarg1, long jarg2, pj_str_t jarg2_, long jarg3, pjsua_call_setting jarg3_, byte[] jarg4, long jarg5, pjsua_msg_data jarg5_, int[] jarg6);
     
     ...
     
 }

我们看到了native方法call_make_call,它调用的是封装在库libpjsipjni.so中的函数pjsua_call_make_call,进一步可以在jni目录下找到C代码。

目录:jni\pjsip\sources\pjsip\src\pjsua-lib

pjsua_call.c
  1. PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id,  
  2.                      const pj_str_t *dest_uri,  
  3.                      const pjsua_call_setting *opt,  
  4.                      void *user_data,  
  5.                      const pjsua_msg_data *msg_data,  
  6.                      pjsua_call_id *p_call_id)  
  7. {  
  8.     pj_pool_t *tmp_pool = NULL;  
  9.     pjsip_dialog *dlg = NULL;  
  10.     pjsua_acc *acc;  
  11.     pjsua_call *call;  
  12.     int call_id = -1;  
  13.     pj_str_t contact;  
  14.     pj_status_t status;  
  15.   
  16.   
  17.     /* Check that account is valid */  
  18.     PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),   
  19.              PJ_EINVAL);  
  20.   
  21.     /* Check arguments */  
  22.     PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);  
  23.   
  24.     PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,  
  25.           (int)dest_uri->slen, dest_uri->ptr));  
  26.   
  27.     pj_log_push_indent();  
  28.   
  29.     PJSUA_LOCK();  
  30.   
  31.     /* Create sound port if none is instantiated, to check if sound device 
  32.      * can be used. But only do this with the conference bridge, as with  
  33.      * audio switchboard (i.e. APS-Direct), we can only open the sound  
  34.      * device once the correct format has been known 
  35.      */  
  36.     if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&   
  37.     pjsua_var.null_snd==NULL && !pjsua_var.no_snd)   
  38.     {  
  39.     status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);  
  40.     if (status != PJ_SUCCESS)  
  41.         goto on_error;  
  42.     }  
  43.   
  44.     acc = &pjsua_var.acc[acc_id];  
  45.     if (!acc->valid) {  
  46.     pjsua_perror(THIS_FILE, "Unable to make call because account "  
  47.              "is not valid", PJ_EINVALIDOP);  
  48.     status = PJ_EINVALIDOP;  
  49.     goto on_error;  
  50.     }  
  51.   
  52.     /* Find free call slot. */  
  53.     call_id = alloc_call_id();  
  54.   
  55.     if (call_id == PJSUA_INVALID_ID) {  
  56.     pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);  
  57.     status = PJ_ETOOMANY;  
  58.     goto on_error;  
  59.     }  
  60.   
  61.     call = &pjsua_var.calls[call_id];  
  62.   
  63.     /* Associate session with account */  
  64.     call->acc_id = acc_id;  
  65.     call->call_hold_type = acc->cfg.call_hold_type;  
  66.   
  67.     /* Apply call setting */  
  68.     status = apply_call_setting(call, opt, NULL);  
  69.     if (status != PJ_SUCCESS) {  
  70.     pjsua_perror(THIS_FILE, "Failed to apply call setting", status);  
  71.     goto on_error;  
  72.     }  
  73.   
  74.     /* Create temporary pool */  
  75.     tmp_pool = pjsua_pool_create("tmpcall10"512256);  
  76.   
  77.     /* Verify that destination URI is valid before calling  
  78.      * pjsua_acc_create_uac_contact, or otherwise there   
  79.      * a misleading "Invalid Contact URI" error will be printed 
  80.      * when pjsua_acc_create_uac_contact() fails. 
  81.      */  
  82.     if (1) {  
  83.     pjsip_uri *uri;  
  84.     pj_str_t dup;  
  85.   
  86.     pj_strdup_with_null(tmp_pool, &dup, dest_uri);  
  87.     uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);  
  88.   
  89.     if (uri == NULL) {  
  90.         pjsua_perror(THIS_FILE, "Unable to make call",   
  91.              PJSIP_EINVALIDREQURI);  
  92.         status = PJSIP_EINVALIDREQURI;  
  93.         goto on_error;  
  94.     }  
  95.     }  
  96.   
  97.     /* Mark call start time. */  
  98.     pj_gettimeofday(&call->start_time);  
  99.   
  100.     /* Reset first response time */  
  101.     call->res_time.sec = 0;  
  102.   
  103.     /* Create suitable Contact header unless a Contact header has been 
  104.      * set in the account. 
  105.      */  
  106.     if (acc->contact.slen) {  
  107.     contact = acc->contact;  
  108.     } else {  
  109.     status = pjsua_acc_create_uac_contact(tmp_pool, &contact,  
  110.                           acc_id, dest_uri);  
  111.     if (status != PJ_SUCCESS) {  
  112.         pjsua_perror(THIS_FILE, "Unable to generate Contact header",   
  113.              status);  
  114.         goto on_error;  
  115.     }  
  116.     }  
  117.   
  118.     /* Create outgoing dialog: */  
  119.     status = pjsip_dlg_create_uac( pjsip_ua_instance(),   
  120.                    &acc->cfg.id, &contact,  
  121.                    dest_uri, dest_uri, &dlg);  
  122.     if (status != PJ_SUCCESS) {  
  123.     pjsua_perror(THIS_FILE, "Dialog creation failed", status);  
  124.     goto on_error;  
  125.     }  
  126.   
  127.     /* Increment the dialog's lock otherwise when invite session creation 
  128.      * fails the dialog will be destroyed prematurely. 
  129.      <span style="WHITE-SPACE: pre">    </span>*/  
  130.     pjsip_dlg_inc_lock(dlg);  
  131.   
  132.     if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)  
  133.         pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);  
  134.   
  135.     /* Calculate call's secure level */  
  136.     call->secure_level = get_secure_level(acc_id, dest_uri);  
  137.   
  138.     /* Attach user data */  
  139.     call->user_data = user_data;  
  140.       
  141.     /* Store variables required for the callback after the async 
  142.      * media transport creation is completed. 
  143.      */  
  144.     if (msg_data) {  
  145.     call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(  
  146.                                                           dlg->pool, msg_data);  
  147.     }  
  148.     call->async_call.dlg = dlg;  
  149.   
  150.     /* Temporarily increment dialog session. Without this, dialog will be 
  151.      * prematurely destroyed if dec_lock() is called on the dialog before 
  152.      * the invite session is created. 
  153.      */  
  154.     pjsip_dlg_inc_session(dlg, &pjsua_var.mod);  
  155.   
  156.     /* Init media channel */  
  157.     status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,   
  158.                       call->secure_level, dlg->pool,  
  159.                       NULL, NULL, PJ_TRUE,  
  160.                                       &on_make_call_med_tp_complete);  
  161.     if (status == PJ_SUCCESS) {  
  162.         status = on_make_call_med_tp_complete(call->index, NULL);  
  163.         if (status != PJ_SUCCESS)  
  164.         goto on_error;  
  165.     } else if (status != PJ_EPENDING) {  
  166.     pjsua_perror(THIS_FILE, "Error initializing media channel", status);  
  167.         pjsip_dlg_dec_session(dlg, &pjsua_var.mod);  
  168.     goto on_error;  
  169.     }  
  170.   
  171.     /* Done. */  
  172.   
  173.     if (p_call_id)  
  174.     *p_call_id = call_id;  
  175.   
  176.     pjsip_dlg_dec_lock(dlg);  
  177.     pj_pool_release(tmp_pool);  
  178.     PJSUA_UNLOCK();  
  179.   
  180.     pj_log_pop_indent();  
  181.   
  182.     return PJ_SUCCESS;  
  183.   
  184.   
  185. on_error:  
  186.     if (dlg) {  
  187.     /* This may destroy the dialog */  
  188.     pjsip_dlg_dec_lock(dlg);  
  189.     }  
  190.   
  191.     if (call_id != -1) {  
  192.     reset_call(call_id);  
  193.     pjsua_media_channel_deinit(call_id);  
  194.     }  
  195.   
  196.     if (tmp_pool)  
  197.     pj_pool_release(tmp_pool);  
  198.     PJSUA_UNLOCK();  
  199.   
  200.     pj_log_pop_indent();  
  201.     return status;  
  202. }  
  1. 通过本文的研究分析,<span style="font-family:Consolas;color:#333333;">我们了解到CSipSimple通过aidl方法实现进程间通信,从而实现了拨打电话功能。</span>  

조회 수 :
43210
등록일 :
2014.02.09
19:02:12 (*.251.139.148)
엮인글 :
http://webs.co.kr/index.php?document_srl=38620&act=trackback&key=996
게시글 주소 :
http://webs.co.kr/index.php?document_srl=38620
List of Articles
번호 제목 글쓴이 날짜 조회 수
65 Package org.linphone.core Description admin 2017-10-22 2573
64 Sqlite detail easy tutorial. admin 2017-09-09 2810
63 Change package name Linphone Android admin 2017-08-25 2985
62 Liblinphone - import Linphone library in Android Studio 2017 admin 2017-08-25 3223
61 I am able to build the limphone on mac by follow the steps describe here admin 2017-08-25 2932
60 To build liblinphone for Android, you must COMPILATION INSTRUCTIONS admin 2017-08-25 3068
59 sipdroid source code admin 2017-08-08 3028
58 안드로이드 주소록 전체가져오기 이름만가져오기 사진가져오기 코드 admin 2015-04-13 14767
57 the sipdroid Research Miscellaneous admin 2015-03-26 5751
56 HSS070 English Korean 무료 국제전화 미국 중국 카나다 무료 통화 제공 admin 2014-12-28 9054
55 Softphones admin 2014-09-20 7494
54 /xxxxx/gen already exists but is not a source folder. Convert to a source folder or rename it. admin 2014-09-03 8345
53 CSIPSimple building Rebuilding Detail on Eclips Good All is heare perfect admin 2014-08-15 8074
52 Improving QoE of SIP-based Automated Voice Interaction in Mobile Networks file admin 2014-03-20 8704
51 speex support in android admin 2014-03-19 8720
50 voxmobile Source and how to build admin 2014-02-18 9696
49 PJSIP hung the phone constantly corrected the problem admin 2014-02-09 9959
» Csipsimple code rebuilding source sip 통신 Call Mechanism admin 2014-02-09 43210
47 Csipsimple code rebuilding source sip 통신 technical interviewer admin 2014-02-09 9663
46 Csipsimple code rebuilding source 주요인터페이스분석 admin 2014-02-09 99004
45 Csipsimple code rebuilding source 다운로드 구성 csipsimple 소스 프로젝트 admin 2014-02-09 10861
44 Android DialogFragment Tutorial admin 2014-02-09 10499
43 Creating a fullscreen DialogFragment with a custom background admin 2014-02-09 13335
42 [Android API] 네트워크 상태 체크하기 admin 2013-12-19 12319
41 [안드로이드] 네트워크 연결 상태 체크 함수. admin 2013-12-19 10719
40 안드로이드 인터넷연결체크 ( 3g/lte/wifi ) 메서드 admin 2013-12-19 17913
39 Android Get Phone Contacts details with Contact Image admin 2013-12-15 10176
38 안드로이드 커스텀 타이틀바 How to add custom title bar to android application admin 2013-10-02 13000
37 How to create a custom title bar admin 2013-07-15 13121
36 Rotary Dialer clip art admin 2013-04-20 20708
35 Java 프로그램 실행화일 만들기 : Launch4j admin 2013-02-21 14434
34 java network programming source code, learning socket programming admin 2012-12-04 13666
33 2X Client Configuration | Android Remote Desktop 안드로이드 PC 리모트 데스크탑 admin 2012-12-02 15378
32 WIFI 3G 인터넷 연결 실시간 체크 소스코드 Broadcast Receiver 사용 (Adnroid) admin 2012-10-31 21363
31 통신사별 와이파이 비밀번호 admin 2012-10-31 23726
30 팁, wifi 패스워드 모음집 password admin 2012-10-21 13993
29 Android User Session Management using Shared Preferences admin 2012-10-16 41578
28 Unicode Tables v4 admin 2012-10-16 34718
27 HTML Codes admin 2012-10-16 12961
26 IBM Scan Codes EBCDIC Codes admin 2012-10-16 15149
25 ASCII Table and Description admin 2012-10-16 14544
24 How to Show Alert Dialog in Android (간단하고 쉬운설명) admin 2012-10-15 30828
23 How to read contacts on Android 2.0 admin 2012-10-14 17084
22 WIFI 3G 인터넷 연결 체크 소스코드 직접 사용한 코드 android admin 2012-10-14 19190
21 Android Detect Internet Connection Status admin 2012-10-12 24961
20 JAVA + SSL (server and client examples) admin 2012-10-07 25813
19 Querying The Android Contact Database admin 2012-09-30 14234
18 Manage Contacts android admin 2012-09-30 16441
17 Android Contacts API 2 example admin 2012-09-30 17428
16 Android 2.0 주소록 동기화 기능 admin 2012-09-30 51497
15 안드로이드용 앱 개발 마켓등록 까지 해본거 샘플 무료국제전화 앱 FreeDialer admin 2012-09-27 18366
14 아이폰 IPhone 앱 개발 기본 admin 2012-09-27 15019
13 [안드로이드/개발] Intent admin 2012-09-23 26417
12 안드로이드 타이머 절적한 설명과 간단한소스코드 등 안드로이드 프로그램 설명 깔끔 admin 2012-09-23 36718
11 google voice source code admin 2012-09-22 14095
10 [안드로이드] 서버/클라이언트 소켓(Socket) 통신하기 admin 2012-09-20 29701
9 FreeDial 무료국제전화는 프리미엄 국제전화 다이렉트 회선사용 admin 2012-09-20 15379
8 Android SQlite 사용 admin 2012-09-04 16834
7 안드로이드마켓에 앱 등록하는 순서 admin 2012-08-19 52915
6 전화관련 안드로이드함수 admin 2012-08-18 18725
5 자바 ,이클립스,Android SDK, ADT 설치하여 무료국제전화 다이얼러를 만들어봅시다. admin 2012-08-06 18749
4 아이폰용 무료국제전화용 무료 다이얼러 Speedydialer 설치및 사용방법 file admin 2012-07-14 14440
3 접속번호 + 상대방번호 # 를 주소록에 전화번호로 저장하여 사용하면 편리하다 admin 2012-06-17 16189
2 App Store - ILD Dialer admin 2012-06-17 12604
1 한국휴대폰에서 미국 중국 카나다 무료 국제전화 서비스 admin 2012-04-27 13074