Search in sources :

Example 26 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by signalapp.

the class DirectoryHelper method refreshDirectoryFor.

@WorkerThread
public static void refreshDirectoryFor(@NonNull Context context, @NonNull List<Recipient> recipients, boolean notifyOfNewUsers) throws IOException {
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    for (Recipient recipient : recipients) {
        if (recipient.hasServiceId() && !recipient.hasE164()) {
            if (ApplicationDependencies.getSignalServiceAccountManager().isIdentifierRegistered(recipient.requireServiceId())) {
                recipientDatabase.markRegistered(recipient.getId(), recipient.requireServiceId());
            } else {
                recipientDatabase.markUnregistered(recipient.getId());
            }
        }
    }
    Set<String> numbers = Stream.of(recipients).filter(Recipient::hasE164).map(Recipient::requireE164).collect(Collectors.toSet());
    refreshNumbers(context, numbers, numbers, notifyOfNewUsers, false);
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) Recipient(org.thoughtcrime.securesms.recipients.Recipient) WorkerThread(androidx.annotation.WorkerThread)

Example 27 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by signalapp.

the class StorageForcePushJob method onRun.

@Override
protected void onRun() throws IOException, RetryLaterException {
    if (SignalStore.account().isLinkedDevice()) {
        Log.i(TAG, "Only the primary device can force push");
        return;
    }
    StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    UnknownStorageIdDatabase storageIdDatabase = SignalDatabase.unknownStorageIds();
    long currentVersion = accountManager.getStorageManifestVersion();
    Map<RecipientId, StorageId> oldContactStorageIds = recipientDatabase.getContactStorageSyncIdsMap();
    long newVersion = currentVersion + 1;
    Map<RecipientId, StorageId> newContactStorageIds = generateContactStorageIds(oldContactStorageIds);
    List<SignalStorageRecord> inserts = Stream.of(oldContactStorageIds.keySet()).map(recipientDatabase::getRecordForSync).withoutNulls().map(s -> StorageSyncModels.localToRemoteRecord(s, Objects.requireNonNull(newContactStorageIds.get(s.getId())).getRaw())).toList();
    SignalStorageRecord accountRecord = StorageSyncHelper.buildAccountRecord(context, Recipient.self().fresh());
    List<StorageId> allNewStorageIds = new ArrayList<>(newContactStorageIds.values());
    inserts.add(accountRecord);
    allNewStorageIds.add(accountRecord.getId());
    SignalStorageManifest manifest = new SignalStorageManifest(newVersion, allNewStorageIds);
    StorageSyncValidations.validateForcePush(manifest, inserts, Recipient.self().fresh());
    try {
        if (newVersion > 1) {
            Log.i(TAG, String.format(Locale.ENGLISH, "Force-pushing data. Inserting %d IDs.", inserts.size()));
            if (accountManager.resetStorageRecords(storageServiceKey, manifest, inserts).isPresent()) {
                Log.w(TAG, "Hit a conflict. Trying again.");
                throw new RetryLaterException();
            }
        } else {
            Log.i(TAG, String.format(Locale.ENGLISH, "First version, normal push. Inserting %d IDs.", inserts.size()));
            if (accountManager.writeStorageRecords(storageServiceKey, manifest, inserts, Collections.emptyList()).isPresent()) {
                Log.w(TAG, "Hit a conflict. Trying again.");
                throw new RetryLaterException();
            }
        }
    } catch (InvalidKeyException e) {
        Log.w(TAG, "Hit an invalid key exception, which likely indicates a conflict.");
        throw new RetryLaterException(e);
    }
    Log.i(TAG, "Force push succeeded. Updating local manifest version to: " + newVersion);
    SignalStore.storageService().setManifest(manifest);
    recipientDatabase.applyStorageIdUpdates(newContactStorageIds);
    recipientDatabase.applyStorageIdUpdates(Collections.singletonMap(Recipient.self().getId(), accountRecord.getId()));
    storageIdDatabase.deleteAll();
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) StorageSyncValidations(org.thoughtcrime.securesms.storage.StorageSyncValidations) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) Stream(com.annimon.stream.Stream) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) StorageSyncModels(org.thoughtcrime.securesms.storage.StorageSyncModels) HashMap(java.util.HashMap) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) ArrayList(java.util.ArrayList) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) StorageSyncHelper(org.thoughtcrime.securesms.storage.StorageSyncHelper) Locale(java.util.Locale) Map(java.util.Map) Recipient(org.thoughtcrime.securesms.recipients.Recipient) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) StorageId(org.whispersystems.signalservice.api.storage.StorageId) PushNetworkException(org.whispersystems.signalservice.api.push.exceptions.PushNetworkException) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) UnknownStorageIdDatabase(org.thoughtcrime.securesms.database.UnknownStorageIdDatabase) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) Objects(java.util.Objects) TimeUnit(java.util.concurrent.TimeUnit) Log(org.signal.core.util.logging.Log) List(java.util.List) Job(org.thoughtcrime.securesms.jobmanager.Job) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) ArrayList(java.util.ArrayList) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) StorageId(org.whispersystems.signalservice.api.storage.StorageId) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) UnknownStorageIdDatabase(org.thoughtcrime.securesms.database.UnknownStorageIdDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey)

