added hash for status quo to make sync reply more performant

This commit is contained in:
Daniel Gultsch 2018-10-29 14:42:43 +01:00
parent 1bcbd257c3
commit a1a625bb2d
4 changed files with 141 additions and 57 deletions

View file

@ -48,7 +48,7 @@ public final class Config {
public static final boolean ALLOW_NON_TLS_CONNECTIONS = false; //very dangerous. you should have a good reason to set this to true
public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 5;
public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 1;
//Notification settings

View file

@ -4,10 +4,12 @@ import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.util.Log;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -65,4 +67,13 @@ public class PhoneNumberContact extends AbstractPhoneContact {
}
return contacts;
}
public static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
for(PhoneNumberContact contact : haystack) {
if (needle.equals(contact.getLookupUri())) {
return contact;
}
}
return null;
}
}

View file

@ -0,0 +1,114 @@
package eu.siacs.conversations.entities;
import android.util.Base64;
import android.util.Log;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.android.PhoneNumberContact;
import eu.siacs.conversations.xml.Element;
import rocks.xmpp.addr.Jid;
public class Entry implements Comparable<Entry> {
private final List<Jid> jids;
private final String number;
private Entry(String number, List<Jid> jids) {
this.number = number;
this.jids = jids;
}
public static Entry of(Element element) {
final String number = element.getAttribute("number");
final List<Jid> jids = new ArrayList<>();
for (Element jidElement : element.getChildren()) {
String content = jidElement.getContent();
if (content != null) {
jids.add(Jid.of(content));
}
}
return new Entry(number, jids);
}
public static List<Entry> ofPhoneBook(Element phoneBook) {
List<Entry> entries = new ArrayList<>();
for (Element entry : phoneBook.getChildren()) {
if ("entry".equals(entry.getName())) {
entries.add(of(entry));
}
}
return entries;
}
public static String statusQuo(final Collection<PhoneNumberContact> phoneNumberContacts, Collection<Contact> systemContacts) {
return statusQuo(ofPhoneNumberContactsAndContacts(phoneNumberContacts, systemContacts));
}
private static String statusQuo(final List<Entry> entries) {
Collections.sort(entries);
StringBuilder builder = new StringBuilder();
for(Entry entry : entries) {
if (builder.length() != 0) {
builder.append('\u001d');
}
builder.append(entry.getNumber());
List<Jid> jids = entry.getJids();
Collections.sort(jids);
for(Jid jid : jids) {
builder.append('\u001e');
builder.append(jid.asBareJid().toEscapedString());
}
}
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
return "";
}
Log.d(Config.LOGTAG,"status quo string: "+builder.toString());
byte[] sha1 = md.digest(builder.toString().getBytes());
return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
private static List<Entry> ofPhoneNumberContactsAndContacts(final Collection<PhoneNumberContact> phoneNumberContacts, Collection<Contact> systemContacts) {
ArrayList<Entry> entries = new ArrayList<>();
for(Contact contact : systemContacts) {
PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(phoneNumberContacts, contact.getSystemAccount());
if (phoneNumberContact != null && phoneNumberContact.getPhoneNumber() != null) {
Entry entry = findOrCreateByPhoneNumber(entries, phoneNumberContact.getPhoneNumber());
entry.jids.add(contact.getJid().asBareJid());
}
}
return entries;
}
private static Entry findOrCreateByPhoneNumber(final List<Entry> entries, String number) {
for(Entry entry : entries) {
if (entry.number.equals(number)) {
return entry;
}
}
Entry entry = new Entry(number, new ArrayList<>());
entries.add(entry);
return entry;
}
public List<Jid> getJids() {
return jids;
}
public String getNumber() {
return number;
}
@Override
public int compareTo(Entry o) {
return number.compareTo(o.number);
}
}

View file

