weizhengliang пре 2 година
родитељ
комит
cb728740de

+ 4 - 0
conversion_box/src/main/AndroidManifest.xml

@@ -20,5 +20,9 @@
         <activity android:name="com.wdkl.app.ncs.conversion_box.activity.AppUpdateActivity"
             android:screenOrientation="landscape"
             android:launchMode="singleTask"/>
+
+        <service
+            android:name="com.wdkl.app.ncs.conversion_box.service.WdklSipService"
+            android:label="@string/app_name" />
     </application>
 </manifest>

+ 148 - 15
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/activity/MainActivity.kt

@@ -25,6 +25,7 @@ import com.wdkl.app.ncs.conversion_box.databinding.MainActivityLayoutBinding
 import com.wdkl.app.ncs.conversion_box.fragment.*
 import com.wdkl.app.ncs.conversion_box.helper.*
 import com.wdkl.app.ncs.conversion_box.launch.MainLaunch
+import com.wdkl.app.ncs.conversion_box.service.WdklSipService
 import com.wdkl.app.ncs.conversion_box.settings.SettingConfig
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.base.BaseApplication
@@ -68,6 +69,9 @@ import okhttp3.Request
 import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
+import org.linphone.core.AccountCreator
+import org.linphone.core.RegistrationState
+import org.linphone.core.TransportType
 import serialporttest.utils.SerialPort485Util
 import serialporttest.utils.SerialPortUtil
 import java.io.File
@@ -99,6 +103,7 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
     private var clickTime : Long = 0
     private var clickSosTime : Long = 0
 
+    private var mAccountCreator: AccountCreator? = null
 
     //网络异常计数
     private var netErrCount : Int = 0
@@ -211,6 +216,20 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         }
 
         SoundPoolManager.getInstance().init()