Example 28 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by signalapp.

the class PushGroupSendJob method onPushSend.

@Override
public void onPushSend() throws IOException, MmsException, NoSuchMessageException, RetryLaterException {
    SignalLocalMetrics.GroupMessageSend.onJobStarted(messageId);
    MessageDatabase database = SignalDatabase.mms();
    OutgoingMediaMessage message = database.getOutgoingMessage(messageId);
    long threadId = database.getMessageRecord(messageId).getThreadId();
    Set<NetworkFailure> existingNetworkFailures = message.getNetworkFailures();
    Set<IdentityKeyMismatch> existingIdentityMismatches = message.getIdentityKeyMismatches();
    ApplicationDependencies.getJobManager().cancelAllInQueue(TypingSendJob.getQueue(threadId));
    if (database.isSent(messageId)) {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Message " + messageId + " was already sent. Ignoring.");
        return;
    }
    Recipient groupRecipient = message.getRecipient().resolve();
    if (!groupRecipient.isPushGroup()) {
        throw new MmsException("Message recipient isn't a group!");
    }
    if (groupRecipient.isPushV1Group()) {
        throw new MmsException("No GV1 messages can be sent anymore!");
    }
    try {
        log(TAG, String.valueOf(message.getSentTimeMillis()), "Sending message: " + messageId + ", Recipient: " + message.getRecipient().getId() + ", Thread: " + threadId + ", Attachments: " + buildAttachmentString(message.getAttachments()));
        if (!groupRecipient.resolve().isProfileSharing() && !database.isGroupQuitMessage(messageId)) {
            RecipientUtil.shareProfileIfFirstSecureMessage(context, groupRecipient);
        }
        List<Recipient> target;
        if (filterRecipient != null)
            target = Collections.singletonList(Recipient.resolved(filterRecipient));
        else if (!existingNetworkFailures.isEmpty())
            target = Stream.of(existingNetworkFailures).map(nf -> nf.getRecipientId(context)).distinct().map(Recipient::resolved).toList();
        else
            target = Stream.of(getGroupMessageRecipients(groupRecipient.requireGroupId(), messageId)).distinctBy(Recipient::getId).toList();
        RecipientAccessList accessList = new RecipientAccessList(target);
        List<SendMessageResult> results = deliver(message, groupRecipient, target);
        Log.i(TAG, JobLogger.format(this, "Finished send."));
        List<NetworkFailure> networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(accessList.requireIdByAddress(result.getAddress()))).toList();
        List<IdentityKeyMismatch> identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(accessList.requireIdByAddress(result.getAddress()), result.getIdentityFailure().getIdentityKey())).toList();
        ProofRequiredException proofRequired = Stream.of(results).filter(r -> r.getProofRequiredFailure() != null).findLast().map(SendMessageResult::getProofRequiredFailure).orElse(null);
        List<SendMessageResult> successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList();
        List<Pair<RecipientId, Boolean>> successUnidentifiedStatus = Stream.of(successes).map(result -> new Pair<>(accessList.requireIdByAddress(result.getAddress()), result.getSuccess().isUnidentified())).toList();
        Set<RecipientId> successIds = Stream.of(successUnidentifiedStatus).map(Pair::first).collect(Collectors.toSet());
        List<NetworkFailure> resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
        List<IdentityKeyMismatch> resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successIds.contains(failure.getRecipientId(context))).toList();
        List<RecipientId> unregisteredRecipients = Stream.of(results).filter(SendMessageResult::isUnregisteredFailure).map(result -> RecipientId.from(result.getAddress())).toList();
        if (networkFailures.size() > 0 || identityMismatches.size() > 0 || proofRequired != null || unregisteredRecipients.size() > 0) {
            Log.w(TAG, String.format(Locale.US, "Failed to send to some recipients. Network: %d, Identity: %d, ProofRequired: %s, Unregistered: %d", networkFailures.size(), identityMismatches.size(), proofRequired != null, unregisteredRecipients.size()));
        }
        RecipientDatabase recipientDatabase = SignalDatabase.recipients();
        for (RecipientId unregistered : unregisteredRecipients) {
            recipientDatabase.markUnregistered(unregistered);
        }
        existingNetworkFailures.removeAll(resolvedNetworkFailures);
        existingNetworkFailures.addAll(networkFailures);
        database.setNetworkFailures(messageId, existingNetworkFailures);
        existingIdentityMismatches.removeAll(resolvedIdentityFailures);
        existingIdentityMismatches.addAll(identityMismatches);
        database.setMismatchedIdentities(messageId, existingIdentityMismatches);
        SignalDatabase.groupReceipts().setUnidentified(successUnidentifiedStatus, messageId);
        if (proofRequired != null) {
            handleProofRequiredException(proofRequired, groupRecipient, threadId, messageId, true);
        }
        if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) {
            database.markAsSent(messageId, true);
            markAttachmentsUploaded(messageId, message);
            if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) {
                database.markExpireStarted(messageId);
                ApplicationDependencies.getExpiringMessageManager().scheduleDeletion(messageId, true, message.getExpiresIn());
            }
            if (message.isViewOnce()) {
                SignalDatabase.attachments().deleteAttachmentFilesForViewOnceMessage(messageId);
            }
        } else if (!identityMismatches.isEmpty()) {
            Log.w(TAG, "Failing because there were " + identityMismatches.size() + " identity mismatches.");
            database.markAsSentFailed(messageId);
            notifyMediaMessageDeliveryFailed(context, messageId);
            Set<RecipientId> mismatchRecipientIds = Stream.of(identityMismatches).map(mismatch -> mismatch.getRecipientId(context)).collect(Collectors.toSet());
            RetrieveProfileJob.enqueue(mismatchRecipientIds);
        } else if (!networkFailures.isEmpty()) {
            Log.w(TAG, "Retrying because there were " + networkFailures.size() + " network failures.");
            throw new RetryLaterException();
        }
    } catch (UntrustedIdentityException | UndeliverableMessageException e) {
        warn(TAG, String.valueOf(message.getSentTimeMillis()), e);
        database.markAsSentFailed(messageId);
        notifyMediaMessageDeliveryFailed(context, messageId);
    }
    SignalLocalMetrics.GroupMessageSend.onJobFinished(messageId);
}
Also used : ServerRejectedException(org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException) SignalServiceDataMessage(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) RecipientUtil(org.thoughtcrime.securesms.recipients.RecipientUtil) Quote(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Quote) MessageGroupContext(org.thoughtcrime.securesms.mms.MessageGroupContext) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Locale(java.util.Locale) Recipient(org.thoughtcrime.securesms.recipients.Recipient) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) SignalServiceAttachment(org.whispersystems.signalservice.api.messages.SignalServiceAttachment) Set(java.util.Set) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) ByteString(com.google.protobuf.ByteString) Log(org.signal.core.util.logging.Log) GroupContextV2(org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2) List(java.util.List) Nullable(androidx.annotation.Nullable) SignalServiceGroupV2(org.whispersystems.signalservice.api.messages.SignalServiceGroupV2) GroupId(org.thoughtcrime.securesms.groups.GroupId) Job(org.thoughtcrime.securesms.jobmanager.Job) SharedContact(org.whispersystems.signalservice.api.messages.shared.SharedContact) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) GroupUtil(org.thoughtcrime.securesms.util.GroupUtil) NoSuchMessageException(org.thoughtcrime.securesms.database.NoSuchMessageException) Attachment(org.thoughtcrime.securesms.attachments.Attachment) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) Preview(org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview) Context(android.content.Context) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) ContentHint(org.whispersystems.signalservice.api.crypto.ContentHint) Stream(com.annimon.stream.Stream) JobLogger(org.thoughtcrime.securesms.jobmanager.JobLogger) SignalLocalMetrics(org.thoughtcrime.securesms.util.SignalLocalMetrics) WorkerThread(androidx.annotation.WorkerThread) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) Pair(org.whispersystems.libsignal.util.Pair) GroupSendUtil(org.thoughtcrime.securesms.messages.GroupSendUtil) GroupReceiptInfo(org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo) MessageId(org.thoughtcrime.securesms.database.model.MessageId) Collectors(com.annimon.stream.Collectors) GroupReceiptDatabase(org.thoughtcrime.securesms.database.GroupReceiptDatabase) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) MmsException(org.thoughtcrime.securesms.mms.MmsException) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) Optional(org.whispersystems.libsignal.util.guava.Optional) TimeUnit(java.util.concurrent.TimeUnit) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Set(java.util.Set) UntrustedIdentityException(org.whispersystems.signalservice.api.crypto.UntrustedIdentityException) OutgoingMediaMessage(org.thoughtcrime.securesms.mms.OutgoingMediaMessage) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) MmsException(org.thoughtcrime.securesms.mms.MmsException) UndeliverableMessageException(org.thoughtcrime.securesms.transport.UndeliverableMessageException) Pair(org.whispersystems.libsignal.util.Pair) MessageDatabase(org.thoughtcrime.securesms.database.MessageDatabase) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ProofRequiredException(org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException) NetworkFailure(org.thoughtcrime.securesms.database.documents.NetworkFailure) IdentityKeyMismatch(org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch) SendMessageResult(org.whispersystems.signalservice.api.messages.SendMessageResult) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) RecipientAccessList(org.thoughtcrime.securesms.util.RecipientAccessList)

