Search in sources :

Example 1 with MultiDeviceContactUpdateJob

use of org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob in project Signal-Android by WhisperSystems.

the class DirectoryHelper method refreshNumbers.

@WorkerThread
private static void refreshNumbers(@NonNull Context context, @NonNull Set<String> databaseNumbers, @NonNull Set<String> systemNumbers, boolean notifyOfNewUsers, boolean removeSystemContactEntryForMissing) throws IOException {
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    Set<String> allNumbers = SetUtil.union(databaseNumbers, systemNumbers);
    if (allNumbers.isEmpty()) {
        Log.w(TAG, "No numbers to refresh!");
        return;
    }
    Stopwatch stopwatch = new Stopwatch("refresh");
    DirectoryResult result;
    if (FeatureFlags.cdsh()) {
        result = ContactDiscoveryV3.getDirectoryResult(databaseNumbers, systemNumbers);
    } else {
        result = ContactDiscoveryV2.getDirectoryResult(context, databaseNumbers, systemNumbers);
    }
    stopwatch.split("network");
    if (result.getNumberRewrites().size() > 0) {
        Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
        recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
    }
    Map<RecipientId, ACI> aciMap = recipientDatabase.bulkProcessCdsResult(result.getRegisteredNumbers());
    Set<String> activeNumbers = result.getRegisteredNumbers().keySet();
    Set<RecipientId> activeIds = aciMap.keySet();
    Set<RecipientId> inactiveIds = Stream.of(allNumbers).filterNot(activeNumbers::contains).filterNot(n -> result.getNumberRewrites().containsKey(n)).filterNot(n -> result.getIgnoredNumbers().contains(n)).map(recipientDatabase::getOrInsertFromE164).collect(Collectors.toSet());
    stopwatch.split("process-cds");
    UnlistedResult unlistedResult = filterForUnlistedUsers(context, inactiveIds);
    inactiveIds.removeAll(unlistedResult.getPossiblyActive());
    if (unlistedResult.getRetries().size() > 0) {
        Log.i(TAG, "Some profile fetches failed to resolve. Assuming not-inactive for now and scheduling a retry.");
        RetrieveProfileJob.enqueue(unlistedResult.getRetries());
    }
    stopwatch.split("handle-unlisted");
    Set<RecipientId> preExistingRegisteredUsers = new HashSet<>(recipientDatabase.getRegistered());
    recipientDatabase.bulkUpdatedRegisteredStatus(aciMap, inactiveIds);
    stopwatch.split("update-registered");
    updateContactsDatabase(context, activeIds, removeSystemContactEntryForMissing, result.getNumberRewrites());
    stopwatch.split("contacts-db");
    if (TextSecurePreferences.isMultiDevice(context)) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
    }
    if (TextSecurePreferences.hasSuccessfullyRetrievedDirectory(context) && notifyOfNewUsers) {
        Set<RecipientId> systemContacts = new HashSet<>(recipientDatabase.getSystemContacts());
        Set<RecipientId> newlyRegisteredSystemContacts = new HashSet<>(activeIds);
        newlyRegisteredSystemContacts.removeAll(preExistingRegisteredUsers);
        newlyRegisteredSystemContacts.retainAll(systemContacts);
        notifyNewUsers(context, newlyRegisteredSystemContacts);
    } else {
        TextSecurePreferences.setHasSuccessfullyRetrievedDirectory(context, true);
    }
    stopwatch.stop(TAG);
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) NonNull(androidx.annotation.NonNull) R(org.thoughtcrime.securesms.R) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) Manifest(android.Manifest) ProfileAndCredential(org.whispersystems.signalservice.api.profiles.ProfileAndCredential) ContactsContract(android.provider.ContactsContract) BulkOperationsHandle(org.thoughtcrime.securesms.database.RecipientDatabase.BulkOperationsHandle) RegisteredState(org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) StorageSyncHelper(org.thoughtcrime.securesms.storage.StorageSyncHelper) ContentResolver(android.content.ContentResolver) Map(java.util.Map) SignalProtocolAddress(org.whispersystems.libsignal.SignalProtocolAddress) Recipient(org.thoughtcrime.securesms.recipients.Recipient) AccountManager(android.accounts.AccountManager) ACI(org.whispersystems.signalservice.api.push.ACI) Account(android.accounts.Account) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) Collection(java.util.Collection) Set(java.util.Set) SetUtil(org.thoughtcrime.securesms.util.SetUtil) Objects(java.util.Objects) Log(org.signal.core.util.logging.Log) FeatureFlags(org.thoughtcrime.securesms.util.FeatureFlags) List(java.util.List) Nullable(androidx.annotation.Nullable) StorageSyncJob(org.thoughtcrime.securesms.jobs.StorageSyncJob) ProfileService(org.whispersystems.signalservice.api.services.ProfileService) BuildConfig(org.thoughtcrime.securesms.BuildConfig) InsertResult(org.thoughtcrime.securesms.database.MessageDatabase.InsertResult) Context(android.content.Context) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) Stream(com.annimon.stream.Stream) Util(org.thoughtcrime.securesms.util.Util) WorkerThread(androidx.annotation.WorkerThread) RemoteException(android.os.RemoteException) RetrieveProfileJob(org.thoughtcrime.securesms.jobs.RetrieveProfileJob) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) SignalServiceProfile(org.whispersystems.signalservice.api.profiles.SignalServiceProfile) TextSecurePreferences(org.thoughtcrime.securesms.util.TextSecurePreferences) HashSet(java.util.HashSet) Schedulers(io.reactivex.rxjava3.schedulers.Schedulers) Pair(org.whispersystems.libsignal.util.Pair) NotificationChannels(org.thoughtcrime.securesms.notifications.NotificationChannels) ProfileUtil(org.thoughtcrime.securesms.util.ProfileUtil) Calendar(java.util.Calendar) Observable(io.reactivex.rxjava3.core.Observable) RegistrationUtil(org.thoughtcrime.securesms.registration.RegistrationUtil) ContactsDatabase(org.thoughtcrime.securesms.contacts.ContactsDatabase) MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) Cursor(android.database.Cursor) Collectors(com.annimon.stream.Collectors) Permissions(org.thoughtcrime.securesms.permissions.Permissions) UuidUtil(org.whispersystems.signalservice.api.util.UuidUtil) TextUtils(android.text.TextUtils) IOException(java.io.IOException) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) OperationApplicationException(android.content.OperationApplicationException) Optional(org.whispersystems.libsignal.util.guava.Optional) TimeUnit(java.util.concurrent.TimeUnit) CursorUtil(org.thoughtcrime.securesms.util.CursorUtil) IncomingJoinedMessage(org.thoughtcrime.securesms.sms.IncomingJoinedMessage) ContactAccessor(org.thoughtcrime.securesms.contacts.ContactAccessor) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) PhoneNumberFormatter(org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) ACI(org.whispersystems.signalservice.api.push.ACI) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) HashSet(java.util.HashSet) WorkerThread(androidx.annotation.WorkerThread)