+
+        if (SettingConfig.getSipEnabled(activity)) {
+            //启动sip服务
+            val serviceIntent = Intent(BaseApplication.appContext, WdklSipService::class.java)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                //android8.0以上通过startForegroundService启动service
+                startForegroundService(serviceIntent)
+            } else {
+                startService(serviceIntent)
+            }
+            view_title_layout_tv_point.text = "sip"
+        } else {
+            view_title_layout_tv_point.text = "rtc"
+        }
     }
 
     private fun checkAppExist() : Boolean {
@@ -603,6 +622,34 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         startTcp()
         showMessage("tcp connect...host: " + Constant.TCP_SERVER_URL + ", port: " + Constant.TCP_PORT)
 
+        if (data.sipIp != null) {
+            Constant.sip_ip = data.sipIp
+        }
+        if (data.sipPort != null) {
+            Constant.sip_port = data.sipPort
+        }
+
+        if (data.voiceType != null) {
+            val orgSipEnable = SettingConfig.getSipEnabled(activity)
+            if ("sip".equals(data.voiceType)) {
+                //使用sip通话
+                SettingConfig.setSipEnable(activity, true)
+                if (!orgSipEnable) {
+                    handler.postDelayed({
+                        AppUpdateHelper.reboot(activity)
+                    }, 5000)
+                }
+            } else {
+                //使用webrtc通话
+                SettingConfig.setSipEnable(activity, false)
+                if (orgSipEnable) {
+                    handler.postDelayed({
+                        AppUpdateHelper.reboot(activity)
+                    }, 5000)
+                }
+            }
+        }
+
         Thread(Runnable {
             while (!initialized) {
                 runOnUiThread(Runnable {
@@ -659,7 +706,9 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
     //显示设备信息
     override fun showDeviceInfo(deviceInfo: DeviceNurseInfoVO) {
-        Constant.SIP_ID = deviceInfo.sipId
+        if (deviceInfo.sipId != null) {
+            Constant.SIP_ID = deviceInfo.sipId
+        }
         Constant.DEVICE_ID = deviceInfo.id
         Constant.PART_ID = deviceInfo.partId
 
@@ -759,6 +808,38 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
         //initCallTimer()
         updateSettings(true)
+
+
+        if (SettingConfig.getSipEnabled(activity)) {
+            //配置sip账户
+            if (WdklSipService.getCore() != null) {
+                mAccountCreator = WdklSipService.getCore().createAccountCreator(null)
+                // 以下三项必须
+                if (!TextUtils.isEmpty(Constant.SIP_ID) && !TextUtils.isEmpty(Constant.sip_ip)) {
+                    Log.e(TAG, "sip connect: ${Constant.SIP_ID}@${Constant.sip_ip}:${Constant.sip_port}")
+                    mAccountCreator!!.setDomain(Constant.sip_ip)
+                    mAccountCreator!!.setUsername(Constant.SIP_ID)
+                    mAccountCreator!!.setPassword(Constant.SIP_ID)
+                    //默认使用udp
+                    mAccountCreator!!.transport = TransportType.Udp
+
+                    // 这里会自动创建代理配置、认证信息到 SIP核心
+                    val cfg = mAccountCreator!!.createProxyConfig()
+                    // 确保新创建的是最新
+                    WdklSipService.getCore().defaultProxyConfig = cfg
+
+                    /*if (Constants.sip_port != null) {
+                        var transports = WdklSipService.getCore().transports
+                        transports.udpPort = Constants.sip_port!!
+                        transports.tcpPort = Constants.sip_port!!
+                        transports.tlsPort = -1
+                        WdklSipService.getCore().transports = transports
+                    }*/
+                } else {
+                    showMessage("SIP empty")
+                }
+            }
+        }
     }
 
     override fun loadAppVersion(appInfo: AppVersionDO) {
@@ -843,8 +924,8 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                         } else if (Constant.CALL_STATE == Constant.CALL_INCOMING && addr.equals(curDeviceUart, true)) {
                             //分机接听
                             RingPlayHelper.stopRingTone()
-                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                             showCallFragment()
+                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                         } else {
                             //分机呼叫
                             startCall(addr)
@@ -1379,10 +1460,10 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
 
             //Sip注册状态
             Constant.EVENT_SIP_REGISTER_STATUS -> {
-                if (messageEvent.message is String) run {
-                    val status = messageEvent.message as String
-                    Log.d("sip", "sip regist status: " + status)
-                    updateStatus(status)
+                if (messageEvent.getMessage() is RegistrationState) {
+                    val state = messageEvent.getMessage() as RegistrationState
+                    Log.d(TAG, "sip register state: $state")
+                    //updateSipState(state)
                 }
             }
 
@@ -1423,8 +1504,8 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             Constant.CALL_STATE = Constant.CALL_INCOMING
                             if (Constant.autoAnswer) {
                                 //自动接听
-                                VoiceUtil.acceptAudioCall(tid, tcpModel.toId, fromId, interactionVO?.id)
                                 showCallFragment()
+                                VoiceUtil.acceptAudioCall(tid, tcpModel.toId, fromId, interactionVO?.id)
                             } else {
                                 //响铃并手动接听
                                 SerialPortHelper.callInChannel(curDeviceUart)
@@ -1449,6 +1530,21 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                             if (!TextUtils.isEmpty(doorAddr)) {
                                 SerialPortHelper.closeDoorLight(doorAddr)
                             }
+
+                            val sipCore = WdklSipService.getCore()
+                            if (sipCore == null || TextUtils.isEmpty(targetSip)) {
+                                //通话失败,重置并返回主界面
+                                showMessage("Core或targetSipId为空!")
+                                handoffCall()
+                            } else {
+                                val addressToCall = sipCore.interpretUrl(targetSip)
+                                val params = sipCore.createCallParams(null)
+                                params?.isVideoEnabled = false
+                                if (addressToCall != null) {
+                                    sipCore.inviteAddressWithParams(addressToCall, params!!)
+                                    Log.d(TAG, ">>>>>>>>>>> invite address: " + addressToCall.asString())
+                                }
+                            }
                         } else if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
                             //呼叫成功
                             val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
@@ -1618,8 +1714,8 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
                                 outCallList.remove(ethMac.toUpperCase(Locale.ROOT))
                             }
                             RingPlayHelper.stopRingTone()
-                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                             showCallFragment()
+                            VoiceUtil.acceptAudioCall(tid, curDeviceId, fromId, curInteractionVO!!.id)
                         } else if (tcpModel.action == TcpAction.VoiceAction.RS485HANDOFF) {
                             /*EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_END_CALL))
                             Constant.CALL_STATE = Constant.CALL_STANDBY
@@ -1840,13 +1936,23 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         //模拟分机呼叫特殊性,所有呼叫都是通过转换盒,但是转换盒只有一台,同一时间只能建立一个呼叫,为保证其他分机也能呼叫(不是通话),
         //所有模拟分机的通话都由对方来创建room,然后转换盒加入room来通话,所以房间id都是对方的,转换盒是被动加入通话
         if (callFragment == null) {
-            var fragment = CallFragment()
-            var bundle = Bundle()
-            bundle.putInt("call_state", 1)
-            bundle.putBoolean("audio_only", true)
-            bundle.putString("targetSip", targetSip)
-            fragment.arguments = bundle
-            addCallFragment(fragment)
+            if (SettingConfig.getSipEnabled(activity)) {
+                var fragment = SipCallFragment()
+                var bundle = Bundle()
+                bundle.putInt("call_state", 1)
+                bundle.putBoolean("audio_only", true)
+                bundle.putString("tcp_tid", tid)
+                fragment.arguments = bundle
+                addCallFragment(fragment)
+            } else {
+                var fragment = CallFragment()
+                var bundle = Bundle()
+                bundle.putInt("call_state", 1)
+                bundle.putBoolean("audio_only", true)
+                bundle.putString("targetSip", targetSip)
+                fragment.arguments = bundle
+                addCallFragment(fragment)
+            }
         }
     }
 
@@ -1890,6 +1996,33 @@ class MainActivity :BaseActivity<MainActivityPresenter, MainActivityLayoutBindin
         }
     }
 
+    private fun updateSipState(state: RegistrationState) {
+        runOnUiThread {
+            when (state) {
+                RegistrationState.Ok -> {
+                    //连接完成
+                    view_title_layout_tv_point.setBackgroundResource(R.color.green)
+                }
+
+                RegistrationState.Failed -> {
+                    //连接错误
+                    view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
+                }
+
+                RegistrationState.Progress -> {
+                    //正在连接
+                    view_title_layout_tv_point.setBackgroundResource(R.color.yellow_color)
+                }
+
+                RegistrationState.None, RegistrationState.Cleared -> {
+                    //默认状态,断开连接
+                    view_title_layout_tv_point.setBackgroundResource(R.color.register_text_color)
+                }
+            }
+            view_title_layout_tv_point.text = "sip"
+        }
+    }
+
     private fun updateNetState() {
         if (NetHelper.getInstance().networkType == ConnectivityManager.TYPE_WIFI) {
             view_title_layout_iv_wifi.visibility = View.VISIBLE

+ 160 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/fragment/SipCallFragment.kt

@@ -0,0 +1,160 @@
+package com.wdkl.app.ncs.conversion_box.fragment
+
+import android.os.*
+import android.support.v4.app.Fragment
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.enation.javashop.utils.base.tool.BaseToolActivity
+import com.wdkl.app.ncs.conversion_box.R
+import com.wdkl.app.ncs.conversion_box.activity.MainActivity
+import com.wdkl.app.ncs.conversion_box.helper.AudioRouteUtils
+import com.wdkl.app.ncs.conversion_box.helper.RingPlayHelper
+import com.wdkl.app.ncs.conversion_box.service.WdklSipService
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import kotlinx.android.synthetic.main.call_fragment_layout.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import org.linphone.core.Core
+
+class SipCallFragment: Fragment() {
+    private val TAG = "SipCallFragment"
+
+    private lateinit var baseActivity: BaseToolActivity
+
+    private val handler = Handler(Looper.getMainLooper())
+    private var callEnded: Boolean = false
+
+    //通话状态:0-去电, 1-来电
+    protected var callState : Int = 0
+    protected var onlyAudio: Boolean = true
+
+    //呼叫倒计时
+    private var sipCore: Core? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        retainInstance = true
+
+        callState = arguments.getInt("call_state")
+        onlyAudio = arguments.getBoolean("audio_only")
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        /**初始化宿主Activity*/
+        baseActivity = activity as BaseToolActivity
+
+        return inflater.inflate(R.layout.call_fragment_layout, null)
+    }
+
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        init()
+    }
+
+    fun init() {
+        RingPlayHelper.stopRingTone()
+
+        sipCore = WdklSipService.getCore()
+        toggleSpeaker(true)
+
+        //Log.e(TAG, "udpPort: ${sipCore!!.transports.udpPort}, tcpPort: ${sipCore!!.transports.tcpPort}")
+        //Log.d(TAG, "callState: $callState, local sip: ${Constants.sip_id}, target sip: ${Constants.targetSipId}")
+
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        if (activity != null) {
+            (activity as MainActivity).openCall()
+        }
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+
+        RingPlayHelper.stopRingTone()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        handler.removeCallbacksAndMessages(null)
+    }
+
+    private fun callTerminate() {
+        if (sipCore != null && sipCore!!.callsNb > 0) {
+            var call = sipCore!!.currentCall
+            if (call == null) {
+                call = sipCore!!.calls[0]
+            }
+            call!!.terminate()
+        }
+    }
+
+    //通话结束
+    private fun callEnd() {
+        Log.e(TAG, ">>>>>>>>>>> call end !!!!!!!!!!!!!!!!!!")
+        RingPlayHelper.stopRingTone()
+
+        synchronized(this) {
+            if (callEnded) {
+                return
+            }
+            callEnded = true
+
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            callTerminate()
+
+            backToMain()
+        }
+    }
+
+    private fun toggleSpeaker(enable: Boolean) {
+        Log.d(TAG, "toggle speaker: $enable, sipCore: $sipCore")
+        if ( sipCore == null) {
+            return
+        }
+
+        if (enable) {
+            AudioRouteUtils.routeAudioToSpeaker(sipCore!!)
+        } else {
+            AudioRouteUtils.routeAudioToEarpiece(sipCore!!)
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.type) {
+
+            Constant.EVENT_END_CALL -> {
+                Log.d(TAG, ">>>>>>>>>>>>>> EVENT_END_CALL")
+                callEnd()
+            }
+
+            Constant.SIP_CALL_CONNECTED -> {
+                RingPlayHelper.stopRingTone()
+                //更新界面显示
+                Constant.CALL_STATE = Constant.CALL_CALLING
+                if (tv_call_text != null) {
+                    tv_call_text.setText(R.string.call_in_call)
+                }
+                if (activity != null) {
+                    (activity as MainActivity).inCalling()
+                }
+            }
+        }
+    }
+
+    //返回主界面
+    private fun backToMain() {
+        EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+    }
+}

+ 8 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AppUpdateHelper.java

@@ -12,6 +12,8 @@ import android.os.Build;
 import android.os.Environment;
 import android.util.Log;
 
+import com.wdkl.app.ncs.conversion_box.service.WdklSipService;
+import com.wdkl.app.ncs.conversion_box.settings.SettingConfig;
 import com.wdkl.ncs.android.component.welcome.activity.WelcomeActivity;
 
 import java.io.BufferedReader;
@@ -229,6 +231,12 @@ public class AppUpdateHelper {
     }
 
     public static void restartApp(Context context) {
+        if (SettingConfig.getSipEnabled(context)) {
+            //停止服务
+            Intent serviceIntent = new Intent(context, WdklSipService.class);
+            context.stopService(serviceIntent);
+        }
+
         //重新启动app
         Intent mStartActivity = new Intent(context.getApplicationContext(), WelcomeActivity.class);
         mStartActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

+ 170 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AudioRouteUtils.kt

@@ -0,0 +1,170 @@
+package com.wdkl.app.ncs.conversion_box.helper
+
+import android.telecom.CallAudioState
+import org.linphone.core.AudioDevice
+import org.linphone.core.Call
+import org.linphone.core.Core
+import org.linphone.core.tools.Log
+
+class AudioRouteUtils {
+    companion object {
+        private fun applyAudioRouteChange(
+            core: Core,
+            call: Call?,
+            types: List<AudioDevice.Type>,
+            output: Boolean = true
+        ) {
+            val currentCall = if (core.callsNb > 0) {
+                call ?: core.currentCall ?: core.calls[0]
+            } else {
+                Log.w("[Audio Route Helper] No call found, setting audio route on Core")
+                null
+            }
+            val conference = core.conference
+            val capability = if (output)
+                AudioDevice.Capabilities.CapabilityPlay
+            else
+                AudioDevice.Capabilities.CapabilityRecord
+            val preferredDriver = if (output) {
+                core.defaultOutputAudioDevice?.driverName
+            } else {
+                core.defaultInputAudioDevice?.driverName
+            }
+
+            val extendedAudioDevices = core.extendedAudioDevices
+            Log.i("[Audio Route Helper] Looking for an ${if (output) "output" else "input"} audio device with capability [$capability], driver name [$preferredDriver] and type [$types] in extended audio devices list (size ${extendedAudioDevices.size})")
+            val foundAudioDevice = extendedAudioDevices.find {
+                it.driverName == preferredDriver && types.contains(it.type) && it.hasCapability(capability)
+            }
+            val audioDevice = if (foundAudioDevice == null) {
+                Log.w("[Audio Route Helper] Failed to find an audio device with capability [$capability], driver name [$preferredDriver] and type [$types]")
+                extendedAudioDevices.find {
+                    types.contains(it.type) && it.hasCapability(capability)
+                }
+            } else {
+                foundAudioDevice
+            }
+
+            if (audioDevice == null) {
+                Log.e("[Audio Route Helper] Couldn't find audio device with capability [$capability] and type [$types]")
+                for (device in extendedAudioDevices) {
+                    // TODO: switch to debug?
+                    Log.i("[Audio Route Helper] Extended audio device: [${device.deviceName} (${device.driverName}) ${device.type} / ${device.capabilities}]")
+                }
+                return
+            }
+            if (conference != null && conference.isIn) {
+                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing conference audio to it")
+                if (output) conference.outputAudioDevice = audioDevice
+                else conference.inputAudioDevice = audioDevice
+            } else if (currentCall != null) {
+                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing call audio to it")
+                if (output) currentCall.outputAudioDevice = audioDevice
+                else currentCall.inputAudioDevice = audioDevice
+            } else {
+                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], changing core default audio device")
+                if (output) core.outputAudioDevice = audioDevice
+                else core.inputAudioDevice = audioDevice
+            }
+        }
+
+        private fun changeCaptureDeviceToMatchAudioRoute(core: Core, call: Call?, types: List<AudioDevice.Type>) {
+            when (types.first()) {
+                AudioDevice.Type.Bluetooth -> {
+                    if (isBluetoothAudioRecorderAvailable(core)) {
+                        Log.i("[Audio Route Helper] Bluetooth device is able to record audio, also change input audio device")
+                        applyAudioRouteChange(core, call, arrayListOf(AudioDevice.Type.Bluetooth), false)
+                    }
+                }
+                AudioDevice.Type.Headset, AudioDevice.Type.Headphones -> {
+                    if (isHeadsetAudioRecorderAvailable(core)) {
+                        Log.i("[Audio Route Helper] Headphones/Headset device is able to record audio, also change input audio device")
+                        applyAudioRouteChange(core, call, (arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset)), false)
+                    }
+                }
+                AudioDevice.Type.Earpiece, AudioDevice.Type.Speaker -> {
+                    Log.i("[Audio Route Helper] Audio route requested to Earpiece or Speaker, setting input to Microphone")
+                    applyAudioRouteChange(core, call, (arrayListOf(AudioDevice.Type.Microphone)), false)
+                }
+                else -> {
+                    Log.w("[Audio Route Helper] Unexpected audio device type: ${types.first()}")
+                }
+            }
+        }
+
+        private fun routeAudioTo(
+            core: Core,
+            call: Call?,
+            types: List<AudioDevice.Type>,
+            skipTelecom: Boolean = false
+        ) {
+            val currentCall = call ?: core.currentCall ?: core.calls.firstOrNull()
+
+                if (currentCall != null) {
+                    Log.i("[Audio Route Helper] Telecom Helper & matching connection found, dispatching audio route change through it")
+                    // We will be called here again by NativeCallWrapper.onCallAudioStateChanged()
+                    // but this time with skipTelecom = true
+                    //if (!Compatibility.changeAudioRouteForTelecomManager(connection, route)) {
+                        Log.w("[Audio Route Helper] Connection is already using this route internally, make the change!")
+                        applyAudioRouteChange(core, currentCall, types)
+                        changeCaptureDeviceToMatchAudioRoute(core, currentCall, types)
+                    //}
+                }
+
+        }
+
+        fun routeAudioToEarpiece(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
+            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Earpiece), skipTelecom)
+        }
+
+        fun routeAudioToSpeaker(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
+            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Speaker), skipTelecom)
+        }
+
+        fun routeAudioToBluetooth(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
+            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Bluetooth), skipTelecom)
+        }
+
+        fun routeAudioToHeadset(core: Core, call: Call? = null, skipTelecom: Boolean = false) {
+            routeAudioTo(core, call, arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset), skipTelecom)
+        }
+
+
+
+        fun isBluetoothAudioRouteAvailable(core: Core): Boolean {
+            for (audioDevice in core.audioDevices) {
+                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
+                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
+                ) {
+                    Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName} (${audioDevice.driverName})]")
+                    return true
+                }
+            }
+            return false
+        }
+
+        private fun isBluetoothAudioRecorderAvailable(core: Core): Boolean {
+            for (audioDevice in core.audioDevices) {
+                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
+                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
+                ) {
+                    Log.i("[Audio Route Helper] Found bluetooth audio recorder [${audioDevice.deviceName} (${audioDevice.driverName})]")
+                    return true
+                }
+            }
+            return false
+        }
+
+        private fun isHeadsetAudioRecorderAvailable(core: Core): Boolean {
+            for (audioDevice in core.audioDevices) {
+                if ((audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) &&
+                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
+                ) {
+                    Log.i("[Audio Route Helper] Found headset/headphones audio recorder [${audioDevice.deviceName} (${audioDevice.driverName})]")
+                    return true
+                }
+            }
+            return false
+        }
+    }
+}

+ 329 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/service/WdklSipService.java

@@ -0,0 +1,329 @@
+package com.wdkl.app.ncs.conversion_box.service;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import android.widget.Toast;
+
+
+import com.wdkl.app.ncs.conversion_box.R;
+import com.wdkl.app.ncs.conversion_box.settings.SettingConfig;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+import org.linphone.core.Call;
+import org.linphone.core.CallParams;
+import org.linphone.core.Core;
+import org.linphone.core.CoreListenerStub;
+import org.linphone.core.Factory;
+import org.linphone.core.LogCollectionState;
+import org.linphone.core.PayloadType;
+import org.linphone.core.ProxyConfig;
+import org.linphone.core.RegistrationState;
+import org.linphone.mediastream.Version;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class WdklSipService extends Service {
+    private static final String START_SIPPHONE_LOGS = " ==== Device information dump ====";
+
+    private static final String PREFER_PAYLOAD = "PUMU";
+
+    //单例化服务,以便全局调用
+    private static WdklSipService sInstance;
+
+    private NotificationManager notificationManager = null;
+    private String notificationId = "channelId0";
+    private String notificationName = "sip_service";
+
+    private Handler mHandler;
+    private Timer mTimer;
+
+    private Core mCore;
+    private CoreListenerStub mCoreListener;
+
+    public static boolean sipTesting = false;
+
+    public static boolean isReady() {
+        return sInstance != null;
+    }
+
+    public static WdklSipService getInstance() {
+        return sInstance;
+    }
+
+    public static Core getCore() {
+        return sInstance.mCore;
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            NotificationChannel channel = new NotificationChannel(
+                    notificationId,
+                    notificationName,
+                    NotificationManager.IMPORTANCE_HIGH
+            );
+            channel.enableVibration(true);
+            channel.enableLights(true);
+            channel.setBypassDnd(true);
+            channel.setShowBadge(true);
+            channel.setSound(null, null);
+            notificationManager.createNotificationChannel(channel);
+        }
+
+        //首次调用必须使用 Factory相关方法
+        //这里开户调试日志及设置路径
+        String basePath = getFilesDir().getAbsolutePath();
+        Factory.instance().setLogCollectionPath(basePath);
+        Factory.instance().enableLogCollection(LogCollectionState.Enabled);
+        Factory.instance().setDebugMode(false, getString(R.string.app_name));
+
+        //收集一些设备信息
+        Log.i("sipCall", START_SIPPHONE_LOGS);
+        dumpDeviceInformation();
+        dumpInstalledLinphoneInformation();
+
+        mHandler = new Handler();
+        //主监听器,根据事件调用界面
+        mCoreListener = new CoreListenerStub() {
+            @Override
+            public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
+                if (!SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                    return;
+                }
+
+                Toast.makeText(WdklSipService.this, message, Toast.LENGTH_SHORT).show();
+                Log.d("sipCall", ">>>>>>>>>>>> call state: " + state + ", " + call.getRemoteAddress().asString());
+
+                if (state == Call.State.IncomingReceived || state == Call.State.IncomingEarlyMedia) {
+                    //Toast.makeText(WdklSipService.this, "Incoming call", Toast.LENGTH_LONG).show();
+                    //来电时将自动接听
+                    CallParams params = getCore().createCallParams(call);
+                    call.acceptWithParams(params);
+                } else if (state == Call.State.Connected) {
+                    if (sipTesting) {
+                        //通话已建立完成,打开通话界面
+                        //Intent intent = new Intent(WdklSipService.this, CallActivity.class);
+                        //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        //startActivity(intent);
+                    } else {
+                        EventBus.getDefault().post(new MessageEvent(1, Constant.SIP_CALL_CONNECTED));
+                    }
+                } else if (state == Call.State.End || state == Call.State.Released){
+                    EventBus.getDefault().post(new MessageEvent("endcall", Constant.EVENT_END_CALL));
+                }
+            }
+
+            @Override
+            public void onRegistrationStateChanged(Core core, ProxyConfig cfg, RegistrationState state, String message) {
+                if (!SettingConfig.getSipEnabled(BaseApplication.appContext)) {
+                    return;
+                }
+
+                EventBus.getDefault().post(new MessageEvent(state, Constant.EVENT_SIP_REGISTER_STATUS));
+            }
+        };
+
+        try {
+            //复制一些源资源
+            //默认配置只能在首次时安装一次
+            copyIfNotExist(R.raw.linphonerc_default, basePath + "/.wdkl_sip_rc");
+            //用户配置,每次复制
+            copyFromPackage(R.raw.linphonerc_factory, "wdkl_sip_rc");
+        } catch (IOException ioe) {
+            Log.e("sipCall",ioe.getMessage());
+        }
+
+        //创建SIP核心并加载监听器
+        mCore = Factory.instance()
+                .createCore(basePath + "/.wdkl_sip_rc", basePath + "/wdkl_sip_rc", this);
+        mCore.addListener(mCoreListener);
+        //SIP核心配置完成
+        configureCore();
+    }
+
+    private Notification getNotification() {
+        Notification.Builder builder = new Notification.Builder(this)
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setContentTitle("sip service")
+                .setContentText("running...")
+                .setOnlyAlertOnce(true);
+
+        //设置Notification的ChannelID,否则不能正常显示
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            builder.setChannelId(notificationId);
+        }
+
+        return builder.build();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            startForeground(11, getNotification()); //开前台服务
+        }
+
+        super.onStartCommand(intent, flags, startId);
+        //Toast.makeText(WdklSipService.this, "sip服务已启动", Toast.LENGTH_SHORT).show();
+
+        //如果服务已经在运行,则返回
+        if (sInstance != null) {
+            return START_STICKY;
+        }
+
+        //一旦服务启动,则一直保持
+        sInstance = this;
+
+        //SIP核心在创建和配置完成后,开启
+        mCore.start();
+        //必须定时运行 SIP核心 iterate()
+        TimerTask lTask = new TimerTask() {
+            @Override
+            public void run() {
+                mHandler.post(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                if (mCore != null) {
+                                    mCore.iterate();
+                                }
+                            }
+                        });
+            }
+        };
+        mTimer = new Timer("wdkl sip scheduler");
+        mTimer.schedule(lTask, 0, 20);
+
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        mCore.removeListener(mCoreListener);
+        mTimer.cancel();
+        mCore.stop();
+        // A stopped Core can be started again
+        // To ensure resources are freed, we must ensure it will be garbage collected
+        mCore = null;
+        // Don't forget to free the singleton as well
+        sInstance = null;
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onTaskRemoved(Intent rootIntent) {
+        // For this sample we will kill the Service at the same time we kill the app
+        stopSelf();
+
+        super.onTaskRemoved(rootIntent);
+    }
+
+    private void configureCore() {
+        // We will create a directory for user signed certificates if needed
+        String basePath = getFilesDir().getAbsolutePath();
+        String userCerts = basePath + "/user-certs";
+        File f = new File(userCerts);
+        if (!f.exists()) {
+            if (!f.mkdir()) {
+                Log.e("sipCall",userCerts + " can't be created.");
+            }
+        }
+        mCore.setUserCertificatesPath(userCerts);
+
+        //音频部分, 这里增加了一个遍历, 用于设置指定的音频格式.
+        PayloadType[] payloads = mCore.getAudioPayloadTypes();
+        for(int i = 0; i < payloads.length; i ++){
+            PayloadType pt = payloads[i];
+            //Log.i("sipCall", ">>>>>>>>>>>>>>>>>1 " + pt.getMimeType() + " = " + pt.enabled());
+            if (pt.getMimeType().equals("PCMU")
+//                    || pt.getMimeType().equals("PUMA")
+//                    || pt.getMimeType().equals("GSM")
+            ){
+                pt.enable(true);
+            } else {
+                pt.enable(false);
+            }
+        }
+        mCore.setAudioPayloadTypes(payloads);
+    }
+
+    private void dumpDeviceInformation() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DEVICE=").append(Build.DEVICE).append("\n");
+        sb.append("MODEL=").append(Build.MODEL).append("\n");
+        sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
+        sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
+        sb.append("Supported ABIs=");
+        for (String abi : Version.getCpuAbis()) {
+            sb.append(abi).append(", ");
+        }
+        sb.append("\n");
+        Log.i("sipCall",sb.toString());
+    }
+
+    private void dumpInstalledLinphoneInformation() {
+        PackageInfo info = null;
+        try {
+            info = getPackageManager().getPackageInfo(getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException nnfe) {
+            Log.e("sipCall",nnfe.getMessage());
+        }
+
+        if (info != null) {
+            Log.i(
+                    "[Service] sipphone version is ",
+                    info.versionName + " (" + info.versionCode + ")");
+        } else {
+            Log.i("sipCall","[Service] sipphone version is unknown");
+        }
+    }
+
+    private void copyIfNotExist(int ressourceId, String target) throws IOException {
+        File lFileToCopy = new File(target);
+        if (!lFileToCopy.exists()) {
+            copyFromPackage(ressourceId, lFileToCopy.getName());
+        }
+    }
+
+    private void copyFromPackage(int ressourceId, String target) throws IOException {
+        FileOutputStream lOutputStream = openFileOutput(target, 0);
+        InputStream lInputStream = getResources().openRawResource(ressourceId);
+        int readByte;
+        byte[] buff = new byte[8048];
+        while ((readByte = lInputStream.read(buff)) != -1) {
+            lOutputStream.write(buff, 0, readByte);
+        }
+        lOutputStream.flush();
+        lOutputStream.close();
+        lInputStream.close();
+    }
+}