Example 29 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by signalapp.

the class RetrieveProfileAvatarJob method onRun.

@Override
public void onRun() throws IOException {
    RecipientDatabase database = SignalDatabase.recipients();
    ProfileKey profileKey = ProfileKeyUtil.profileKeyOrNull(recipient.resolve().getProfileKey());
    if (profileKey == null) {
        Log.w(TAG, "Recipient profile key is gone!");
        return;
    }
    if (Util.equals(profileAvatar, recipient.resolve().getProfileAvatar())) {
        Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar);
        return;
    }
    if (TextUtils.isEmpty(profileAvatar)) {
        Log.w(TAG, "Removing profile avatar (no url) for: " + recipient.getId().serialize());
        AvatarHelper.delete(context, recipient.getId());
        database.setProfileAvatar(recipient.getId(), profileAvatar);
        return;
    }
    File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
    try {
        SignalServiceMessageReceiver receiver = ApplicationDependencies.getSignalServiceMessageReceiver();
        InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, AvatarHelper.AVATAR_DOWNLOAD_FAILSAFE_MAX_SIZE);
        try {
            AvatarHelper.setAvatar(context, recipient.getId(), avatarStream);
            if (recipient.isSelf()) {
                SignalStore.misc().markHasEverHadAnAvatar();
            }
        } catch (AssertionError e) {
            throw new IOException("Failed to copy stream. Likely a Conscrypt issue.", e);
        }
    } catch (PushNetworkException e) {
        if (e.getCause() instanceof NonSuccessfulResponseCodeException) {
            Log.w(TAG, "Removing profile avatar (no image available) for: " + recipient.getId().serialize());
            AvatarHelper.delete(context, recipient.getId());
        } else {
            throw e;
        }
    } finally {
        if (downloadDestination != null)
            downloadDestination.delete();
    }
    database.setProfileAvatar(recipient.getId(), profileAvatar);
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) PushNetworkException(org.whispersystems.signalservice.api.push.exceptions.PushNetworkException) SignalServiceMessageReceiver(org.whispersystems.signalservice.api.SignalServiceMessageReceiver) InputStream(java.io.InputStream) NonSuccessfulResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException) IOException(java.io.IOException) File(java.io.File) ProfileKey(org.signal.zkgroup.profiles.ProfileKey)

