Limit the directory refresh in response to system contact changes.

Previously, we would do a full directory/CDS refresh in response to any
change in system contacts. That can be expensive.

This changes the behavior to look at how many new contacts there after
being notified of a contact change.

- If there aren't any, we just sync names and stuff.
- If we just have a few new contacts, we'll sync just those specifically.
- If we have a lot, we'll do a full sync.
This commit is contained in:
Greyson Parrelli 2021-04-07 16:29:00 -04:00 committed by Alan Evans
parent 1aa8e9753d
commit fcc49ae7b6
2 changed files with 133 additions and 80 deletions

View file

@ -7,16 +7,30 @@ import android.content.Context;
import android.content.SyncResult; import android.content.SyncResult;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper; import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.SetUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter { public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
private static final String TAG = Log.tag(ContactsSyncAdapter.class); private static final String TAG = Log.tag(ContactsSyncAdapter.class);
private static final int FULL_SYNC_THRESHOLD = 10;
public ContactsSyncAdapter(Context context, boolean autoInitialize) { public ContactsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize); super(context, autoInitialize);
} }
@ -27,12 +41,40 @@ public class ContactsSyncAdapter extends AbstractThreadedSyncAdapter {
{ {
Log.i(TAG, "onPerformSync(" + authority +")"); Log.i(TAG, "onPerformSync(" + authority +")");
if (TextSecurePreferences.isPushRegistered(getContext())) { Context context = getContext();
if (!TextSecurePreferences.isPushRegistered(context)) {
Log.i(TAG, "Not push registered. Just syncing contact info.");
DirectoryHelper.syncRecipientInfoWithSystemContacts(context);
return;
}
Set<String> allSystemNumbers = ContactAccessor.getInstance().getAllContactsWithNumbers(context);
Set<String> knownSystemNumbers = DatabaseFactory.getRecipientDatabase(context).getAllPhoneNumbers();
Set<String> unknownSystemNumbers = SetUtil.difference(allSystemNumbers, knownSystemNumbers);
if (unknownSystemNumbers.size() > FULL_SYNC_THRESHOLD) {
Log.i(TAG, "There are " + unknownSystemNumbers.size() + " unknown contacts. Doing a full sync.");
try { try {
DirectoryHelper.refreshDirectory(getContext(), true); DirectoryHelper.refreshDirectory(context, true);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, e); Log.w(TAG, e);
} }
} else if (unknownSystemNumbers.size() > 0) {
Log.i(TAG, "There are " + unknownSystemNumbers.size() + " unknown contacts. Doing an individual sync.");
List<Recipient> recipients = Stream.of(unknownSystemNumbers)
.filter(s -> s.startsWith("+"))
.map(s -> Recipient.external(getContext(), s))
.toList();
try {
DirectoryHelper.refreshDirectoryFor(context, recipients, true);
} catch (IOException e) {
Log.w(TAG, "Failed to refresh! Scheduling for later.", e);
ApplicationDependencies.getJobManager().add(new DirectoryRefreshJob(true));
}
} else {
Log.i(TAG, "No new contacts. Just syncing system contact data.");
DirectoryHelper.syncRecipientInfoWithSystemContacts(context);
} }
} }

View file

@ -209,6 +209,13 @@ public class DirectoryHelper {
return newRegisteredState; return newRegisteredState;
} }
/**
* Reads the system contacts and copies over any matching data (like names) int our local store.
*/
public static void syncRecipientInfoWithSystemContacts(@NonNull Context context) {
syncRecipientInfoWithSystemContacts(context, Collections.emptyMap());
}
@WorkerThread @WorkerThread
private static void refreshNumbers(@NonNull Context context, @NonNull Set<String> databaseNumbers, @NonNull Set<String> systemNumbers, boolean notifyOfNewUsers) throws IOException { private static void refreshNumbers(@NonNull Context context, @NonNull Set<String> databaseNumbers, @NonNull Set<String> systemNumbers, boolean notifyOfNewUsers) throws IOException {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
@ -310,7 +317,6 @@ public class DirectoryHelper {
} }
try { try {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(context); ContactsDatabase contactsDatabase = DatabaseFactory.getContactsDatabase(context);
List<String> activeAddresses = Stream.of(activeIds) List<String> activeAddresses = Stream.of(activeIds)
.map(Recipient::resolved) .map(Recipient::resolved)
@ -321,6 +327,14 @@ public class DirectoryHelper {
contactsDatabase.removeDeletedRawContacts(account.getAccount()); contactsDatabase.removeDeletedRawContacts(account.getAccount());
contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing); contactsDatabase.setRegisteredUsers(account.getAccount(), activeAddresses, removeMissing);
syncRecipientInfoWithSystemContacts(context, rewrites);
} catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, "Failed to update contacts.", e);
}
}
private static void syncRecipientInfoWithSystemContacts(@NonNull Context context, @NonNull Map<String, String> rewrites) {
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate(); BulkOperationsHandle handle = recipientDatabase.beginBulkSystemContactUpdate();
try (Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context)) { try (Cursor cursor = ContactAccessor.getInstance().getAllSystemContacts(context)) {
@ -392,9 +406,6 @@ public class DirectoryHelper {
} }
} }
} }
} catch (RemoteException | OperationApplicationException e) {
Log.w(TAG, "Failed to update contacts.", e);
}
} }
private static boolean isPhoneMimeType(@NonNull String mimeType) { private static boolean isPhoneMimeType(@NonNull String mimeType) {