+ 10 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/settings/SettingConfig.java

@@ -72,6 +72,8 @@ public class SettingConfig {
     //网络异常重启次数
     private static final String KEY_SP_NET_ERR_RESET_COUNT = "KEY_SP_NET_ERR_RESET_COUNT";
 
+    //是否使用sip通话
+    private static final String KEY_SP_SIP_ENABLE = "KEY_SP_SIP_ENABLE";
 
     //默认语言
     private static final String KEY_LANGUAGE_ID = "KEY_LANGUAGE_ID";
@@ -94,6 +96,14 @@ public class SettingConfig {
         getEditor(context).putInt(KEY_LANGUAGE_MODE, mode).apply();
     }
 
+    public static boolean getSipEnabled(Context context) {
+        return getSP(context).getBoolean(KEY_SP_SIP_ENABLE, false);
+    }
+
+    public static void setSipEnable(Context context, boolean enable) {
+        getEditor(context).putBoolean(KEY_SP_SIP_ENABLE, enable).apply();
+    }
+
     /**
      * 获取分机白天亮度
      *

+ 4 - 3
conversion_box/src/main/res/layout/view_title_layout.xml

@@ -8,10 +8,11 @@
     <!--SIP状态图标-->
     <TextView
         android:id="@+id/view_title_layout_tv_point"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
         android:layout_centerVertical="true"
-        android:layout_marginLeft="20dp"
+        android:gravity="center"
+        android:layout_marginLeft="10dp"
         android:background="@color/red_color"/>
 
     <!--医院名称-->

+ 20 - 0
conversion_box/src/main/res/raw/linphonerc_default

@@ -0,0 +1,20 @@
+[sip]
+contact="Linphone Android" <sip:allen@8.129.220.143>
+use_info=0
+use_ipv6=1
+keepalive_period=30000
+sip_port=-1
+sip_tcp_port=-1
+sip_tls_port=-1
+media_encryption=none
+
+[video]
+size=vga
+
+[app]
+tunnel=disabled
+push_notification=1
+
+[misc]
+max_calls=10
+history_max_size=100

+ 34 - 0
conversion_box/src/main/res/raw/linphonerc_factory

@@ -0,0 +1,34 @@
+
+#
+#This file shall not contain path referencing package name, in order to be portable when app is renamed.
+#Paths to resources must be set from LinphoneManager, after creating LinphoneCore.
+[net]
+mtu=1300
+#Because dynamic bitrate adaption can increase bitrate, we must allow "no limit"
+download_bw=0
+upload_bw=0
+force_ice_disablement=0
+
+[sip]
+guess_hostname=1
+register_only_when_network_is_up=1
+auto_net_state_mon=1
+auto_answer_replacing_calls=1
+ping_with_options=0
+use_cpim=1
+
+[video]
+displaytype=MSAndroidTextureDisplay
+
+[misc]
+enable_basic_to_client_group_chat_room_migration=0
+enable_simple_group_chat_message_state=0
+aggregate_imdn=1
+notify_each_friend_individually_when_presence_received=0
+
+[app]
+activation_code_length=4
+prefer_basic_chat_room=1
+
+[assistant]
+xmlrpc_url=https://subscribe.linphone.org:444/wizard.php

+ 2 - 0
middleware/build.gradle

@@ -55,6 +55,8 @@ dependencies {
         exclude group: 'com.android.support', module: 'support-annotations'
     })
 
+    compile files('libs/linphone-sdk-android-5.2.10.aar')
+
     compile project(':common')
     compile project(':resource')
     compile project(':traditionlib')

BIN
middleware/libs/linphone-sdk-android-5.2.10.aar


+ 5 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constant.java

@@ -17,6 +17,9 @@ public class Constant {
     //TCP服务端口
     public static int TCP_PORT = 5080;
 
+    public static String sip_ip = "";  //sip
+    public static int sip_port = 5060;
+
     //TCP服务连接心跳
     public static int TCP_HEART_BEAT = 9;
 
@@ -164,4 +167,6 @@ public class Constant {
     public static final int EVENT_END_CALL = 0x12;
 
     public static final int EVENT_DEVICE_TEST = 0x13;
+
+    public static final int SIP_CALL_CONNECTED = 0x14;
 }

+ 201 - 10
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/ServerInfo.java

@@ -13,58 +13,113 @@ public class ServerInfo implements Serializable {
     @ApiModelProperty(notes = "局域网TCP服务器IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String tcpLocalIp;
-
     @ApiModelProperty(notes = "互联网TCP服务器IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String tcpPublicIp;
-
     @ApiModelProperty(notes = "TCP端口")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer tcpPort;
-
     @ApiModelProperty(notes = "TCP体征数据库端口")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer tcpVsPort;
-
     @ApiModelProperty(notes = "TCP心跳间隔,单位:秒")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer tcpIdleSeconds;
+    @ApiModelProperty(notes = "TCP心跳是否开启")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Boolean tcpIdleLogEnabled;
+    @ApiModelProperty(notes = "平行主机模式是否开启")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Boolean openHostDeviceCombinedUse;
 
     @ApiModelProperty(notes = "局域网HTTP服务器IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String httpLocalIp;
-
     @ApiModelProperty(notes = "互联网HTTP服务器IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String httpPublicIp;
-
     @ApiModelProperty(notes = "HTTP端口")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer httpPort;
+    @ApiModelProperty(notes = "HTTP System端口")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer httpSystemPort;
+
+    @ApiModelProperty(notes = "互联网HTTP服务器IP")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String voiceType;
+
+    @ApiModelProperty(notes = "媒体服务是否启用")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Boolean mediaEnable;
+    @ApiModelProperty(notes = "每个科室限制的广播数")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer mediaPartLimit;
+    @ApiModelProperty(notes = "局域网媒体服务IP")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String mediaLocalIp;
+    @ApiModelProperty(notes = "互联网媒体服务IP")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String mediaPublicIp;
+    @ApiModelProperty(notes = "媒体服务端口")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer mediaPort;
 
     @ApiModelProperty(notes = "RTC Local IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String rtcLocalIp;
-
     @ApiModelProperty(notes = "RTC Public IP")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String rtcPublicIp;
-
     @ApiModelProperty(notes = "RTC 端口")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private Integer rtcPort;
 
+    @ApiModelProperty(notes = "SIP LOCAL IP")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String sipIp;
+    @ApiModelProperty(notes = "SIP LOCAL 端口")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer sipPort;
+    @ApiModelProperty(notes = "SIP PUBLIC IP")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String sipPublicIp;
+    @ApiModelProperty(notes = "SIP LOCAL 端口")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer sipPublicPort;
+    @ApiModelProperty(notes = "SIP LOCAL 端口")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Integer sipBroadcastStart;
+
     @ApiModelProperty(notes = "stun地址", example = "stun:xxx.xxx.xxx.xxx:3478")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String stunServer;
-
     @ApiModelProperty(notes = "turn地址", example = "turn:xxx.xxx.xxx.xxx:3478|username|password")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     private String[] turnServer;
 
+    @ApiModelProperty(notes = "语言")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String lang;
+
+    @ApiModelProperty(notes = "led屏控制,true就是服务器控制,false就是护士主机控制")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Boolean ledControl;
+    @ApiModelProperty(notes = "true就是科室控制点阵屏,false就是医院控制点阵屏")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private Boolean ledControlPart;
+
+    @ApiModelProperty(notes = "录像文件的网关")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String recordGateway;
+    @ApiModelProperty(notes = "录像文件的存储路径")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private String recordDir;
+
     private Long serverTime;
 
-    private String lang;
+
+    private String appName;
 
     public String getTcpLocalIp() {
         return tcpLocalIp;
@@ -106,6 +161,22 @@ public class ServerInfo implements Serializable {
         this.tcpIdleSeconds = tcpIdleSeconds;
     }
 
+    public Boolean getTcpIdleLogEnabled() {
+        return tcpIdleLogEnabled;
+    }
+
+    public void setTcpIdleLogEnabled(Boolean tcpIdleLogEnabled) {
+        this.tcpIdleLogEnabled = tcpIdleLogEnabled;
+    }
+
+    public Boolean getOpenHostDeviceCombinedUse() {
+        return openHostDeviceCombinedUse;
+    }
+
+    public void setOpenHostDeviceCombinedUse(Boolean openHostDeviceCombinedUse) {
+        this.openHostDeviceCombinedUse = openHostDeviceCombinedUse;
+    }
+
     public String getHttpLocalIp() {
         return httpLocalIp;
     }
@@ -130,6 +201,14 @@ public class ServerInfo implements Serializable {
         this.httpPort = httpPort;
     }
 
+    public Integer getHttpSystemPort() {
+        return httpSystemPort;
+    }
+
+    public void setHttpSystemPort(Integer httpSystemPort) {
+        this.httpSystemPort = httpSystemPort;
+    }
+
     public String getRtcLocalIp() {
         return rtcLocalIp;
     }
@@ -154,6 +233,22 @@ public class ServerInfo implements Serializable {
         this.rtcPort = rtcPort;
     }
 
+    public String getSipIp() {
+        return sipIp;
+    }
+
+    public void setSipIp(String sipIp) {
+        this.sipIp = sipIp;
+    }
+
+    public Integer getSipPort() {
+        return sipPort;
+    }
+
+    public void setSipPort(Integer sipPort) {
+        this.sipPort = sipPort;
+    }
+
     public String getStunServer() {
         return stunServer;
     }
@@ -185,4 +280,100 @@ public class ServerInfo implements Serializable {
     public void setLang(String lang) {
         this.lang = lang;
     }
+
+    public Boolean getMediaEnable() {
+        return mediaEnable;
+    }
+
+    public void setMediaEnable(Boolean mediaEnable) {
+        this.mediaEnable = mediaEnable;
+    }
+
+    public String getMediaLocalIp() {
+        return mediaLocalIp;
+    }
+
+    public void setMediaLocalIp(String mediaLocalIp) {
+        this.mediaLocalIp = mediaLocalIp;
+    }
+
+    public String getMediaPublicIp() {
+        return mediaPublicIp;
+    }
+
+    public void setMediaPublicIp(String mediaPublicIp) {
+        this.mediaPublicIp = mediaPublicIp;
+    }
+
+    public Integer getMediaPort() {
+        return mediaPort;
+    }
+
+    public void setMediaPort(Integer mediaPort) {
+        this.mediaPort = mediaPort;
+    }
+
+    public Integer getMediaPartLimit() {
+        return mediaPartLimit;
+    }
+
+    public void setMediaPartLimit(Integer mediaPartLimit) {
+        this.mediaPartLimit = mediaPartLimit;
+    }
+
+    public Boolean getLedControl() {
+        return ledControl;
+    }
+
+    public void setLedControl(Boolean ledControl) {
+        this.ledControl = ledControl;
+    }
+
+    public Boolean getLedControlPart() {
+        return ledControlPart;
+    }
+
+    public void setLedControlPart(Boolean ledControlPart) {
+        this.ledControlPart = ledControlPart;
+    }
+
+    public String getRecordGateway() {
+        return recordGateway;
+    }
+
+    public void setRecordGateway(String recordGateway) {
+        this.recordGateway = recordGateway;
+    }
+
+    public String getRecordDir() {
+        return recordDir;
+    }
+
+    public void setRecordDir(String recordDir) {
+        this.recordDir = recordDir;
+    }
+
+    public String getVoiceType() {
+        return voiceType;
+    }
+
+    public String getSipPublicIp() {
+        return sipPublicIp;
+    }
+
+    public Integer getSipPublicPort() {
+        return sipPublicPort;
+    }
+
+    public Integer getSipBroadcastStart() {
+        return sipBroadcastStart;
+    }
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
 }

+ 1 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/CommonUtils.java

@@ -17,6 +17,7 @@ public class CommonUtils {
     private static final String DEFAULT_URL = "172.28.100.100";
     //private static final String DEFAULT_URL = "8.129.220.143";
     //private static final String DEFAULT_URL = "119.23.151.229";
+    //private static final String DEFAULT_URL = "47.106.137.131";
     private static final String DEFAULT_URL_PORT = "8006";
     private static final String DEFAULT_SIP_PORT = "8188";