Example 2 with MultiDeviceContactUpdateJob

use of org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob in project Signal-Android by WhisperSystems.

the class DirectoryHelper method refreshDirectoryFor.

@WorkerThread
public static RegisteredState refreshDirectoryFor(@NonNull Context context, @NonNull Recipient recipient, boolean notifyOfNewUsers) throws IOException {
    Stopwatch stopwatch = new Stopwatch("single");
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    RegisteredState originalRegisteredState = recipient.resolve().getRegistered();
    RegisteredState newRegisteredState;
    if (recipient.hasServiceId() && !recipient.hasE164()) {
        boolean isRegistered = ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(recipient.requireServiceId());
        stopwatch.split("aci-network");
        if (isRegistered) {
            boolean idChanged = recipientDatabase.markRegistered(recipient.getId(), recipient.requireServiceId());
            if (idChanged) {
                Log.w(TAG, "ID changed during refresh by UUID.");
            }
        } else {
            recipientDatabase.markUnregistered(recipient.getId());
        }
        stopwatch.split("aci-disk");
        stopwatch.stop(TAG);
        return isRegistered ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
    }
    if (!recipient.getE164().isPresent()) {
        Log.w(TAG, "No ACI or E164?");
        return RegisteredState.NOT_REGISTERED;
    }
    DirectoryResult result = ContactDiscoveryV2.getDirectoryResult(context, recipient.getE164().get());
    stopwatch.split("e164-network");
    if (result.getNumberRewrites().size() > 0) {
        Log.i(TAG, "[getDirectoryResult] Need to rewrite some numbers.");
        recipientDatabase.updatePhoneNumbers(result.getNumberRewrites());
    }
    if (result.getRegisteredNumbers().size() > 0) {
        ACI aci = result.getRegisteredNumbers().values().iterator().next();
        if (aci != null) {
            boolean idChanged = recipientDatabase.markRegistered(recipient.getId(), aci);
            if (idChanged) {
                recipient = Recipient.resolved(recipientDatabase.getByServiceId(aci).get());
            }
        } else {
            Log.w(TAG, "Registered number set had a null ACI!");
        }
    } else if (recipient.hasServiceId() && recipient.isRegistered() && hasCommunicatedWith(recipient)) {
        if (ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(recipient.requireServiceId())) {
            recipientDatabase.markRegistered(recipient.getId(), recipient.requireServiceId());
        } else {
            recipientDatabase.markUnregistered(recipient.getId());
        }
        stopwatch.split("e164-unlisted-network");
    } else {
        recipientDatabase.markUnregistered(recipient.getId());
    }
    if (Permissions.hasAll(context, Manifest.permission.WRITE_CONTACTS)) {
        updateContactsDatabase(context, Collections.singletonList(recipient.getId()), false, result.getNumberRewrites());
    }
    newRegisteredState = result.getRegisteredNumbers().size() > 0 ? RegisteredState.REGISTERED : RegisteredState.NOT_REGISTERED;
    if (newRegisteredState != originalRegisteredState) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob());
        ApplicationDependencies.getJobManager().add(new StorageSyncJob());
        if (notifyOfNewUsers && newRegisteredState == RegisteredState.REGISTERED && recipient.resolve().isSystemContact()) {
            notifyNewUsers(context, Collections.singletonList(recipient.getId()));
        }
        StorageSyncHelper.scheduleSyncForDataChange();
    }
    stopwatch.split("e164-disk");
    stopwatch.stop(TAG);
    return newRegisteredState;
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) StorageSyncJob(org.thoughtcrime.securesms.jobs.StorageSyncJob) RegisteredState(org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState) ACI(org.whispersystems.signalservice.api.push.ACI) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) WorkerThread(androidx.annotation.WorkerThread)