@ -34,6 +34,7 @@ import eu.siacs.conversations.android.PhoneNumberContact;
import eu.siacs.conversations.crypto.sasl.Plain;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Entry;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.PhoneNumberUtilWrapper;
@ -286,7 +287,7 @@ public class QuickConversationsService extends AbstractQuickConversationsService
if (uri == null) {
continue;
}
PhoneNumberContact phoneNumberContact = findByUri(contacts, uri);
PhoneNumberContact phoneNumberContact = PhoneNumberContact.findByUri(contacts, uri);
final boolean needsCacheClean;
if (phoneNumberContact != null) {
needsCacheClean = contact.setPhoneContact(phoneNumberContact);
@ -320,13 +321,17 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}
IqPacket query = new IqPacket(IqPacket.TYPE.GET);
query.setTo(syncServer);
query.addChild(new Element("phone-book", Namespace.SYNCHRONIZATION).setChildren(entries));
Element book = new Element("phone-book", Namespace.SYNCHRONIZATION).setChildren(entries);
String statusQuo = Entry.statusQuo(contacts.values(), account.getRoster().getWithSystemAccounts(PhoneNumberContact.class));
Log.d(Config.LOGTAG,"status quo="+statusQuo);
book.setAttribute("ver",statusQuo);
query.addChild(book);
mLastSyncAttempt = Attempt.create(hash);
service.sendIqPacket(account, query, (a, response) -> {
if (response.getType() == IqPacket.TYPE.RESULT) {
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(PhoneNumberContact.class);
final Element phoneBook = response.findChild("phone-book", Namespace.SYNCHRONIZATION);
if (phoneBook != null) {
List<Contact> withSystemAccounts = account.getRoster().getWithSystemAccounts(PhoneNumberContact.class);
for(Entry entry : Entry.ofPhoneBook(phoneBook)) {
PhoneNumberContact phoneContact = contacts.get(entry.getNumber());
if (phoneContact == null) {
@ -341,12 +346,14 @@ public class QuickConversationsService extends AbstractQuickConversationsService
withSystemAccounts.remove(contact);
}
}
}
for (Contact contact : withSystemAccounts) {
final boolean needsCacheClean = contact.unsetPhoneContact(PhoneNumberContact.class);
if (needsCacheClean) {
service.getAvatarService().clear(contact);
for (Contact contact : withSystemAccounts) {
final boolean needsCacheClean = contact.unsetPhoneContact(PhoneNumberContact.class);
if (needsCacheClean) {
service.getAvatarService().clear(contact);
}
}
} else {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": phone number contact list remains unchanged");
}
}
service.syncRoster(account);
@ -355,14 +362,6 @@ public class QuickConversationsService extends AbstractQuickConversationsService
return true;
}
private static PhoneNumberContact findByUri(Collection<PhoneNumberContact> haystack, Uri needle) {
for(PhoneNumberContact contact : haystack) {
if (needle.equals(contact.getLookupUri())) {
return contact;
}
}
return null;
}
private static class Attempt {
private final long timestamp;
@ -382,46 +381,6 @@ public class QuickConversationsService extends AbstractQuickConversationsService
}
}
public static class Entry {
private final List<Jid> jids;
private final String number;
private Entry(String number, List<Jid> jids) {
this.number = number;
this.jids = jids;
}
public static Entry of(Element element) {
final String number = element.getAttribute("number");
final List<Jid> jids = new ArrayList<>();
for (Element jidElement : element.getChildren()) {
String content = jidElement.getContent();
if (content != null) {
jids.add(Jid.of(content));
}
}
return new Entry(number, jids);
}
public static List<Entry> ofPhoneBook(Element phoneBook) {
List<Entry> entries = new ArrayList<>();
for (Element entry : phoneBook.getChildren()) {
if ("entry".equals(entry.getName())) {
entries.add(of(entry));
}
}
return entries;
}
public List<Jid> getJids() {
return jids;
}
public String getNumber() {
return number;
}
}
public interface OnVerificationRequested {
void onVerificationRequestFailed(int code);