Example 30 with RecipientDatabase

use of org.thoughtcrime.securesms.database.RecipientDatabase in project Signal-Android by signalapp.

the class RetrieveProfileJob method setProfileKeyCredential.

private void setProfileKeyCredential(@NonNull Recipient recipient, @NonNull ProfileKey recipientProfileKey, @NonNull ProfileKeyCredential credential) {
    RecipientDatabase recipientDatabase = SignalDatabase.recipients();
    recipientDatabase.setProfileKeyCredential(recipient.getId(), recipientProfileKey, credential);
}
Also used : RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase)

Aggregations

RecipientDatabase (org.thoughtcrime.securesms.database.RecipientDatabase)72 Recipient (org.thoughtcrime.securesms.recipients.Recipient)32 WorkerThread (androidx.annotation.WorkerThread)30 RecipientId (org.thoughtcrime.securesms.recipients.RecipientId)20 NonNull (androidx.annotation.NonNull)18 IOException (java.io.IOException)14 Nullable (androidx.annotation.Nullable)10 ArrayList (java.util.ArrayList)10 Log (org.signal.core.util.logging.Log)10 SignalDatabase (org.thoughtcrime.securesms.database.SignalDatabase)10 ACI (org.whispersystems.signalservice.api.push.ACI)10 Context (android.content.Context)8 Stream (com.annimon.stream.Stream)8 Collections (java.util.Collections)8 List (java.util.List)8 TimeUnit (java.util.concurrent.TimeUnit)8 ProfileKey (org.signal.zkgroup.profiles.ProfileKey)8 ApplicationDependencies (org.thoughtcrime.securesms.dependencies.ApplicationDependencies)8 Optional (org.whispersystems.libsignal.util.guava.Optional)8 HashSet (java.util.HashSet)7