refactored disco cache. avoid making duplicate call. check hash
This commit is contained in:
parent
4a1a59f0c8
commit
f0798216d5
|
@ -62,6 +62,8 @@ public final class Config {
|
||||||
|
|
||||||
public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
|
public static final boolean IGNORE_ID_REWRITE_IN_MUC = true;
|
||||||
|
|
||||||
|
public static final boolean REQUEST_DISCO = true;
|
||||||
|
|
||||||
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
|
public static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
|
||||||
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
|
public static final long MAM_MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
|
||||||
public static final int MAM_MAX_MESSAGES = 500;
|
public static final int MAM_MAX_MESSAGES = 500;
|
||||||
|
|
|
@ -3,6 +3,7 @@ package eu.siacs.conversations.entities;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import eu.siacs.conversations.crypto.PgpDecryptionService;
|
import eu.siacs.conversations.crypto.PgpDecryptionService;
|
||||||
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
|
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
|
||||||
|
@ -14,6 +15,7 @@ import org.json.JSONObject;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.interfaces.DSAPublicKey;
|
import java.security.interfaces.DSAPublicKey;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
@ -48,6 +50,7 @@ public class Account extends AbstractEntity {
|
||||||
public static final int OPTION_DISABLED = 1;
|
public static final int OPTION_DISABLED = 1;
|
||||||
public static final int OPTION_REGISTER = 2;
|
public static final int OPTION_REGISTER = 2;
|
||||||
public static final int OPTION_USECOMPRESSION = 3;
|
public static final int OPTION_USECOMPRESSION = 3;
|
||||||
|
public final HashSet<Pair<String, String>> inProgressDiscoFetches = new HashSet<>();
|
||||||
|
|
||||||
public boolean httpUploadAvailable() {
|
public boolean httpUploadAvailable() {
|
||||||
return xmppConnection != null && xmppConnection.getFeatures().httpUpload();
|
return xmppConnection != null && xmppConnection.getFeatures().httpUpload();
|
||||||
|
|
|
@ -22,23 +22,31 @@ public class Presence implements Comparable {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Status status;
|
protected final Status status;
|
||||||
protected final ServiceDiscoveryResult disco;
|
protected ServiceDiscoveryResult disco;
|
||||||
|
protected final String ver;
|
||||||
|
protected final String hash;
|
||||||
|
|
||||||
public Presence(Element show, ServiceDiscoveryResult disco) {
|
private Presence(Status status, String ver, String hash) {
|
||||||
this.disco = disco;
|
this.status = status;
|
||||||
|
this.ver = ver;
|
||||||
|
this.hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Presence parse(Element show, Element caps) {
|
||||||
|
final String hash = caps == null ? null : caps.getAttribute("hash");
|
||||||
|
final String ver = caps == null ? null : caps.getAttribute("ver");
|
||||||
if ((show == null) || (show.getContent() == null)) {
|
if ((show == null) || (show.getContent() == null)) {
|
||||||
this.status = Status.ONLINE;
|
return new Presence(Status.ONLINE, ver, hash);
|
||||||
} else if (show.getContent().equals("away")) {
|
} else if (show.getContent().equals("away")) {
|
||||||
this.status = Status.AWAY;
|
return new Presence(Status.AWAY, ver, hash);
|
||||||
} else if (show.getContent().equals("xa")) {
|
} else if (show.getContent().equals("xa")) {
|
||||||
this.status = Status.XA;
|
return new Presence(Status.XA, ver, hash);
|
||||||
} else if (show.getContent().equals("chat")) {
|
} else if (show.getContent().equals("chat")) {
|
||||||
this.status = Status.CHAT;
|
return new Presence(Status.CHAT, ver, hash);
|
||||||
} else if (show.getContent().equals("dnd")) {
|
} else if (show.getContent().equals("dnd")) {
|
||||||
this.status = Status.DND;
|
return new Presence(Status.DND, ver, hash);
|
||||||
} else {
|
} else {
|
||||||
this.status = Status.OFFLINE;
|
return new Presence(Status.OFFLINE, ver, hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,4 +57,20 @@ public class Presence implements Comparable {
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasCaps() {
|
||||||
|
return ver != null && hash != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVer() {
|
||||||
|
return this.ver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHash() {
|
||||||
|
return this.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceDiscoveryResult(ServiceDiscoveryResult disco) {
|
||||||
|
this.disco = disco;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,6 @@ public class ServiceDiscoveryResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ver = this.mkCapHash();
|
this.ver = this.mkCapHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,15 +138,22 @@ public class ServiceDiscoveryResult {
|
||||||
this.ver = ver;
|
this.ver = ver;
|
||||||
|
|
||||||
JSONArray identities = o.optJSONArray("identities");
|
JSONArray identities = o.optJSONArray("identities");
|
||||||
for(int i = 0; i < identities.length(); i++) {
|
if (identities != null) {
|
||||||
|
for (int i = 0; i < identities.length(); i++) {
|
||||||
this.identities.add(new Identity(identities.getJSONObject(i)));
|
this.identities.add(new Identity(identities.getJSONObject(i)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
JSONArray features = o.optJSONArray("features");
|
JSONArray features = o.optJSONArray("features");
|
||||||
for(int i = 0; i < features.length(); i++) {
|
if (features != null) {
|
||||||
|
for (int i = 0; i < features.length(); i++) {
|
||||||
this.features.add(features.getString(i));
|
this.features.add(features.getString(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVer() {
|
||||||
|
return new String(Base64.encode(this.ver, Base64.DEFAULT)).trim();
|
||||||
|
}
|
||||||
|
|
||||||
public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
|
public ServiceDiscoveryResult(Cursor cursor) throws JSONException {
|
||||||
this(
|
this(
|
||||||
|
@ -235,7 +241,7 @@ public class ServiceDiscoveryResult {
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
values.put(HASH, this.hash);
|
values.put(HASH, this.hash);
|
||||||
values.put(VER, new String(Base64.encode(this.ver, Base64.DEFAULT)).trim());
|
values.put(VER, getVer());
|
||||||
values.put(RESULT, this.toJSON().toString());
|
values.put(RESULT, this.toJSON().toString());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ public class PresenceParser extends AbstractParser implements
|
||||||
final String type = packet.getAttribute("type");
|
final String type = packet.getAttribute("type");
|
||||||
final Contact contact = account.getRoster().getContact(from);
|
final Contact contact = account.getRoster().getContact(from);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
final String presence = from.isBareJid() ? "" : from.getResourcepart();
|
final String resource = from.isBareJid() ? "" : from.getResourcepart();
|
||||||
contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
|
contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick"));
|
||||||
Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
|
Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
|
||||||
if (avatar != null && !contact.isSelf()) {
|
if (avatar != null && !contact.isSelf()) {
|
||||||
|
@ -187,31 +187,12 @@ public class PresenceParser extends AbstractParser implements
|
||||||
}
|
}
|
||||||
int sizeBefore = contact.getPresences().size();
|
int sizeBefore = contact.getPresences().size();
|
||||||
|
|
||||||
ServiceDiscoveryResult disco = null;
|
final Element show = packet.findChild("show");
|
||||||
Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
|
final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
|
||||||
|
final Presence presence = Presence.parse(show, caps);
|
||||||
if (caps != null) {
|
contact.updatePresence(resource, presence);
|
||||||
disco = mXmppConnectionService.databaseBackend.
|
if (presence.hasCaps() && Config.REQUEST_DISCO) {
|
||||||
findDiscoveryResult(caps.getAttribute("hash"), caps.getAttribute("ver"));
|
mXmppConnectionService.fetchCaps(account, from, presence);
|
||||||
}
|
|
||||||
|
|
||||||
if (disco != null || caps == null) {
|
|
||||||
contact.updatePresence(presence, new Presence(packet.findChild("show"), disco));
|
|
||||||
} else {
|
|
||||||
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
|
||||||
request.setTo(from);
|
|
||||||
request.query("http://jabber.org/protocol/disco#info");
|
|
||||||
|
|
||||||
mXmppConnectionService.sendIqPacket(account, request, new OnIqPacketReceived() {
|
|
||||||
@Override
|
|
||||||
public void onIqPacketReceived(Account account, IqPacket discoPacket) {
|
|
||||||
if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
|
|
||||||
ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
|
|
||||||
contact.updatePresence(presence, new Presence(packet.findChild("show"), disco));
|
|
||||||
mXmppConnectionService.databaseBackend.insertDiscoveryResult(disco);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PgpEngine pgp = mXmppConnectionService.getPgpEngine();
|
PgpEngine pgp = mXmppConnectionService.getPgpEngine();
|
||||||
|
|
|
@ -74,6 +74,8 @@ import eu.siacs.conversations.entities.MucOptions;
|
||||||
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
|
import eu.siacs.conversations.entities.MucOptions.OnRenameListener;
|
||||||
import eu.siacs.conversations.entities.Presence;
|
import eu.siacs.conversations.entities.Presence;
|
||||||
import eu.siacs.conversations.entities.Presences;
|
import eu.siacs.conversations.entities.Presences;
|
||||||
|
import eu.siacs.conversations.entities.Roster;
|
||||||
|
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
||||||
import eu.siacs.conversations.entities.Transferable;
|
import eu.siacs.conversations.entities.Transferable;
|
||||||
import eu.siacs.conversations.entities.TransferablePlaceholder;
|
import eu.siacs.conversations.entities.TransferablePlaceholder;
|
||||||
import eu.siacs.conversations.generator.IqGenerator;
|
import eu.siacs.conversations.generator.IqGenerator;
|
||||||
|
@ -245,6 +247,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
|
private OnKeyStatusUpdated mOnKeyStatusUpdated = null;
|
||||||
private int keyStatusUpdatedListenerCount = 0;
|
private int keyStatusUpdatedListenerCount = 0;
|
||||||
private SecureRandom mRandom;
|
private SecureRandom mRandom;
|
||||||
|
private LruCache<Pair<String,String>,ServiceDiscoveryResult> discoCache = new LruCache<>(20);
|
||||||
private final OnBindListener mOnBindListener = new OnBindListener() {
|
private final OnBindListener mOnBindListener = new OnBindListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -2956,13 +2959,67 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
@Override
|
@Override
|
||||||
public void onIqPacketReceived(Account account, IqPacket packet) {
|
public void onIqPacketReceived(Account account, IqPacket packet) {
|
||||||
if (packet.getType() == IqPacket.TYPE.ERROR) {
|
if (packet.getType() == IqPacket.TYPE.ERROR) {
|
||||||
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not publish nick");
|
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not publish nick");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ServiceDiscoveryResult getCachedServiceDiscoveryResult(Pair<String,String> key) {
|
||||||
|
ServiceDiscoveryResult result = discoCache.get(key);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
result = databaseBackend.findDiscoveryResult(key.first, key.second);
|
||||||
|
if (result != null) {
|
||||||
|
discoCache.put(key, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fetchCaps(Account account, final Jid jid, final Presence presence) {
|
||||||
|
final Pair<String,String> key = new Pair<>(presence.getHash(), presence.getVer());
|
||||||
|
ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
|
||||||
|
if (disco != null) {
|
||||||
|
presence.setServiceDiscoveryResult(disco);
|
||||||
|
} else {
|
||||||
|
if (!account.inProgressDiscoFetches.contains(key)) {
|
||||||
|
account.inProgressDiscoFetches.add(key);
|
||||||
|
IqPacket request = new IqPacket(IqPacket.TYPE.GET);
|
||||||
|
request.setTo(jid);
|
||||||
|
request.query("http://jabber.org/protocol/disco#info");
|
||||||
|
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": making disco request for "+key.second+" to "+jid);
|
||||||
|
sendIqPacket(account, request, new OnIqPacketReceived() {
|
||||||
|
@Override
|
||||||
|
public void onIqPacketReceived(Account account, IqPacket discoPacket) {
|
||||||
|
if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
|
||||||
|
ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
|
||||||
|
if (presence.getVer().equals(disco.getVer())) {
|
||||||
|
databaseBackend.insertDiscoveryResult(disco);
|
||||||
|
injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid+" "+presence.getVer()+" vs "+disco.getVer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
account.inProgressDiscoFetches.remove(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void injectServiceDiscorveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
|
||||||
|
for(Contact contact : roster.getContacts()) {
|
||||||
|
for(Presence presence : contact.getPresences().getPresences().values()) {
|
||||||
|
if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
|
||||||
|
presence.setServiceDiscoveryResult(disco);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public interface OnAccountCreated {
|
public interface OnAccountCreated {
|
||||||
void onAccountCreated(Account account);
|
void onAccountCreated(Account account);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue