I have a connection delay that I cannot find explanations. Basically the connection to the local machine takes almost 1min in the Emulator and 20s+ on the physical device. I have tried everything I know and ended up with the code below. The time on device is now around 15s, but it is still a lot for just connecting.
Am not doing anything yet, just connecting.
Anyone with idea what I can do to optimize the delay?
class XMPPConnectionManager {
companion object {
const val JABBER_DOMAIN = "localhost"
const val JABBER_URL = "192.168.1.101"
const val JABBER_RESOURCE = "Resource"
const val JABBER_PORT = 5222
const val CONNECTION_TIMEOUT = 300
const val REPLY_TIMEOUT = 30000L
}
private val conn: XMPPTCPConnection by lazy {
val builder = XMPPTCPConnectionConfiguration.builder()
.setXmppDomain(JidCreate.domainBareFrom(JABBER_DOMAIN))
.setHostAddress(InetSocketAddress(JABBER_URL, JABBER_PORT).address)
.setResource(JABBER_RESOURCE)
.setCompressionEnabled(true)
.setSendPresence(false)
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) //TODO: Enable it
//.enableDefaultDebugger()
.setConnectTimeout(CONNECTION_TIMEOUT)
val connection = XMPPTCPConnection(builder.build())
connection.replyTimeout = REPLY_TIMEOUT
//auto reconnection
val reconnectionManager = ReconnectionManager.getInstanceFor(connection)
reconnectionManager.enableAutomaticReconnection()
reconnectionManager.setReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY)
connection.connect()
return#lazy connection
}
fun login(username: String?, password: String?): Boolean {
try {
if (!conn.isConnected) {
conn.connect()
}
if(!conn.isAuthenticated){
conn.login(username, password)
//Setup OX-IM and other listeners
}
} catch (e: Exception) {
return false
}
return true
}
}
Related
I'm using ktor for the android client but I have an error.
When I run the app for the first time everything is fine and there is no issue, but when I click on the device back button and close the app, and open it again, the app is crashed and I get this error about the ktor:
Parent job is Completed
this is my ktor configure the module:
#InstallIn(SingletonComponent::class)
#Module
object NetworkModule {
private const val TIME_OUT = 60_000
#Singleton
#Provides
fun provideKtor(): HttpClient = HttpClient(Android) {
install(HttpCache)
defaultRequest {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
}
install(ContentNegotiation) {
json(json = Json {
prettyPrint = true
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
})
}
install(HttpTimeout) {
connectTimeoutMillis = TIME_OUT.toLong()
socketTimeoutMillis = TIME_OUT.toLong()
requestTimeoutMillis = TIME_OUT.toLong()
}
install(ResponseObserver) {
onResponse { response ->
Log.d("HttpClientLogger - HTTP status", "${response.status.value}")
Log.d("HttpClientLogger - Response:", response.toString())
}
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.v("Logger Ktor =>", message)
}
}
level = LogLevel.NONE
}
}
}
Note: I use ktor version "2.0.2".
const val ktor_client_core = "io.ktor:ktor-client-core:$ktor_version"
const val ktor_client_cio = "io.ktor:ktor-client-cio:$ktor_version"
const val ktor_serialization_json = "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
const val ktor_serialization = "io.ktor:ktor-client-serialization:$ktor_version"
const val ktor_android = "io.ktor:ktor-client-android:$ktor_version"
const val ktor_negotiation = "io.ktor:ktor-client-content-negotiation:$ktor_version"
const val ktor_okhttp = "io.ktor:ktor-client-okhttp:$ktor_version"
const val ktor_logging = "io.ktor:ktor-client-logging:$ktor_version"
How can i fix it?
I found the reason: This is related to Hilt Di (NetworkModule). I have to use an object instead of hilt module for now
The problem is that you cannot use the same instance of the HttpClient.
companion object {
val client get() = HttpClient(Android) { }
}
Hello am new in Android development I was reading about android bluetooth Here on Android development documentation.
I was able to setup bluetooth, find the bonded device and to connect but am having an issue on transfer data between them
Here is the bluetooth server socket code that listen to bluetooth connection request.
class BluetoothActivity : AppCompatActivity() {
private lateinit var listen: Button
private lateinit var msgBox:TextView
private lateinit var status:TextView
private lateinit var sendButton: Button
private lateinit var writeMsg:EditText
private lateinit var listDevice:Button
private lateinit var listView: ListView
private val handler = Handler()
private var bluetoothDevices = arrayListOf<BluetoothDevice>()
private var deviceName = arrayListOf<String>()
private inner class ServerAcceptThread:Thread(){
private val mmServerSocket:BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE){
bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(myName,myUUID)
}
override fun run() {
//Keep listen until error occured or socket is returned
var shouldKeepListen = true
while (shouldKeepListen){
val socket:BluetoothSocket? = try {
mmServerSocket?.accept()
}catch (e:IOException){
Log.e("bluetoothSocket","ServerSocket failde",e)
shouldKeepListen = false
null
}
if (socket!= null){
val connected = ConnectedThread(socket)
connected.start()
}
}
}
//Close server socket and cause the thread to finish
fun cancel(){
try {
mmServerSocket?.close()
}catch (e:IOException){
Log.e("ConnectionFailed!", "Connection close failed",e)
}
}
}
And down below is the code for Bluetooth client that connect to bluetooth server socket.
private inner class ClientConnectThread(device: BluetoothDevice):Thread(){
private val mmSocket:BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE){
device.createRfcommSocketToServiceRecord(myUUID)
}
public override fun run() {
//Cancel the discovery process because it slow down the connection
bluetoothAdapter?.cancelDiscovery()
mmSocket?.let { socket ->
socket.connect()
}
}
fun cancel(){
try {
mmSocket?.close()
}catch (e:IOException){
Log.e("Socket", "Could not close the client socket",e)
}
}
}
And Then I have bluetooth service that read and write data to send to remote device (client). which take BluetoothSocket as parameter, were the server is listening to
private inner class ConnectedThread(private val mmSocket:BluetoothSocket):Thread(){
private val mmInPutStream:InputStream = mmSocket.inputStream
private val mmOutPutStream:OutputStream = mmSocket.outputStream
private val mmBuffer:ByteArray = ByteArray(1024)
override fun run() {
var numByte:Int //number of bytes returns from read()
//keep listen to the InputStream until an error occured
while (true){
//Read from inputStream
numByte = try {
mmInPutStream.read(mmBuffer)
}catch (e:IOException){
Log.e(TAG,"InputStream was disconnected",e)
break
}
//Send the message to Ui activity
val readMsg = handler.obtainMessage(
MESSAGE_READ,numByte,-1,mmBuffer
)
readMsg.sendToTarget()
}
}
//Call this function to mainActivity to send data to remote device
fun write(byte:ByteArray){
try {
mmOutPutStream.write(byte)
}catch (e:IOException){
Log.e(TAG,"Error occured during send messge",e)
//Send the failed message back to activity
val writeErrorMessage = handler.obtainMessage(MESSAGE_TOAST)
val bundle = Bundle().apply {
putString("Toast","could not send the data")
}
writeErrorMessage.data = bundle
handler.sendMessage(writeErrorMessage)
return
}
//Share the sent message with UI activity
val writtenMsg = handler.obtainMessage(
MESSAGE_WRITE, -1,-1,mmBuffer
)
writtenMsg.sendToTarget()
}
//Call this method to activity to shut socket
fun cancle(){
try {
mmSocket.close()
}catch (e:IOException){
Log.e(TAG,"Connection closed failed!")
}
}
}
}
And I have also implement the listener for UI to start listen to a connection request, list Bonded device and connect to remote device and transfer data through each other.
fun implementsListeners(){
listDevice.setOnClickListener {
val pairedDevice: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
var index:Int = 0
val pairedDevice: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
if (pairedDevice != null){
var listDeviceName = arrayListOf<String>()
try {
pairedDevice.forEachIndexed { index, device ->
listDeviceName.add(index, device.name)
bluetoothDevices.add(device)
}
}catch (e:IndexOutOfBoundsException){
Log.e(TAG, "indexOutOfBond",e)
}
val arrayAdapter:ArrayAdapter<String> = ArrayAdapter(
this,android.R.layout.simple_list_item_1,listDeviceName
)
listView.adapter =arrayAdapter
}
listen.setOnClickListener {
val serverClass = ServerAcceptThread()
serverClass.start()
}
listView.setOnItemClickListener { parent, view, position, id ->
val client = ClientConnectThread(bluetoothDevices[position])
client.start()
status.text = "Connecting..."
}
sendButton.setOnClickListener {
val client = BluetoothService(Handler())
//Call the write() method to write data
}
My Question is how can I access the write() method and read() that is on ConnectedThread. I have tried to Instantiate ConnectedThread but it's take BluetoothSocket as parameter I can't access the socket outside client or server class. Method Any help or suggestion on. I would Appreciate
I have an app that implements the video calling feature using WebRTC in android.
From the App, I am creating an Offer for calls and receiving from another App/ Web.
So, when I am working on app to app, everything is working fine, I can see the remote peer, and the remote peer can also see me.
But when I am working on app (creating offer) to web (receiver). In the app, I can see both local and remote Stream, but from the web, I can't see the remote stream only seeing the remote stream.
This is my android code while creating an offer
val pcConstraints = object : MediaConstraints() {
init {
optional.add(KeyValuePair("DtlsSrtpKeyAgreement", "true"))
optional.add(KeyValuePair("OfferToReceiveAudio", "true"))
optional.add(KeyValuePair("OfferToReceiveVideo", "true"))
}
}
val peerConnection = getOrCreatePeerConnection(mRemoteSocketId, "A")
peerConnection.createOffer(object : CustomSdpObserver() {
override fun onCreateSuccess(sessionDescription: SessionDescription?) {
Timber.d("onCreateSuccess: ")
peerConnection.setLocalDescription(CustomSdpObserver(), sessionDescription)
if (sessionDescription != null) {
// sending sessionDescription from here
}
// starting the call from here
}
}, pcConstraints)
Here Creating the peerConnection
private fun getOrCreatePeerConnection(socketId: String, role: String): PeerConnection {
Timber.tag("live").d("getOrCreatePeerConnection socketId $socketId role $role")
var peerConnection = peerConnectionMap[socketId]
if (peerConnection != null) {
return peerConnection
}
val rtcConfig = PeerConnection.RTCConfiguration(iceServers)
rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE
rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE
rtcConfig.keyType = PeerConnection.KeyType.ECDSA
rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.ALL
rtcConfig.enableCpuOveruseDetection = true
peerConnection =
peerConnectionFactory?.createPeerConnection(
rtcConfig,
pcConstraints,
object : CustomPCObserver() {
override fun onIceCandidate(p0: IceCandidate?) {
super.onIceCandidate(p0)
Timber.tag("live").d("getOrCreatePeerConnection onIceCandidate${p0} ")
if (p0 != null) {
SignalingServer.get()?.sendIceCandidate(p0)
}
}
override fun onAddStream(p0: MediaStream?) {
super.onAddStream(p0)
Timber.tag("live")
.d("onAddStream Remote MediaStream ${p0?.videoTracks?.size} ")
gotRemoteStream(p0!!)
}
override fun onRenegotiationNeeded() {
super.onRenegotiationNeeded()
Timber.tag("live").d("onRenegotiationNeeded")
}
})
peerConnection!!.addStream(localMediaStream)
peerConnectionMap[socketId] = peerConnection
Timber.tag("live")
.d("getOrCreatePeerConnection size $socketId ${peerConnectionMap.size} , ${peerConnectionMap.values} ")
return peerConnection
}
So, what am I missing, I believe in Web end somehow my local Stream is not received. Your help would be highly appreciated.
I need to change DNS on all Android requests. How to get it done?
I found this:
class HTTPDNSInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originRequest = chain.request()
val httpUrl = originRequest.url()
val url = httpUrl.toString()
val host = httpUrl.host()
val hostIP = HttpDNS.getIpByHost(host)
val builder = originRequest.newBuilder()
if (hostIP != null) {
builder.url(HttpDNS.getIpUrl(url, host, hostIP))
builder.header("host", hostIP)
}
val newRequest = builder.build()
val newResponse = chain.proceed(newRequest)
return newResponse
}
}
But, I don't have all the helper classes and I have not found any library where I can explicitly set certain DNS. Even if there is a way to dynamically check valid DNS and setting any working, would be great too.
I am trying to stream video from Raspberry Pi to android device via webrtc. I am using firebase (firestore) as signalling. I am able to run the setup while connected to same wifi but it fails when different networks are being used.
Device - RPI
Client
1) Web client (hosted on firebase)
2) Android App
On same network (wifi) between device and clients, both clients are able to play video and audio.
But when device and client are on different network, web client is able to show video but Android App is not able to show video.
Signalling is working correctly and on device, camera and microphone are started and ice candidates are exchanged successfully. I also get remote stream added (onAddStream called) on android. But no video and audio is playing.
Android PeerConnectionClient
class PeerConnectionClient(private val activity: MainActivity, private val fSignalling: FSignalling) {
internal var isVideoRunning = false
private val rootEglBase by lazy {
EglBase.create()
}
private val peerConnectionFactory: PeerConnectionFactory by lazy {
val initializationOptions = PeerConnectionFactory.InitializationOptions.builder(activity).createInitializationOptions()
PeerConnectionFactory.initialize(initializationOptions)
val options = PeerConnectionFactory.Options()
val defaultVideoEncoderFactory = DefaultVideoEncoderFactory(rootEglBase.eglBaseContext, true, true)
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(rootEglBase.eglBaseContext)
PeerConnectionFactory.builder()
.setOptions(options)
.setVideoEncoderFactory(defaultVideoEncoderFactory)
.setVideoDecoderFactory(defaultVideoDecoderFactory)
.createPeerConnectionFactory()
}
private val iceServersList = mutableListOf("stun:stun.l.google.com:19302")
private var sdpConstraints: MediaConstraints? = null
private var localAudioTrack: AudioTrack? = null
private var localPeer: PeerConnection? = null
private var gotUserMedia: Boolean = false
private var peerIceServers: MutableList<PeerConnection.IceServer> = ArrayList()
init {
peerIceServers.add(PeerConnection.IceServer.builder(iceServersList).createIceServer())
// activity.surface_view.release()
activity.surface_view.init(rootEglBase.eglBaseContext, null)
activity.surface_view.setZOrderMediaOverlay(true)
createPeer()
}
private fun createPeer() {
sdpConstraints = MediaConstraints()
val audioconstraints = MediaConstraints()
val audioSource = peerConnectionFactory.createAudioSource(audioconstraints)
localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource)
gotUserMedia = true
activity.runOnUiThread {
if (localAudioTrack != null) {
createPeerConnection()
// doCall()
}
}
}
/**
* Creating the local peerconnection instance
*/
private fun createPeerConnection() {
val constraints = MediaConstraints()
constraints.mandatory.add(MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"))
constraints.mandatory.add(MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"))
constraints.optional.add(MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"))
val rtcConfig = PeerConnection.RTCConfiguration(peerIceServers)
// TCP candidates are only useful when connecting to a server that supports
// ICE-TCP.
rtcConfig.enableDtlsSrtp = true
rtcConfig.enableRtpDataChannel = true
// rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED
// rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE
// rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE
// rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY
// Use ECDSA encryption.
// rtcConfig.keyType = PeerConnection.KeyType.ECDSA
localPeer = peerConnectionFactory.createPeerConnection(rtcConfig, constraints, object : PeerObserver {
override fun onIceCandidate(p0: IceCandidate) {
super.onIceCandidate(p0)
onIceCandidateReceived(p0)
}
override fun onAddStream(p0: MediaStream) {
activity.showToast("Received Remote stream")
super.onAddStream(p0)
gotRemoteStream(p0)
}
})
addStreamToLocalPeer()
}
/**
* Adding the stream to the localpeer
*/
private fun addStreamToLocalPeer() {
//creating local mediastream
val stream = peerConnectionFactory.createLocalMediaStream("102")
stream.addTrack(localAudioTrack)
localPeer!!.addStream(stream)
}
/**
* This method is called when the app is initiator - We generate the offer and send it over through socket
* to remote peer
*/
/*private fun doCall() {
localPeer!!.createOffer(object : mySdpObserver {
override fun onCreateSuccess(p0: SessionDescription) {
super.onCreateSuccess(p0)
localPeer!!.setLocalDescription(object: mySdpObserver {}, p0)
Log.d("onCreateSuccess", "SignallingClient emit ")
}
}, sdpConstraints)
}*/
private fun onIceCandidateReceived(iceCandidate: IceCandidate) {
//we have received ice candidate. We can set it to the other peer.
if (localPeer == null) {
return
}
val message = JSONObject()
message.put("type", "candidate")
message.put("label", iceCandidate.sdpMLineIndex)
message.put("id", iceCandidate.sdpMid)
message.put("candidate", iceCandidate.serverUrl)
fSignalling.doSignalingSend(message.toString())
}
private fun gotRemoteStream(stream: MediaStream) {
isVideoRunning = true
//we have remote video stream. add to the renderer.
val videoTrack = stream.videoTracks[0]
videoTrack.setEnabled(true)
activity.runOnUiThread {
try {
// val remoteRenderer = VideoRenderer(surface_view)
activity.surface_view.visibility = View.VISIBLE
// videoTrack.addRenderer(remoteRenderer)
videoTrack.addSink(activity.surface_view)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun onReceivePeerMessage(data: JSONObject) {
if (data.getString("type") == "offer") {
// val sdpReturned = SdpUtils.forceChosenVideoCodec(data.getString("sdp"), "H264")
val sdpReturned = data.getString("sdp")
// data.remove("sdp")
// data.put("sdp", sdpReturned)
val sessionDescription = SessionDescription(SessionDescription.Type.OFFER, sdpReturned)
localPeer?.setRemoteDescription(object: mySdpObserver { }, sessionDescription)
localPeer?.createAnswer(object : mySdpObserver {
override fun onCreateSuccess(p0: SessionDescription) {
super.onCreateSuccess(p0)
localPeer!!.setLocalDescription( object : mySdpObserver {}, p0)
val description = JSONObject()
description.put("type", p0.type.canonicalForm())
description.put("sdp", p0.description)
this#PeerConnectionClient.fSignalling.doSignalingSend(description.toString())
}
override fun onCreateFailure(p0: String) {
super.onCreateFailure(p0)
activity.showToast("Failed to create answer")
}
}, MediaConstraints())
} else if (data.getString("type") == "candidate") {
val iceCandidates = IceCandidate(data.getString("id"), data.getInt("label"), data.getString("candidate"))
localPeer?.addIceCandidate(iceCandidates)
}
}
internal fun close() {
isVideoRunning = false
localPeer?.close()
localPeer = null
}
}
I am under the impression that if web client is able to display video on different network (mobile hotspot), android client on same internet used by web client should be able to display video as well. Is it wrong?
Why won't android display video (onAddStream is called)
Is it required to use Turn server? My assumption again is the if web client works, so should android. The service i am using on RPI do not have support for turn server.
Additional info:
Device is behind double natted ISP (i guess) (but since web client can connect, it won't be an issue i guess).
I have found a solution to the issue
I was using
private fun onIceCandidateReceived(iceCandidate: IceCandidate) {
//we have received ice candidate. We can set it to the other peer.
if (localPeer == null) {
return
}
val message = JSONObject()
message.put("type", "candidate")
message.put("label", iceCandidate.sdpMLineIndex)
message.put("id", iceCandidate.sdpMid)
message.put("candidate", iceCandidate.serverUrl)
fSignalling.doSignalingSend(message.toString())
}
Instead was required to use
message.put("candidate", iceCandidate.sdp) // iceCandidate.serverUrl)