Example 3 with MultiDeviceContactUpdateJob

use of org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob in project Signal-Android by WhisperSystems.

the class MessageContentProcessor method handleSynchronizeRequestMessage.

private void handleSynchronizeRequestMessage(@NonNull RequestMessage message, long envelopeTimestamp) {
    if (SignalStore.account().isPrimaryDevice()) {
        log(envelopeTimestamp, "Synchronize request message.");
    } else {
        log(envelopeTimestamp, "Linked device ignoring synchronize request message.");
        return;
    }
    if (message.isContactsRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob(true));
    }
    if (message.isGroupsRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceGroupUpdateJob());
    }
    if (message.isBlockedListRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceBlockedUpdateJob());
    }
    if (message.isConfigurationRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(context), TextSecurePreferences.isTypingIndicatorsEnabled(context), TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(context), SignalStore.settings().isLinkPreviewsEnabled()));
        ApplicationDependencies.getJobManager().add(new MultiDeviceStickerPackSyncJob());
    }
    if (message.isKeysRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDeviceKeysUpdateJob());
    }
    if (message.isPniIdentityRequest()) {
        ApplicationDependencies.getJobManager().add(new MultiDevicePniIdentityUpdateJob());
    }
}
Also used : MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) MultiDeviceGroupUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob) MultiDeviceConfigurationUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob) MultiDeviceKeysUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceKeysUpdateJob) MultiDevicePniIdentityUpdateJob(org.thoughtcrime.securesms.jobs.MultiDevicePniIdentityUpdateJob) MultiDeviceBlockedUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob) MultiDeviceStickerPackSyncJob(org.thoughtcrime.securesms.jobs.MultiDeviceStickerPackSyncJob)

Example 4 with MultiDeviceContactUpdateJob

use of org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob in project Signal-Android by WhisperSystems.

the class RecipientIdJobMigrationTest method migrate_multiDeviceContactUpdateJob.

