From 08023210ba875f3cd088eca2b4b3df7410966344 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 10 Mar 2014 19:22:13 +0100 Subject: [PATCH 1/4] basic stream managment functionality --- .../siacs/conversations/crypto/OtrEngine.java | 2 +- .../conversations/entities/MucOptions.java | 2 +- .../services/XmppConnectionService.java | 70 +++++---- .../ui/ConversationFragment.java | 4 +- .../conversations/utils/MessageParser.java | 2 +- src/eu/siacs/conversations/xml/TagWriter.java | 59 +++++--- .../xmpp/OnIqPacketReceived.java | 1 + .../xmpp/OnMessagePacketReceived.java | 1 + .../xmpp/OnPresencePacketReceived.java | 1 + .../conversations/xmpp/PresencePacket.java | 13 -- .../conversations/xmpp/XmppConnection.java | 143 ++++++++++++++---- .../xmpp/stanzas/AbstractStanza.java | 34 +++++ .../xmpp/{ => stanzas}/IqPacket.java | 9 +- .../xmpp/{ => stanzas}/MessagePacket.java | 24 +-- .../xmpp/stanzas/PresencePacket.java | 9 ++ .../xmpp/stanzas/streammgmt/AckPacket.java | 13 ++ .../xmpp/stanzas/streammgmt/EnablePacket.java | 13 ++ .../stanzas/streammgmt/RequestPacket.java | 12 ++ .../xmpp/stanzas/streammgmt/ResumePacket.java | 14 ++ 19 files changed, 303 insertions(+), 123 deletions(-) delete mode 100644 src/eu/siacs/conversations/xmpp/PresencePacket.java create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java rename src/eu/siacs/conversations/xmpp/{ => stanzas}/IqPacket.java (73%) rename src/eu/siacs/conversations/xmpp/{ => stanzas}/MessagePacket.java (75%) create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java create mode 100644 src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java diff --git a/src/eu/siacs/conversations/crypto/OtrEngine.java b/src/eu/siacs/conversations/crypto/OtrEngine.java index 23af94e7..300de735 100644 --- a/src/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/eu/siacs/conversations/crypto/OtrEngine.java @@ -20,7 +20,7 @@ import android.util.Log; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.MessagePacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import net.java.otr4j.OtrEngineHost; import net.java.otr4j.OtrException; diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java index d04fea16..3ea7f4f6 100644 --- a/src/eu/siacs/conversations/entities/MucOptions.java +++ b/src/eu/siacs/conversations/entities/MucOptions.java @@ -5,7 +5,7 @@ import java.util.List; import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.PresencePacket; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import android.annotation.SuppressLint; import android.util.Log; diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 5adfa981..de261dbe 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -35,15 +35,15 @@ import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener; import eu.siacs.conversations.utils.PhoneHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.IqPacket; -import eu.siacs.conversations.xmpp.MessagePacket; import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; -import eu.siacs.conversations.xmpp.PresencePacket; import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; @@ -126,7 +126,9 @@ public class XmppConnectionService extends Service { || (packet.hasChild("sent"))) { message = MessageParser.parseCarbonMessage(packet, account, service); - message.getConversation().markRead(); + if (message!=null) { + message.getConversation().markRead(); + } notify = false; } @@ -195,13 +197,6 @@ public class XmppConnectionService extends Service { if (convChangedListener != null) { convChangedListener.onConversationListChanged(); } - if (account.getKeys().has("pgp_signature")) { - try { - sendPgpPresence(account, account.getKeys().getString("pgp_signature")); - } catch (JSONException e) { - // - } - } scheduleWakeupCall(PING_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { Log.d(LOGTAG,"onStatusChanged offline"); @@ -445,7 +440,7 @@ public class XmppConnectionService extends Service { super.onDestroy(); for (Account account : accounts) { if (account.getXmppConnection() != null) { - disconnect(account); + disconnect(account,true); } } } @@ -864,7 +859,7 @@ public class XmppConnectionService extends Service { public void deleteAccount(Account account) { Log.d(LOGTAG, "called delete account"); if (account.getXmppConnection() != null) { - this.disconnect(account); + this.disconnect(account,false); } databaseBackend.deleteAccount(account); this.accounts.remove(account); @@ -954,7 +949,7 @@ public class XmppConnectionService extends Service { packet.setAttribute("to", conversation.getContactJid().split("/")[0]+"/"+nick); packet.setAttribute("from", conversation.getAccount().getFullJid()); - packet = conversation.getAccount().getXmppConnection().sendPresencePacket(packet, new OnPresencePacketReceived() { + conversation.getAccount().getXmppConnection().sendPresencePacket(packet, new OnPresencePacketReceived() { @Override public void onPresencePacketReceived(Account account, PresencePacket packet) { @@ -992,7 +987,7 @@ public class XmppConnectionService extends Service { conversation.getMucOptions().setOffline(); } - public void disconnect(Account account) { + public void disconnect(final Account account, boolean blocking) { List conversations = getConversations(); for (int i = 0; i < conversations.size(); i++) { Conversation conversation = conversations.get(i); @@ -1009,9 +1004,21 @@ public class XmppConnectionService extends Service { } } } - account.getXmppConnection().disconnect(); - Log.d(LOGTAG, "disconnected account: " + account.getJid()); - account.setXmppConnection(null); + if (!blocking) { + new Thread(new Runnable() { + + @Override + public void run() { + account.getXmppConnection().disconnect(false); + Log.d(LOGTAG, "disconnected account: " + account.getJid()); + account.setXmppConnection(null); + } + }).start(); + } else { + account.getXmppConnection().disconnect(false); + Log.d(LOGTAG, "disconnected account: " + account.getJid()); + account.setXmppConnection(null); + } } @Override @@ -1134,20 +1141,27 @@ public class XmppConnectionService extends Service { this.tlsException = null; } - public void reconnectAccount(Account account) { - if (account.getXmppConnection() != null) { - disconnect(account); - } - if (!account.isOptionSet(Account.OPTION_DISABLED)) { - if (account.getXmppConnection()==null) { - account.setXmppConnection(this.createConnection(account)); + public void reconnectAccount(final Account account) { + new Thread(new Runnable() { + + @Override + public void run() { + if (account.getXmppConnection() != null) { + disconnect(account,true); + } + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + if (account.getXmppConnection()==null) { + account.setXmppConnection(createConnection(account)); + } + Thread thread = new Thread(account.getXmppConnection()); + thread.start(); + } } - Thread thread = new Thread(account.getXmppConnection()); - thread.start(); - } + }).start(); } public void ping(final Account account,final int timeout) { + account.getXmppConnection().r(); Log.d(LOGTAG,account.getJid()+": sending ping"); IqPacket iq = new IqPacket(IqPacket.TYPE_GET); Element ping = new Element("ping"); diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index b49106f1..3373479a 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -332,6 +332,9 @@ public class ConversationFragment extends Fragment { final ConversationActivity activity = (ConversationActivity) getActivity(); activity.registerListener(); this.conversation = activity.getSelectedConversation(); + if (this.conversation==null) { + return; + } this.selfBitmap = findSelfPicture(); updateMessages(); // rendering complete. now go tell activity to close pane @@ -374,7 +377,6 @@ public class ConversationFragment extends Fragment { public void updateMessages() { ConversationActivity activity = (ConversationActivity) getActivity(); List encryptedMessages = new LinkedList(); - // TODO this.conversation could be null?! for(Message message : this.conversation.getMessages()) { if (message.getEncryption() == Message.ENCRYPTION_PGP) { encryptedMessages.add(message); diff --git a/src/eu/siacs/conversations/utils/MessageParser.java b/src/eu/siacs/conversations/utils/MessageParser.java index 614683a2..2da0459e 100644 --- a/src/eu/siacs/conversations/utils/MessageParser.java +++ b/src/eu/siacs/conversations/utils/MessageParser.java @@ -11,7 +11,7 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.xmpp.MessagePacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public class MessageParser { diff --git a/src/eu/siacs/conversations/xml/TagWriter.java b/src/eu/siacs/conversations/xml/TagWriter.java index 109078ca..15ad385f 100644 --- a/src/eu/siacs/conversations/xml/TagWriter.java +++ b/src/eu/siacs/conversations/xml/TagWriter.java @@ -5,25 +5,29 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.concurrent.LinkedBlockingQueue; -import android.util.Log; +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; public class TagWriter { private OutputStreamWriter outputStream; - private LinkedBlockingQueue writeQueue = new LinkedBlockingQueue(); - private Thread writer = new Thread() { - public boolean shouldStop = false; + private boolean finshed = false; + private LinkedBlockingQueue writeQueue = new LinkedBlockingQueue(); + private Thread asyncStanzaWriter = new Thread() { + private boolean shouldStop = false; @Override public void run() { while(!shouldStop) { + if ((finshed)&&(writeQueue.size() == 0)) { + return; + } try { - String output = writeQueue.take(); - outputStream.write(output); + AbstractStanza output = writeQueue.take(); + outputStream.write(output.toString()); outputStream.flush(); } catch (IOException e) { - Log.d("xmppService", "error writing to stream"); + shouldStop = true; } catch (InterruptedException e) { - + shouldStop = true; } } } @@ -31,34 +35,49 @@ public class TagWriter { public TagWriter() { - } public TagWriter(OutputStream out) { this.setOutputStream(out); - writer.start(); } public void setOutputStream(OutputStream out) { this.outputStream = new OutputStreamWriter(out); - if (!writer.isAlive()) writer.start(); } - public TagWriter beginDocument() { - writeQueue.add(""); + public TagWriter beginDocument() throws IOException { + outputStream.write(""); + outputStream.flush(); return this; } - public TagWriter writeTag(Tag tag) { - writeQueue.add(tag.toString()); + public TagWriter writeTag(Tag tag) throws IOException { + outputStream.write(tag.toString()); + outputStream.flush(); return this; } - public void writeString(String string) { - writeQueue.add(string); + public TagWriter writeElement(Element element) throws IOException { + outputStream.write(element.toString()); + outputStream.flush(); + return this; } - - public void writeElement(Element element) { - writeQueue.add(element.toString()); + + public TagWriter writeStanzaAsync(AbstractStanza stanza) { + if (finshed) { + return this; + } else { + if (!asyncStanzaWriter.isAlive()) asyncStanzaWriter.start(); + writeQueue.add(stanza); + return this; + } + } + + public void finish() { + this.finshed = true; + } + + public boolean finished() { + return (this.writeQueue.size() == 0); } } diff --git a/src/eu/siacs/conversations/xmpp/OnIqPacketReceived.java b/src/eu/siacs/conversations/xmpp/OnIqPacketReceived.java index 54909f88..a4cff986 100644 --- a/src/eu/siacs/conversations/xmpp/OnIqPacketReceived.java +++ b/src/eu/siacs/conversations/xmpp/OnIqPacketReceived.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; public interface OnIqPacketReceived extends PacketReceived { public void onIqPacketReceived(Account account, IqPacket packet); diff --git a/src/eu/siacs/conversations/xmpp/OnMessagePacketReceived.java b/src/eu/siacs/conversations/xmpp/OnMessagePacketReceived.java index 6f0b387d..325e945f 100644 --- a/src/eu/siacs/conversations/xmpp/OnMessagePacketReceived.java +++ b/src/eu/siacs/conversations/xmpp/OnMessagePacketReceived.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public interface OnMessagePacketReceived extends PacketReceived { public void onMessagePacketReceived(Account account, MessagePacket packet); diff --git a/src/eu/siacs/conversations/xmpp/OnPresencePacketReceived.java b/src/eu/siacs/conversations/xmpp/OnPresencePacketReceived.java index 55672fff..95c1acfc 100644 --- a/src/eu/siacs/conversations/xmpp/OnPresencePacketReceived.java +++ b/src/eu/siacs/conversations/xmpp/OnPresencePacketReceived.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public interface OnPresencePacketReceived extends PacketReceived { public void onPresencePacketReceived(Account account, PresencePacket packet); diff --git a/src/eu/siacs/conversations/xmpp/PresencePacket.java b/src/eu/siacs/conversations/xmpp/PresencePacket.java deleted file mode 100644 index 3d77ce15..00000000 --- a/src/eu/siacs/conversations/xmpp/PresencePacket.java +++ /dev/null @@ -1,13 +0,0 @@ -package eu.siacs.conversations.xmpp; - -import eu.siacs.conversations.xml.Element; - -public class PresencePacket extends Element { - private PresencePacket(String name) { - super("presence"); - } - - public PresencePacket() { - super("presence"); - } -} diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 53f9b85a..2ffe21fb 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -26,8 +26,10 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; +import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; +import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.os.PowerManager; import android.util.Log; @@ -38,6 +40,14 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.XmlReader; +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; +import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket; +import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket; +import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket; +import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; public class XmppConnection implements Runnable { @@ -56,6 +66,11 @@ public class XmppConnection implements Runnable { private boolean shouldAuthenticate = true; private Element streamFeatures; private HashSet discoFeatures = new HashSet(); + + private String streamId = null; + + private int stanzasReceived = 0; + private int stanzasSent = 0; private static final int PACKET_IQ = 0; private static final int PACKET_MESSAGE = 1; @@ -176,6 +191,34 @@ public class XmppConnection implements Runnable { } else if (nextTag.isStart("failure")) { Element failure = tagReader.readElement(nextTag); changeStatus(Account.STATUS_UNAUTHORIZED); + } else if (nextTag.isStart("enabled")) { + this.stanzasSent = 0; + Element enabled = tagReader.readElement(nextTag); + if ("true".equals(enabled.getAttribute("resume"))) { + this.streamId = enabled.getAttribute("id"); + Log.d(LOGTAG,account.getJid()+": stream managment enabled (resumable)"); + } else { + Log.d(LOGTAG,account.getJid()+": stream managment enabled"); + } + this.stanzasReceived = 0; + RequestPacket r = new RequestPacket(); + tagWriter.writeStanzaAsync(r); + } else if (nextTag.isStart("resumed")) { + tagReader.readElement(nextTag); + changeStatus(Account.STATUS_ONLINE); + Log.d(LOGTAG,account.getJid()+": session resumed"); + } else if (nextTag.isStart("r")) { + tagReader.readElement(nextTag); + AckPacket ack = new AckPacket(this.stanzasReceived); + //Log.d(LOGTAG,ack.toString()); + tagWriter.writeStanzaAsync(ack); + } else if (nextTag.isStart("a")) { + Element ack = tagReader.readElement(nextTag); + int serverSequence = Integer.parseInt(ack.getAttribute("h")); + if (serverSequence>this.stanzasSent) { + this.stanzasSent = serverSequence; + } + //Log.d(LOGTAG,"server ack"+ack.toString()+" ("+this.stanzasSent+")"); } else if (nextTag.isStart("iq")) { processIq(nextTag); } else if (nextTag.isStart("message")) { @@ -221,6 +264,7 @@ public class XmppConnection implements Runnable { } nextTag = tagReader.readTag(); } + ++stanzasReceived; return element; } @@ -271,7 +315,7 @@ public class XmppConnection implements Runnable { } } - private void sendStartTLS() { + private void sendStartTLS() throws IOException { Tag startTLS = Tag.empty("starttls"); startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"); tagWriter.writeTag(startTLS); @@ -378,25 +422,45 @@ public class XmppConnection implements Runnable { } else if (this.streamFeatures.hasChild("mechanisms") && shouldAuthenticate) { sendSaslAuth(); - } - if (this.streamFeatures.hasChild("bind") && shouldBind) { + } else if (this.streamFeatures.hasChild("sm") && streamId != null) { + Log.d(LOGTAG,"found old stream id. trying to remuse"); + ResumePacket resume = new ResumePacket(this.streamId,stanzasReceived); + this.tagWriter.writeStanzaAsync(resume); + } else if (this.streamFeatures.hasChild("bind") && shouldBind) { sendBindRequest(); if (this.streamFeatures.hasChild("session")) { + Log.d(LOGTAG,"sending session"); IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); Element session = new Element("session"); session.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session"); session.setContent(""); startSession.addChild(session); - sendIqPacket(startSession, null); - tagWriter.writeElement(startSession); + this.sendIqPacket(startSession, null); } - Element presence = new Element("presence"); - - tagWriter.writeElement(presence); } } + private void sendInitialPresence() { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("from", account.getFullJid()); + if (account.getKeys().has("pgp_signature")) { + try { + String signature = account.getKeys().getString("pgp_signature"); + Element status = new Element("status"); + status.setContent("online"); + packet.addChild(status); + Element x = new Element("x"); + x.setAttribute("xmlns", "jabber:x:signed"); + x.setContent(signature); + packet.addChild(x); + } catch (JSONException e) { + // + } + } + this.sendPresencePacket(packet); + } + private void sendBindRequest() throws IOException { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); Element bind = new Element("bind"); @@ -412,10 +476,15 @@ public class XmppConnection implements Runnable { .getContent().split("/")[1]; account.setResource(resource); account.setStatus(Account.STATUS_ONLINE); + if (streamFeatures.hasChild("sm")) { + EnablePacket enable = new EnablePacket(); + tagWriter.writeStanzaAsync(enable); + } + sendInitialPresence(); + sendServiceDiscovery(); if (statusListener != null) { statusListener.onStatusChanged(account); } - sendServiceDiscovery(); } }); } @@ -471,7 +540,7 @@ public class XmppConnection implements Runnable { Log.d(LOGTAG, "processStreamError"); } - private void sendStartStream() { + private void sendStartStream() throws IOException { Tag stream = Tag.start("stream:stream"); stream.setAttribute("from", account.getJid()); stream.setAttribute("to", account.getServer()); @@ -489,39 +558,36 @@ public class XmppConnection implements Runnable { public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) { String id = nextRandomId(); packet.setAttribute("id", id); - tagWriter.writeElement(packet); - if (callback != null) { - packetCallbacks.put(id, callback); - } + this.sendPacket(packet, callback); } public void sendMessagePacket(MessagePacket packet) { - this.sendMessagePacket(packet, null); + this.sendPacket(packet, null); } public void sendMessagePacket(MessagePacket packet, OnMessagePacketReceived callback) { - String id = nextRandomId(); - packet.setAttribute("id", id); - tagWriter.writeElement(packet); - if (callback != null) { - packetCallbacks.put(id, callback); - } + this.sendPacket(packet, callback); } public void sendPresencePacket(PresencePacket packet) { - this.sendPresencePacket(packet, null); + this.sendPacket(packet, null); } - public PresencePacket sendPresencePacket(PresencePacket packet, + public void sendPresencePacket(PresencePacket packet, OnPresencePacketReceived callback) { - String id = nextRandomId(); - packet.setAttribute("id", id); - tagWriter.writeElement(packet); + this.sendPacket(packet, callback); + } + + private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) { + ++stanzasSent; + tagWriter.writeStanzaAsync(packet); if (callback != null) { - packetCallbacks.put(id, callback); + if (packet.getId()==null) { + packet.setId(nextRandomId()); + } + packetCallbacks.put(packet.getId(), callback); } - return packet; } public void setOnMessagePacketReceivedListener( @@ -547,8 +613,23 @@ public class XmppConnection implements Runnable { this.tlsListener = listener; } - public void disconnect() { + public void disconnect(boolean force) { + Log.d(LOGTAG,"disconnecting"); + try { + if (force) { + socket.close(); + } + tagWriter.finish(); + while(!tagWriter.finished()) { + Log.d(LOGTAG,"not yet finished"); + Thread.sleep(100); + } tagWriter.writeTag(Tag.end("stream:stream")); + } catch (IOException e) { + Log.d(LOGTAG,"io exception during disconnect"); + } catch (InterruptedException e) { + Log.d(LOGTAG,"interupted while waiting for disconnect"); + } } public boolean hasFeatureRosterManagment() { @@ -558,4 +639,8 @@ public class XmppConnection implements Runnable { return this.streamFeatures.hasChild("ver"); } } + + public void r() { + this.tagWriter.writeStanzaAsync(new RequestPacket()); + } } diff --git a/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java new file mode 100644 index 00000000..204a6bec --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -0,0 +1,34 @@ +package eu.siacs.conversations.xmpp.stanzas; + +import eu.siacs.conversations.xml.Element; + +public class AbstractStanza extends Element { + + protected AbstractStanza(String name) { + super(name); + } + + public String getTo() { + return getAttribute("to"); + } + + public String getFrom() { + return getAttribute("from"); + } + + public String getId() { + return this.getAttribute("id"); + } + + public void setTo(String to) { + setAttribute("to", to); + } + + public void setFrom(String from) { + setAttribute("from",from); + } + + public void setId(String id) { + setAttribute("id",id); + } +} diff --git a/src/eu/siacs/conversations/xmpp/IqPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java similarity index 73% rename from src/eu/siacs/conversations/xmpp/IqPacket.java rename to src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java index 2319fd28..1675d19d 100644 --- a/src/eu/siacs/conversations/xmpp/IqPacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/IqPacket.java @@ -1,8 +1,7 @@ -package eu.siacs.conversations.xmpp; +package eu.siacs.conversations.xmpp.stanzas; -import eu.siacs.conversations.xml.Element; -public class IqPacket extends Element { +public class IqPacket extends AbstractStanza { public static final int TYPE_SET = 0; public static final int TYPE_RESULT = 1; @@ -33,8 +32,4 @@ public class IqPacket extends Element { super("iq"); } - public String getId() { - return this.getAttribute("id"); - } - } diff --git a/src/eu/siacs/conversations/xmpp/MessagePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java similarity index 75% rename from src/eu/siacs/conversations/xmpp/MessagePacket.java rename to src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index a014155f..f31a78e6 100644 --- a/src/eu/siacs/conversations/xmpp/MessagePacket.java +++ b/src/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -1,30 +1,18 @@ -package eu.siacs.conversations.xmpp; +package eu.siacs.conversations.xmpp.stanzas; import eu.siacs.conversations.xml.Element; -public class MessagePacket extends Element { +public class MessagePacket extends AbstractStanza { public static final int TYPE_CHAT = 0; public static final int TYPE_UNKNOWN = 1; public static final int TYPE_NO = 2; public static final int TYPE_GROUPCHAT = 3; public static final int TYPE_ERROR = 4; - - private MessagePacket(String name) { - super(name); - } public MessagePacket() { super("message"); } - public String getTo() { - return getAttribute("to"); - } - - public String getFrom() { - return getAttribute("from"); - } - public String getBody() { Element body = this.findChild("body"); if (body!=null) { @@ -34,14 +22,6 @@ public class MessagePacket extends Element { } } - public void setTo(String to) { - setAttribute("to", to); - } - - public void setFrom(String from) { - setAttribute("from",from); - } - public void setBody(String text) { this.children.remove(findChild("body")); Element body = new Element("body"); diff --git a/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java new file mode 100644 index 00000000..dfbab78c --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/PresencePacket.java @@ -0,0 +1,9 @@ +package eu.siacs.conversations.xmpp.stanzas; + + +public class PresencePacket extends AbstractStanza { + + public PresencePacket() { + super("presence"); + } +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java new file mode 100644 index 00000000..7bc66e9b --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/AckPacket.java @@ -0,0 +1,13 @@ +package eu.siacs.conversations.xmpp.stanzas.streammgmt; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class AckPacket extends AbstractStanza { + + public AckPacket(int sequence) { + super("a"); + this.setAttribute("xmlns","urn:xmpp:sm:3"); + this.setAttribute("h", ""+sequence); + } + +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java new file mode 100644 index 00000000..ae6a513a --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/EnablePacket.java @@ -0,0 +1,13 @@ +package eu.siacs.conversations.xmpp.stanzas.streammgmt; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class EnablePacket extends AbstractStanza { + + public EnablePacket() { + super("enable"); + this.setAttribute("xmlns","urn:xmpp:sm:3"); + this.setAttribute("resume", "true"); + } + +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java new file mode 100644 index 00000000..4d409fd2 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/RequestPacket.java @@ -0,0 +1,12 @@ +package eu.siacs.conversations.xmpp.stanzas.streammgmt; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class RequestPacket extends AbstractStanza { + + public RequestPacket() { + super("r"); + this.setAttribute("xmlns","urn:xmpp:sm:3"); + } + +} diff --git a/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java new file mode 100644 index 00000000..bebfa1a2 --- /dev/null +++ b/src/eu/siacs/conversations/xmpp/stanzas/streammgmt/ResumePacket.java @@ -0,0 +1,14 @@ +package eu.siacs.conversations.xmpp.stanzas.streammgmt; + +import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; + +public class ResumePacket extends AbstractStanza { + + public ResumePacket(String id, int sequence) { + super("resume"); + this.setAttribute("xmlns","urn:xmpp:sm:3"); + this.setAttribute("previd", id); + this.setAttribute("h", ""+sequence); + } + +} From 0d516b060354f577ff52ce2d3bb73f56557b87d1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 11 Mar 2014 15:44:22 +0100 Subject: [PATCH 2/4] ping stuff --- .../conversations/entities/MucOptions.java | 6 +- .../conversations/services/EventReceiver.java | 9 +- .../services/XmppConnectionService.java | 387 ++++++++++-------- .../siacs/conversations/ui/XmppActivity.java | 3 +- .../conversations/xmpp/XmppConnection.java | 23 +- 5 files changed, 240 insertions(+), 188 deletions(-) diff --git a/src/eu/siacs/conversations/entities/MucOptions.java b/src/eu/siacs/conversations/entities/MucOptions.java index 3ea7f4f6..9bc6fb21 100644 --- a/src/eu/siacs/conversations/entities/MucOptions.java +++ b/src/eu/siacs/conversations/entities/MucOptions.java @@ -144,7 +144,11 @@ public class MucOptions { if (split.length == 2) { return split[1]; } else { - return conversation.getAccount().getUsername(); + if (conversation.getAccount()!=null) { + return conversation.getAccount().getUsername(); + } else { + return null; + } } } diff --git a/src/eu/siacs/conversations/services/EventReceiver.java b/src/eu/siacs/conversations/services/EventReceiver.java index 99b9f3c7..a13d51f2 100644 --- a/src/eu/siacs/conversations/services/EventReceiver.java +++ b/src/eu/siacs/conversations/services/EventReceiver.java @@ -8,11 +8,10 @@ public class EventReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { Intent mIntentForService = new Intent(context, XmppConnectionService.class); - mIntentForService.putExtra("ping", intent.getBooleanExtra("ping",false)); - if ((intent.getAction() != null) - && (intent.getAction() - .equals("android.intent.action.BOOT_COMPLETED"))) { - + if (intent.getAction() != null) { + mIntentForService.setAction(intent.getAction()); + } else { + mIntentForService.setAction("other"); } context.startService(mIntentForService); } diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index de261dbe..b8d8b822 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -7,7 +7,6 @@ import java.util.Hashtable; import java.util.List; import java.util.Random; -import org.json.JSONException; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; @@ -70,9 +69,10 @@ public class XmppConnectionService extends Service { public long startDate; - private static final int PING_INTERVAL = 300; + private static final int PING_MAX_INTERVAL = 30; + private static final int PING_MIN_INTERVAL = 10; private static final int PING_TIMEOUT = 2; - + private List accounts; private List conversations = null; @@ -80,10 +80,11 @@ public class XmppConnectionService extends Service { private OnAccountListChangedListener accountChangedListener = null; private OnTLSExceptionReceived tlsException = null; - public void setOnTLSExceptionReceivedListener(OnTLSExceptionReceived listener) { + public void setOnTLSExceptionReceivedListener( + OnTLSExceptionReceived listener) { tlsException = listener; } - + private Random mRandom = new Random(System.currentTimeMillis()); private ContentObserver contactObserver = new ContentObserver(null) { @@ -115,7 +116,7 @@ public class XmppConnectionService extends Service { && (packet.getBody().startsWith("?OTR"))) { message = MessageParser.parseOtrChat(packet, account, service); - if (message!=null) { + if (message != null) { message.markUnread(); } } else if (packet.hasChild("body")) { @@ -126,7 +127,7 @@ public class XmppConnectionService extends Service { || (packet.hasChild("sent"))) { message = MessageParser.parseCarbonMessage(packet, account, service); - if (message!=null) { + if (message != null) { message.getConversation().markRead(); } notify = false; @@ -146,7 +147,7 @@ public class XmppConnectionService extends Service { } else if (packet.getType() == MessagePacket.TYPE_ERROR) { message = MessageParser.parseError(packet, account, service); } else { - //Log.d(LOGTAG, "unparsed message " + packet.toString()); + // Log.d(LOGTAG, "unparsed message " + packet.toString()); } if (message == null) { return; @@ -171,7 +172,8 @@ public class XmppConnectionService extends Service { if (convChangedListener != null) { convChangedListener.onConversationListChanged(); } else { - UIHelper.updateNotification(getApplicationContext(), getConversations(), notify); + UIHelper.updateNotification(getApplicationContext(), + getConversations(), notify); } } }; @@ -197,13 +199,13 @@ public class XmppConnectionService extends Service { if (convChangedListener != null) { convChangedListener.onConversationListChanged(); } - scheduleWakeupCall(PING_INTERVAL, true); + scheduleWakeupCall(PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { - Log.d(LOGTAG,"onStatusChanged offline"); + Log.d(LOGTAG, "onStatusChanged offline"); databaseBackend.clearPresences(account); if (!account.isOptionSet(Account.OPTION_DISABLED)) { - int timeToReconnect = mRandom.nextInt(50)+10; - scheduleWakeupCall(timeToReconnect,false); + int timeToReconnect = mRandom.nextInt(50) + 10; + scheduleWakeupCall(timeToReconnect, false); } } @@ -215,13 +217,17 @@ public class XmppConnectionService extends Service { @Override public void onPresencePacketReceived(Account account, PresencePacket packet) { - if (packet.hasChild("x")&&(packet.findChild("x").getAttribute("xmlns").startsWith("http://jabber.org/protocol/muc"))) { - Conversation muc = findMuc(packet.getAttribute("from").split("/")[0]); - if (muc!=null) { + if (packet.hasChild("x") + && (packet.findChild("x").getAttribute("xmlns") + .startsWith("http://jabber.org/protocol/muc"))) { + Conversation muc = findMuc(packet.getAttribute("from").split( + "/")[0]); + if (muc != null) { int error = muc.getMucOptions().getError(); muc.getMucOptions().processPacket(packet); - if ((muc.getMucOptions().getError()!=error)&&(convChangedListener!=null)) { - Log.d(LOGTAG,"muc error status changed"); + if ((muc.getMucOptions().getError() != error) + && (convChangedListener != null)) { + Log.d(LOGTAG, "muc error status changed"); convChangedListener.onConversationListChanged(); } } @@ -247,15 +253,17 @@ public class XmppConnectionService extends Service { contact.updatePresence(fromParts[1], Presences.DND); } PgpEngine pgp = getPgpEngine(); - if (pgp!=null) { + if (pgp != null) { Element x = packet.findChild("x"); if ((x != null) - && (x.getAttribute("xmlns").equals("jabber:x:signed"))) { + && (x.getAttribute("xmlns") + .equals("jabber:x:signed"))) { try { - contact.setPgpKeyId(pgp.fetchKeyId(packet.findChild("status") - .getContent(), x.getContent())); + contact.setPgpKeyId(pgp.fetchKeyId(packet + .findChild("status").getContent(), x + .getContent())); } catch (OpenPgpException e) { - Log.d(LOGTAG,"faulty pgp. just ignore"); + Log.d(LOGTAG, "faulty pgp. just ignore"); } } } @@ -309,6 +317,8 @@ public class XmppConnectionService extends Service { private OpenPgpServiceConnection pgpServiceConnection; private PgpEngine mPgpEngine = null; + private Intent pingIntent; + private PendingIntent pendingPingIntent = null; public PgpEngine getPgpEngine() { if (pgpServiceConnection.isBound()) { @@ -325,7 +335,7 @@ public class XmppConnectionService extends Service { } protected Conversation findMuc(String name) { - for(Conversation conversation : this.conversations) { + for (Conversation conversation : this.conversations) { if (conversation.getContactJid().split("/")[0].equals(name)) { return conversation; } @@ -386,34 +396,57 @@ public class XmppConnectionService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { - ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); + Log.d(LOGTAG,"calling start service. caller was:"+intent.getAction()); + + // internet and online last_received - list_ping >= max_ping : ping + // internet and online last_ping - last_received >= ping_timeout : + // reconnect + // internet and offline and enabled : connect (Threat start) + // no internet - set no internet + + ConnectivityManager cm = (ConnectivityManager) getApplicationContext() + .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnected(); + for (Account account : accounts) { - if (!isConnected) { - account.setStatus(Account.STATUS_NO_INTERNET); - } else { - if (account.getStatus() == Account.STATUS_NO_INTERNET) { - account.setStatus(Account.STATUS_OFFLINE); - } - } - if (accountChangedListener!=null) { - accountChangedListener.onAccountListChangedListener(); - } - if ((!account.isOptionSet(Account.OPTION_DISABLED))&&(isConnected)) { - if (account.getXmppConnection() == null) { - account.setXmppConnection(this.createConnection(account)); - } - if (account.getStatus()==Account.STATUS_OFFLINE) { - Thread thread = new Thread(account.getXmppConnection()); - thread.start(); + if (!account.isOptionSet(Account.OPTION_DISABLED)) { + if (!isConnected) { + account.setStatus(Account.STATUS_NO_INTERNET); } else { - if (intent.getBooleanExtra("ping", false)) { - Log.d(LOGTAG,"start service ping"); - ping(account,PING_TIMEOUT); + if (account.getStatus() == Account.STATUS_NO_INTERNET) { + account.setStatus(Account.STATUS_OFFLINE); } + + // TODO 3 remaining cases + if (account.getStatus() == Account.STATUS_ONLINE) { + long lastReceived = account.getXmppConnection().lastPaketReceived; + long lastSent = account.getXmppConnection().lastPingSent; + if (lastSent - lastReceived >= PING_TIMEOUT * 1000) { + Log.d(LOGTAG, account.getJid() + ": ping timeout"); + this.reconnectAccount(account); + } else if (SystemClock.elapsedRealtime() - lastReceived >= PING_MIN_INTERVAL * 1000) { + account.getXmppConnection().sendPing(); + account.getXmppConnection().lastPingSent = SystemClock.elapsedRealtime(); + this.scheduleWakeupCall(2, false); + } + } else if (account.getStatus() == Account.STATUS_OFFLINE) { + if (account.getXmppConnection() == null) { + account.setXmppConnection(this + .createConnection(account)); + } + account.getXmppConnection().lastPingSent = SystemClock.elapsedRealtime(); + new Thread(account.getXmppConnection()).start(); + } else { + Log.d(LOGTAG,account.getJid()+": status="+account.getStatus()); + } + //in any case. reschedule wakup call + this.scheduleWakeupCall(PING_MAX_INTERVAL, true); + } + if (accountChangedListener != null) { + accountChangedListener.onAccountListChangedListener(); } } } @@ -431,8 +464,7 @@ public class XmppConnectionService extends Service { this.pgpServiceConnection = new OpenPgpServiceConnection( getApplicationContext(), "org.sufficientlysecure.keychain"); this.pgpServiceConnection.bindToService(); - - + } @Override @@ -440,26 +472,47 @@ public class XmppConnectionService extends Service { super.onDestroy(); for (Account account : accounts) { if (account.getXmppConnection() != null) { - disconnect(account,true); + disconnect(account, true); } } } - - protected void scheduleWakeupCall(int seconds,boolean ping) { + + protected void scheduleWakeupCall(int seconds, boolean ping) { + long timeToWake = SystemClock.elapsedRealtime() + seconds * 1000; Context context = getApplicationContext(); - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(context, EventReceiver.class); - intent.putExtra("ping", ping); - PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + - seconds * 1000, alarmIntent); - if (ping) { - Log.d(LOGTAG,"schedule ping in "+seconds+" seconds"); - } else { - Log.d(LOGTAG,"schedule reconnect in "+seconds+" seconds"); - } + AlarmManager alarmManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + + + if (ping) { + if (this.pingIntent==null) { + this.pingIntent = new Intent(context, EventReceiver.class); + this.pingIntent.setAction("ping"); + this.pingIntent.putExtra("time", timeToWake); + this.pendingPingIntent = PendingIntent.getBroadcast(context, 0, + this.pingIntent, 0); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent); + Log.d(LOGTAG,"schedule ping in "+seconds+" seconds"); + } else { + long scheduledTime = this.pingIntent.getLongExtra("time", 0); + if (scheduledTime timeToWake)) { + this.pingIntent.putExtra("time", timeToWake); + alarmManager.cancel(this.pendingPingIntent); + this.pendingPingIntent = PendingIntent.getBroadcast(context, 0, + this.pingIntent, 0); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent); + Log.d(LOGTAG,"reschedule old ping to ping in "+seconds+" seconds"); + } + } + } else { + Intent intent = new Intent(context, EventReceiver.class); + intent.setAction("ping_check"); + PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, + intent, 0); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, alarmIntent); + } + } public XmppConnection createConnection(Account account) { @@ -470,16 +523,19 @@ public class XmppConnectionService extends Service { connection.setOnPresencePacketReceivedListener(this.presenceListener); connection .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener); - connection.setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { - - @Override - public void onTLSExceptionReceived(String fingerprint, Account account) { - Log.d(LOGTAG,"tls exception arrived in service"); - if (tlsException!=null) { - tlsException.onTLSExceptionReceived(fingerprint,account); - } - } - }); + connection + .setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { + + @Override + public void onTLSExceptionReceived(String fingerprint, + Account account) { + Log.d(LOGTAG, "tls exception arrived in service"); + if (tlsException != null) { + tlsException.onTLSExceptionReceived(fingerprint, + account); + } + } + }); return connection; } @@ -624,9 +680,10 @@ public class XmppConnectionService extends Service { Element query = new Element("query"); query.setAttribute("xmlns", "jabber:iq:roster"); if (!"".equals(account.getRosterVersion())) { - Log.d(LOGTAG,account.getJid()+ ": fetching roster version "+account.getRosterVersion()); + Log.d(LOGTAG, account.getJid() + ": fetching roster version " + + account.getRosterVersion()); } else { - Log.d(LOGTAG,account.getJid()+": fetching roster"); + Log.d(LOGTAG, account.getJid() + ": fetching roster"); } query.setAttribute("ver", account.getRosterVersion()); iqPacket.addChild(query); @@ -638,7 +695,8 @@ public class XmppConnectionService extends Service { IqPacket packet) { Element roster = packet.findChild("query"); if (roster != null) { - Log.d(LOGTAG,account.getJid()+": processing roster"); + Log.d(LOGTAG, account.getJid() + + ": processing roster"); processRosterItems(account, roster); StringBuilder mWhere = new StringBuilder(); mWhere.append("jid NOT IN("); @@ -663,7 +721,8 @@ public class XmppConnectionService extends Service { } } else { - Log.d(LOGTAG,account.getJid()+": empty roster returend"); + Log.d(LOGTAG, account.getJid() + + ": empty roster returend"); } mergePhoneContactsWithRoster(new OnPhoneContactsMerged() { @@ -859,7 +918,7 @@ public class XmppConnectionService extends Service { public void deleteAccount(Account account) { Log.d(LOGTAG, "called delete account"); if (account.getXmppConnection() != null) { - this.disconnect(account,false); + this.disconnect(account, false); } databaseBackend.deleteAccount(account); this.accounts.remove(account); @@ -908,8 +967,7 @@ public class XmppConnectionService extends Service { nick = conversation.getAccount().getUsername(); } PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", muc + "/" - + nick); + packet.setAttribute("to", muc + "/" + nick); Element x = new Element("x"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc"); if (conversation.getMessages().size() != 0) { @@ -923,21 +981,22 @@ public class XmppConnectionService extends Service { conversation.getAccount().getXmppConnection() .sendPresencePacket(packet); } - + private OnRenameListener renameListener = null; private boolean pongReceived; + public void setOnRenameListener(OnRenameListener listener) { this.renameListener = listener; } - + public void renameInMuc(final Conversation conversation, final String nick) { final MucOptions options = conversation.getMucOptions(); if (options.online()) { options.setOnRenameListener(new OnRenameListener() { - + @Override public void onRename(boolean success) { - if (renameListener!=null) { + if (renameListener != null) { renameListener.onRename(success); } if (success) { @@ -946,30 +1005,36 @@ public class XmppConnectionService extends Service { } }); PresencePacket packet = new PresencePacket(); - packet.setAttribute("to", conversation.getContactJid().split("/")[0]+"/"+nick); + packet.setAttribute("to", + conversation.getContactJid().split("/")[0] + "/" + nick); packet.setAttribute("from", conversation.getAccount().getFullJid()); - - conversation.getAccount().getXmppConnection().sendPresencePacket(packet, new OnPresencePacketReceived() { - - @Override - public void onPresencePacketReceived(Account account, PresencePacket packet) { - final boolean changed; - String type = packet.getAttribute("type"); - changed = (!"error".equals(type)); - if (!changed) { - options.getOnRenameListener().onRename(false); - } else { - if (type==null) { - options.getOnRenameListener().onRename(true); - options.setNick(packet.getAttribute("from").split("/")[1]); - } else { - options.processPacket(packet); + + conversation.getAccount().getXmppConnection() + .sendPresencePacket(packet, new OnPresencePacketReceived() { + + @Override + public void onPresencePacketReceived(Account account, + PresencePacket packet) { + final boolean changed; + String type = packet.getAttribute("type"); + changed = (!"error".equals(type)); + if (!changed) { + options.getOnRenameListener().onRename(false); + } else { + if (type == null) { + options.getOnRenameListener() + .onRename(true); + options.setNick(packet.getAttribute("from") + .split("/")[1]); + } else { + options.processPacket(packet); + } + } } - } - } - }); + }); } else { - String jid = conversation.getContactJid().split("/")[0]+"/"+nick; + String jid = conversation.getContactJid().split("/")[0] + "/" + + nick; conversation.setContactJid(jid); databaseBackend.updateConversation(conversation); if (conversation.getAccount().getStatus() == Account.STATUS_ONLINE) { @@ -982,42 +1047,45 @@ public class XmppConnectionService extends Service { PresencePacket packet = new PresencePacket(); packet.setAttribute("to", conversation.getContactJid()); packet.setAttribute("from", conversation.getAccount().getFullJid()); - packet.setAttribute("type","unavailable"); - conversation.getAccount().getXmppConnection().sendPresencePacket(packet); + packet.setAttribute("type", "unavailable"); + conversation.getAccount().getXmppConnection() + .sendPresencePacket(packet); conversation.getMucOptions().setOffline(); } public void disconnect(final Account account, boolean blocking) { - List conversations = getConversations(); - for (int i = 0; i < conversations.size(); i++) { - Conversation conversation = conversations.get(i); - if (conversation.getAccount() == account) { - if (conversation.getMode() == Conversation.MODE_MULTI) { - leaveMuc(conversation); - } else { - try { - conversation.endOtrIfNeeded(); - } catch (OtrException e) { - Log.d(LOGTAG, "error ending otr session for " - + conversation.getName()); + if (account.getStatus() == Account.STATUS_ONLINE) { + List conversations = getConversations(); + for (int i = 0; i < conversations.size(); i++) { + Conversation conversation = conversations.get(i); + if (conversation.getAccount() == account) { + if (conversation.getMode() == Conversation.MODE_MULTI) { + leaveMuc(conversation); + } else { + try { + conversation.endOtrIfNeeded(); + } catch (OtrException e) { + Log.d(LOGTAG, "error ending otr session for " + + conversation.getName()); + } } } } - } - if (!blocking) { - new Thread(new Runnable() { - - @Override - public void run() { - account.getXmppConnection().disconnect(false); - Log.d(LOGTAG, "disconnected account: " + account.getJid()); - account.setXmppConnection(null); - } - }).start(); - } else { - account.getXmppConnection().disconnect(false); - Log.d(LOGTAG, "disconnected account: " + account.getJid()); - account.setXmppConnection(null); + if (!blocking) { + new Thread(new Runnable() { + + @Override + public void run() { + account.getXmppConnection().disconnect(false); + Log.d(LOGTAG, "disconnected account: " + account.getJid()); + account.setXmppConnection(null); + } + }).start(); + } else { + account.getXmppConnection().disconnect(false); + Log.d(LOGTAG, "disconnected account: " + account.getJid()); + account.setXmppConnection(null); + } } } @@ -1099,7 +1167,7 @@ public class XmppConnectionService extends Service { Log.d(LOGTAG, packet.toString()); contact.getAccount().getXmppConnection().sendPresencePacket(packet); } - + public void sendPgpPresence(Account account, String signature) { PresencePacket packet = new PresencePacket(); packet.setAttribute("from", account.getFullJid()); @@ -1129,7 +1197,7 @@ public class XmppConnectionService extends Service { public Contact findContact(String uuid) { Contact contact = this.databaseBackend.getContact(uuid); - for(Account account : getAccounts()) { + for (Account account : getAccounts()) { if (contact.getAccountUuid().equals(account.getUuid())) { contact.setAccount(account); } @@ -1141,63 +1209,22 @@ public class XmppConnectionService extends Service { this.tlsException = null; } + //TODO dont let thread sleep but schedule wake up public void reconnectAccount(final Account account) { new Thread(new Runnable() { - + @Override public void run() { if (account.getXmppConnection() != null) { - disconnect(account,true); + disconnect(account, true); } if (!account.isOptionSet(Account.OPTION_DISABLED)) { - if (account.getXmppConnection()==null) { + if (account.getXmppConnection() == null) { account.setXmppConnection(createConnection(account)); } Thread thread = new Thread(account.getXmppConnection()); thread.start(); - } - } - }).start(); - } - - public void ping(final Account account,final int timeout) { - account.getXmppConnection().r(); - Log.d(LOGTAG,account.getJid()+": sending ping"); - IqPacket iq = new IqPacket(IqPacket.TYPE_GET); - Element ping = new Element("ping"); - iq.setAttribute("from",account.getFullJid()); - ping.setAttribute("xmlns", "urn:xmpp:ping"); - iq.addChild(ping); - pongReceived = false; - account.getXmppConnection().sendIqPacket(iq, new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - pongReceived = true; - } - }); - new Thread(new Runnable() { - - @Override - public void run() { - int i = 0; - while(i <= (5 * timeout)) { - if (pongReceived) { - scheduleWakeupCall(PING_INTERVAL,true); - break; - } - try { - Thread.sleep(200); - } catch (InterruptedException e) { - - } - ++i; } - if (!pongReceived) { - Log.d("xmppService",account.getJid()+" no pong after "+timeout+" seconds"); - reconnectAccount(account); - } - } }).start(); } diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java index 2f1b1d50..1596a53a 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -42,8 +42,9 @@ public abstract class XmppActivity extends Activity { protected void onStart() { super.onStart(); if (!xmppConnectionServiceBound) { - startService(new Intent(this, XmppConnectionService.class)); Intent intent = new Intent(this, XmppConnectionService.class); + intent.setAction("ui"); + startService(intent); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 2ffe21fb..6d5d4c46 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -29,9 +29,9 @@ import javax.net.ssl.X509TrustManager; import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; -import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.os.PowerManager; +import android.os.SystemClock; import android.util.Log; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.utils.CryptoHelper; @@ -71,6 +71,9 @@ public class XmppConnection implements Runnable { private int stanzasReceived = 0; private int stanzasSent = 0; + + public long lastPaketReceived = 0; + public long lastPingSent = 0; private static final int PACKET_IQ = 0; private static final int PACKET_MESSAGE = 1; @@ -214,6 +217,7 @@ public class XmppConnection implements Runnable { tagWriter.writeStanzaAsync(ack); } else if (nextTag.isStart("a")) { Element ack = tagReader.readElement(nextTag); + lastPaketReceived = SystemClock.elapsedRealtime(); int serverSequence = Integer.parseInt(ack.getAttribute("h")); if (serverSequence>this.stanzasSent) { this.stanzasSent = serverSequence; @@ -265,6 +269,7 @@ public class XmppConnection implements Runnable { nextTag = tagReader.readTag(); } ++stanzasReceived; + lastPaketReceived = SystemClock.elapsedRealtime(); return element; } @@ -580,6 +585,7 @@ public class XmppConnection implements Runnable { } private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) { + // TODO dont increment stanza count if packet = request packet or ack; ++stanzasSent; tagWriter.writeStanzaAsync(packet); if (callback != null) { @@ -589,6 +595,21 @@ public class XmppConnection implements Runnable { packetCallbacks.put(packet.getId(), callback); } } + + public void sendPing() { + if (streamFeatures.hasChild("sm")) { + Log.d(LOGTAG,"sending r as ping"); + tagWriter.writeStanzaAsync(new RequestPacket()); + } else { + Log.d(LOGTAG,"sending iq as ping"); + IqPacket iq = new IqPacket(IqPacket.TYPE_GET); + Element ping = new Element("ping"); + iq.setAttribute("from",account.getFullJid()); + ping.setAttribute("xmlns", "urn:xmpp:ping"); + iq.addChild(ping); + this.sendIqPacket(iq, null); + } + } public void setOnMessagePacketReceivedListener( OnMessagePacketReceived listener) { From 838270432f5f4fbbdcc98ac7019c7baa4b666210 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 11 Mar 2014 16:27:33 +0100 Subject: [PATCH 3/4] cleaner/better logging --- .../services/XmppConnectionService.java | 11 +++++------ .../siacs/conversations/xmpp/XmppConnection.java | 15 ++++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index b8d8b822..da8129c4 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -69,7 +69,7 @@ public class XmppConnectionService extends Service { public long startDate; - private static final int PING_MAX_INTERVAL = 30; + private static final int PING_MAX_INTERVAL = 300; private static final int PING_MIN_INTERVAL = 10; private static final int PING_TIMEOUT = 2; @@ -201,7 +201,6 @@ public class XmppConnectionService extends Service { } scheduleWakeupCall(PING_MAX_INTERVAL, true); } else if (account.getStatus() == Account.STATUS_OFFLINE) { - Log.d(LOGTAG, "onStatusChanged offline"); databaseBackend.clearPresences(account); if (!account.isOptionSet(Account.OPTION_DISABLED)) { int timeToReconnect = mRandom.nextInt(50) + 10; @@ -493,7 +492,7 @@ public class XmppConnectionService extends Service { this.pendingPingIntent = PendingIntent.getBroadcast(context, 0, this.pingIntent, 0); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent); - Log.d(LOGTAG,"schedule ping in "+seconds+" seconds"); + //Log.d(LOGTAG,"schedule ping in "+seconds+" seconds"); } else { long scheduledTime = this.pingIntent.getLongExtra("time", 0); if (scheduledTime timeToWake)) { @@ -502,7 +501,7 @@ public class XmppConnectionService extends Service { this.pendingPingIntent = PendingIntent.getBroadcast(context, 0, this.pingIntent, 0); alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,timeToWake, pendingPingIntent); - Log.d(LOGTAG,"reschedule old ping to ping in "+seconds+" seconds"); + //Log.d(LOGTAG,"reschedule old ping to ping in "+seconds+" seconds"); } } } else { @@ -1078,13 +1077,13 @@ public class XmppConnectionService extends Service { public void run() { account.getXmppConnection().disconnect(false); Log.d(LOGTAG, "disconnected account: " + account.getJid()); - account.setXmppConnection(null); + //account.setXmppConnection(null); } }).start(); } else { account.getXmppConnection().disconnect(false); Log.d(LOGTAG, "disconnected account: " + account.getJid()); - account.setXmppConnection(null); + //account.setXmppConnection(null); } } } diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index 6d5d4c46..b11f2016 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -95,14 +95,16 @@ public class XmppConnection implements Runnable { } protected void changeStatus(int nextStatus) { - account.setStatus(nextStatus); - if (statusListener != null) { - statusListener.onStatusChanged(account); + if (account.getStatus() != nextStatus) { + account.setStatus(nextStatus); + if (statusListener != null) { + statusListener.onStatusChanged(account); + } } } protected void connect() { - Log.d(LOGTAG, "connecting"); + Log.d(LOGTAG,account.getJid()+ ": connecting"); try { tagReader = new XmlReader(wakeLock); tagWriter = new TagWriter(); @@ -165,7 +167,6 @@ public class XmppConnection implements Runnable { @Override public void run() { connect(); - Log.d(LOGTAG, "end run"); } private void processStream(Tag currentTag) throws XmlPullParserException, @@ -598,10 +599,10 @@ public class XmppConnection implements Runnable { public void sendPing() { if (streamFeatures.hasChild("sm")) { - Log.d(LOGTAG,"sending r as ping"); + Log.d(LOGTAG,account.getJid()+": sending r as ping"); tagWriter.writeStanzaAsync(new RequestPacket()); } else { - Log.d(LOGTAG,"sending iq as ping"); + Log.d(LOGTAG,account.getJid()+": sending iq as ping"); IqPacket iq = new IqPacket(IqPacket.TYPE_GET); Element ping = new Element("ping"); iq.setAttribute("from",account.getFullJid()); From 11a8a747e37cc643d19855271ede5a9ad752f71d Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 11 Mar 2014 16:49:42 +0100 Subject: [PATCH 4/4] fixed bug with disabling accounts --- .../conversations/services/XmppConnectionService.java | 11 +++++++++-- .../siacs/conversations/ui/ManageAccountActivity.java | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index da8129c4..5fa0c850 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -292,7 +292,7 @@ public class XmppConnectionService extends Service { // TODO: ask user to handle it maybe } } else { - Log.d(LOGTAG, packet.toString()); + //Log.d(LOGTAG, packet.toString()); } replaceContactInConversation(contact.getJid(), contact); } @@ -414,9 +414,15 @@ public class XmppConnectionService extends Service { if (!account.isOptionSet(Account.OPTION_DISABLED)) { if (!isConnected) { account.setStatus(Account.STATUS_NO_INTERNET); + if (statusListener!=null) { + statusListener.onStatusChanged(account); + } } else { if (account.getStatus() == Account.STATUS_NO_INTERNET) { account.setStatus(Account.STATUS_OFFLINE); + if (statusListener!=null) { + statusListener.onStatusChanged(account); + } } // TODO 3 remaining cases @@ -440,6 +446,7 @@ public class XmppConnectionService extends Service { new Thread(account.getXmppConnection()).start(); } else { Log.d(LOGTAG,account.getJid()+": status="+account.getStatus()); + // TODO notify user of ssl cert problem or auth problem or what ever } //in any case. reschedule wakup call this.scheduleWakeupCall(PING_MAX_INTERVAL, true); @@ -1053,7 +1060,7 @@ public class XmppConnectionService extends Service { } public void disconnect(final Account account, boolean blocking) { - if (account.getStatus() == Account.STATUS_ONLINE) { + if ((account.getStatus() == Account.STATUS_ONLINE)||(account.getStatus() == Account.STATUS_DISABLED)) { List conversations = getConversations(); for (int i = 0; i < conversations.size(); i++) { Conversation conversation = conversations.get(i); diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index 2af69867..85998131 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -48,7 +48,6 @@ public class ManageAccountActivity extends XmppActivity { @Override public void onAccountListChangedListener() { - Log.d("xmppService", "ui on account list changed listener"); accountList.clear(); accountList.addAll(xmppConnectionService.getAccounts()); runOnUiThread(new Runnable() {