File transfer not working on mobile connection, only on wifi

Hi there,

I faced a problem with FileTransfer on mobile internet connections. I am able to send a file without problems over wifi but I can´t send the file over full and stable mobile connection. The transfer status allways shows -1 and seem to loop forever. Also, receiving usual messages is not possible anymore, but sending works. There is no exception thrown, wether at the file transfer nor on sending/receiving usual messages. So I guess the file transfer is blocking something and maybe destroys the connection. But by testing, connection and authentication are ok.

I tried it with both methods, sendStream() and sendFile(), both methods have the same behaviour. I do this stuff in an AsyncTask() in doInBackground().

Here my part of code:

@Override
protected Void doInBackground(Void… params) {

mOutgoingFileTransfer = mFileTransferManager.createOutgoingFileTransfer(toUserJid);

File file = new File(filePath);

String fileName = file.getName();

//also tried with sendStream(), same behaviour

// byte[] sdataToSend = FileUtils.FileToByte(file);

// mOutgoingFileTransfer.sendStream(new ByteArrayInputStream(dataToSend), fileName, dataToSend.length, subject + “, I sent the file:” + fileName);
try {

Log.d(“TEST”, “SEND FILE”);

mOutgoingFileTransfer.sendFile(file, “TEST”);

} catch (SmackException e) {

Log.d(“TEST”, " SEND FILE ERROR:" + e);

e.printStackTrace();

}

streamId = mOutgoingFileTransfer.getStreamID();

while (!mOutgoingFileTransfer.isDone()) {

switch (mOutgoingFileTransfer.getStatus()) {

case error:

Log.d(“TEST”, “OUTGOING FILE ERROR:” + mOutgoingFileTransfer.getError());

break;

default:

//allways get -1 at mobile connection. On wifi it shows the bytes that are sent
Log.d(“TEST”, “OUTGOING FILE” + " bytes:" + mOutgoingFileTransfer.getBytesSent());

break;

}

return null;

}

}.execute();

My question is:

  1. why is File Transfer blocking the receiving of usual messages?

  2. Is it because of the wrong port? I send the fileTransfer over the default port 5222 (or any that is used by the server) and it works without problem on wifi connection.

  3. If the port is the problem, so how to switch between ports (normal port 5222 and transfer port 7777)while user is connected?

For information: Sending files and sending/receiving messages are implemented separated, they just use the same XMPPTCPConnection. They use different tasks while messages are send over the connection with sendStanza(), Files are send via the FileTransferManager with OutgoingFileTransfer.

Is there anyone who has an idea about that?

many thanks for some suggestions…

EDIT


Turned out that outgoingFileTransfer.isDone() is never comes up, so the while loop consumes every resource and the messages could not be received. BUT the problem still is there, on mobile connection, no file transfer. There is a 0KB sized file on the receiver side, so anything is trying to come in, but does not work.

Nachricht geändert durch Stefan hennemann

After setting the FileTransferNegotiator.IBB_ONLY to true before creating the OutgoingFileTransfer, also with a mobile connection it works. I can´t understand this, as recommended in the Smack API:

A static variable to use only offer IBB for file transfer. It is generally recommend to only set this variable to true for testing purposes as IBB is the backup file transfer method and shouldn’t be used as the only transfer method in production systems.

So, if we don´t have an alternative if transfer goes over mobile connection, how should we handle this instead? And what´s the reason behind that?

Seems like it first tries to do the file-transfer over Socks5, but for some reason it fails and it also does not fallback to IBB then.

Might be related to this issue:

[SMACK-584] Smack FileTransfer does not fallback to IBB if SOCKS5 negotation fails and recipient is XEP-0095 conform - I…

Yeah, I also seen this post, but thought it is fixed about two years later, but obviously not.

SI file transfer IBB fallback is not implemented because it’s not possible with the legacy SI XEPs, but the Jingle XEPs define it properly.

Besides that, I recommend to read Smack XMPP File Transfer · igniterealtime/Smack Wiki · GitHub

I am pretty sure, it is implemented, intended to work and even documented [1], but just doesn’t work properly in some cases. (that’s why I created the story). I’ve debugged it, I think the class is called FaultTolerantNegotiator.

I agree, that SI XEPs don’t properly specify the fallback mechanism, but on the other hand they somewhat imply it:

In order to enable seamless file transfer and appropriate fall-back mechanisms, implementations of this profile MUST support both SOCKS5 Bytestreams (XEP-0065) [4] and In-Band Bytestreams (XEP-0047) [5], to be preferred in that order.

[1]: Ignite Realtime: IM File Transfer Made Easy

If one of the other two file transfer mechanisms fails, Spark will default to sending the file “in-band”

That mechanism is using an incompatible non-standard way to announce multiple file transfer mechs for legacy SI XMPP file transfer. See also my comment here [SMACK-584] Smack FileTransfer does not fallback to IBB if SOCKS5 negotation fails and recipient is XEP-0095 conform - I… and SMACK-561.

You can only perform proper low protocol level IBB fallback with Jingle, not with the legacy file transfer XEPs.

What about this approach?:

  • Sender initiates the stream (offer S5B and IBB)

  • Receiver chooses S5B (correctly as single-list), but also knows, that the sender supports IBB and prepares for that.

  • Sender offers S5B stream hosts.

  • Receiver cannot connect to any host and returns item-not-found error to the sender. (as per XEP-0065 Example 16)

  • Sender then opens IBB session.

  • Receiver is prepared for it, because it knows that sender supports IBB and can accept the stream over IBB.

I think FaultTolerantNegotiator almost works this way, but only tries to open IBB session (as sender) if receiver advertises it (wrongly in multi-list). Instead it could just ALWAYS open IBB in the item-not-found (and maybe timeout) case, because it can expect that the receiver understands it (XEP-0096: “MUST support IBB”) (and if not, it doens’t harm).

=> No violation of the specs, but still works.

That’s how I’ve interpreted the SI XEPs and implemented it in my library.

The problem with this approach is that some implementations consider the whole file transfer failed after the item-not-found, so they won’t accept the initiation request of the IBB fallback. I think you need to start again from 1. but only offer IBB this time. This of course means that the receiving user will see another “incoming file transfer dialog”. That’s why I could imagine Smack could automatically do for you, eventually adding a human readable message to the IBB fallback initiation request, informing the receiving user that this second request is the IBB fallback.

While I do not oppose any standard compliant improvement to Smack’s SI File Transfer code, I’d personally rather focus adding support for Jingle file transfer to Smack, where IBB fallback is properly standardized: XEP-0260: Jingle SOCKS5 Bytestreams Transport Method and XEP-0166: Jingle

The problem with this approach is that some implementations consider the whole file transfer failed after the item-not-found, so they won’t accept the initiation request of the IBB fallback.

Probably, but at least the sender tried everything to make it work (and it probably will work for the other some implementations).

I think you need to start again from 1. but only offer IBB this time. This of course means that the receiving user will see another “incoming file transfer dialog”. That’s why I could imagine Smack could automatically do for you, eventually adding a human readable message to the IBB fallback initiation request, informing the receiving user that this second request is the IBB fallback.

Sounds like something to be discussed on the XMPP standards list. Creating a whole new SI request would probably lead to bad user experience in many clients for the reason you mentioned (new file dialog). Silently offer IBB using the same SI session (and without creating a new SI request) and hoping the client can handle it (theoretically it should) feels like the better option.

I think Spark works this way, just with a minor spec violation (answering multiple stream methods in list-single).

Sounds like something to be discussed on the XMPP standards list.
Sure, you could try that. But I wouldn’t count on much movement given that fallback has been properly defined in Jingle.