@Test
public void migrate_multiDeviceContactUpdateJob() throws Exception {
    JobData testData = new JobData("MultiDeviceContactUpdateJob", "MultiDeviceContactUpdateJob", new Data.Builder().putBoolean("force_sync", false).putString("address", "+16101234567").build());
    mockRecipientResolve("+16101234567", 1);
    RecipientIdJobMigration subject = new RecipientIdJobMigration(mock(Application.class));
    JobData converted = subject.migrate(testData);
    assertEquals("MultiDeviceContactUpdateJob", converted.getFactoryKey());
    assertEquals("MultiDeviceContactUpdateJob", converted.getQueueKey());
    assertFalse(converted.getData().getBoolean("force_sync"));
    assertFalse(converted.getData().hasString("address"));
    assertEquals("1", converted.getData().getString("recipient"));
    new MultiDeviceContactUpdateJob.Factory().create(mock(Job.Parameters.class), converted.getData());
}
Also used : MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) JobData(org.thoughtcrime.securesms.jobmanager.JobMigration.JobData) Application(android.app.Application) PrepareForTest(org.powermock.core.classloader.annotations.PrepareForTest) Test(org.junit.Test)

Example 5 with MultiDeviceContactUpdateJob

use of org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob in project Signal-Android by WhisperSystems.

the class DirectoryHelper method refreshDirectoryFor.

public static UserCapabilities refreshDirectoryFor(@NonNull Context context, @Nullable MasterSecret masterSecret, @NonNull Recipients recipients, @NonNull String localNumber) throws IOException {
    try {
        TextSecureDirectory directory = TextSecureDirectory.getInstance(context);
        SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
        String number = Util.canonicalizeNumber(context, recipients.getPrimaryRecipient().getNumber());
        Optional<ContactTokenDetails> details = accountManager.getContact(number);
        if (details.isPresent()) {
            directory.setNumber(details.get(), true);
            RefreshResult result = updateContactsDatabase(context, localNumber, details.get());
            if (!result.getNewUsers().isEmpty() && TextSecurePreferences.isMultiDevice(context)) {
                ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context));
            }
            if (!result.isFresh()) {
                notifyNewUsers(context, masterSecret, result.getNewUsers());
            }
            return new UserCapabilities(Capability.SUPPORTED, details.get().isVoice() ? Capability.SUPPORTED : Capability.UNSUPPORTED, details.get().isVideo() ? Capability.SUPPORTED : Capability.UNSUPPORTED);
        } else {
            ContactTokenDetails absent = new ContactTokenDetails();
            absent.setNumber(number);
            directory.setNumber(absent, false);
            return UserCapabilities.UNSUPPORTED;
        }
    } catch (InvalidNumberException e) {
        Log.w(TAG, e);
        return UserCapabilities.UNSUPPORTED;
    }
}
Also used : MultiDeviceContactUpdateJob(org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob) InvalidNumberException(org.whispersystems.signalservice.api.util.InvalidNumberException) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) ContactTokenDetails(org.whispersystems.signalservice.api.push.ContactTokenDetails) TextSecureDirectory(org.thoughtcrime.securesms.database.TextSecureDirectory)

Aggregations

MultiDeviceContactUpdateJob (org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob)6 RecipientDatabase (org.thoughtcrime.securesms.database.RecipientDatabase)3 WorkerThread (androidx.annotation.WorkerThread)2 RegisteredState (org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState)2 StorageSyncJob (org.thoughtcrime.securesms.jobs.StorageSyncJob)2 Stopwatch (org.thoughtcrime.securesms.util.Stopwatch)2 SignalServiceAccountManager (org.whispersystems.signalservice.api.SignalServiceAccountManager)2 ACI (org.whispersystems.signalservice.api.push.ACI)2 ContactTokenDetails (org.whispersystems.signalservice.api.push.ContactTokenDetails)2 Manifest (android.Manifest)1 Account (android.accounts.Account)1 AccountManager (android.accounts.AccountManager)1 Application (android.app.Application)1 ContentResolver (android.content.ContentResolver)1 Context (android.content.Context)1 OperationApplicationException (android.content.OperationApplicationException)1 Cursor (android.database.Cursor)1 RemoteException (android.os.RemoteException)1 ContactsContract (android.provider.ContactsContract)1 TextUtils (android.text.TextUtils)1