Search in sources :

Example 1 with StorageKey

use of org.whispersystems.signalservice.api.storage.StorageKey in project Signal-Android by WhisperSystems.

the class StorageSyncJob method performSync.

private boolean performSync() throws IOException, RetryLaterException, InvalidKeyException {
    final Stopwatch stopwatch = new Stopwatch("StorageSync");
    final SQLiteDatabase db = SignalDatabase.getRawDatabase();
    final SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
    final UnknownStorageIdDatabase storageIdDatabase = SignalDatabase.unknownStorageIds();
    final StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    final SignalStorageManifest localManifest = SignalStore.storageService().getManifest();
    final SignalStorageManifest remoteManifest = accountManager.getStorageManifestIfDifferentVersion(storageServiceKey, localManifest.getVersion()).or(localManifest);
    stopwatch.split("remote-manifest");
    Recipient self = freshSelf();
    boolean needsMultiDeviceSync = false;
    boolean needsForcePush = false;
    if (self.getStorageServiceId() == null) {
        Log.w(TAG, "No storageId for self. Generating.");
        SignalDatabase.recipients().updateStorageId(self.getId(), StorageSyncHelper.generateKey());
        self = freshSelf();
    }
    Log.i(TAG, "Our version: " + localManifest.getVersion() + ", their version: " + remoteManifest.getVersion());
    if (remoteManifest.getVersion() > localManifest.getVersion()) {
        Log.i(TAG, "[Remote Sync] Newer manifest version found!");
        List<StorageId> localStorageIdsBeforeMerge = getAllLocalStorageIds(context, self);
        IdDifferenceResult idDifference = StorageSyncHelper.findIdDifference(remoteManifest.getStorageIds(), localStorageIdsBeforeMerge);
        if (idDifference.hasTypeMismatches() && SignalStore.account().isPrimaryDevice()) {
            Log.w(TAG, "[Remote Sync] Found type mismatches in the ID sets! Scheduling a force push after this sync completes.");
            needsForcePush = true;
        }
        Log.i(TAG, "[Remote Sync] Pre-Merge ID Difference :: " + idDifference);
        stopwatch.split("remote-id-diff");
        if (!idDifference.isEmpty()) {
            Log.i(TAG, "[Remote Sync] Retrieving records for key difference.");
            List<SignalStorageRecord> remoteOnly = accountManager.readStorageRecords(storageServiceKey, idDifference.getRemoteOnlyIds());
            stopwatch.split("remote-records");
            if (remoteOnly.size() != idDifference.getRemoteOnlyIds().size()) {
                Log.w(TAG, "[Remote Sync] Could not find all remote-only records! Requested: " + idDifference.getRemoteOnlyIds().size() + ", Found: " + remoteOnly.size() + ". These stragglers should naturally get deleted during the sync.");
            }
            List<SignalContactRecord> remoteContacts = new LinkedList<>();
            List<SignalGroupV1Record> remoteGv1 = new LinkedList<>();
            List<SignalGroupV2Record> remoteGv2 = new LinkedList<>();
            List<SignalAccountRecord> remoteAccount = new LinkedList<>();
            List<SignalStorageRecord> remoteUnknown = new LinkedList<>();
            for (SignalStorageRecord remote : remoteOnly) {
                if (remote.getContact().isPresent()) {
                    remoteContacts.add(remote.getContact().get());
                } else if (remote.getGroupV1().isPresent()) {
                    remoteGv1.add(remote.getGroupV1().get());
                } else if (remote.getGroupV2().isPresent()) {
                    remoteGv2.add(remote.getGroupV2().get());
                } else if (remote.getAccount().isPresent()) {
                    remoteAccount.add(remote.getAccount().get());
                } else if (remote.getId().isUnknown()) {
                    remoteUnknown.add(remote);
                } else {
                    Log.w(TAG, "Bad record! Type is a known value (" + remote.getId().getType() + "), but doesn't have a matching inner record. Dropping it.");
                }
            }
            db.beginTransaction();
            try {
                self = freshSelf();
                Log.i(TAG, "[Remote Sync] Remote-Only :: Contacts: " + remoteContacts.size() + ", GV1: " + remoteGv1.size() + ", GV2: " + remoteGv2.size() + ", Account: " + remoteAccount.size());
                new ContactRecordProcessor(context, self).process(remoteContacts, StorageSyncHelper.KEY_GENERATOR);
                new GroupV1RecordProcessor(context).process(remoteGv1, StorageSyncHelper.KEY_GENERATOR);
                new GroupV2RecordProcessor(context).process(remoteGv2, StorageSyncHelper.KEY_GENERATOR);
                self = freshSelf();
                new AccountRecordProcessor(context, self).process(remoteAccount, StorageSyncHelper.KEY_GENERATOR);
                List<SignalStorageRecord> unknownInserts = remoteUnknown;
                List<StorageId> unknownDeletes = Stream.of(idDifference.getLocalOnlyIds()).filter(StorageId::isUnknown).toList();
                Log.i(TAG, "[Remote Sync] Unknowns :: " + unknownInserts.size() + " inserts, " + unknownDeletes.size() + " deletes");
                storageIdDatabase.insert(unknownInserts);
                storageIdDatabase.delete(unknownDeletes);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
                ApplicationDependencies.getDatabaseObserver().notifyConversationListListeners();
                stopwatch.split("remote-merge-transaction");
            }
        } else {
            Log.i(TAG, "[Remote Sync] Remote version was newer, but there were no remote-only IDs.");
        }
    } else if (remoteManifest.getVersion() < localManifest.getVersion()) {
        Log.w(TAG, "[Remote Sync] Remote version was older. User might have switched accounts.");
    }
    if (remoteManifest != localManifest) {
        Log.i(TAG, "[Remote Sync] Saved new manifest. Now at version: " + remoteManifest.getVersion());
        SignalStore.storageService().setManifest(remoteManifest);
    }
    Log.i(TAG, "We are up-to-date with the remote storage state.");
    final WriteOperationResult remoteWriteOperation;
    db.beginTransaction();
    try {
        self = freshSelf();
        List<StorageId> localStorageIds = getAllLocalStorageIds(context, self);
        IdDifferenceResult idDifference = StorageSyncHelper.findIdDifference(remoteManifest.getStorageIds(), localStorageIds);
        List<SignalStorageRecord> remoteInserts = buildLocalStorageRecords(context, self, idDifference.getLocalOnlyIds());
        List<byte[]> remoteDeletes = Stream.of(idDifference.getRemoteOnlyIds()).map(StorageId::getRaw).toList();
        Log.i(TAG, "ID Difference :: " + idDifference);
        remoteWriteOperation = new WriteOperationResult(new SignalStorageManifest(remoteManifest.getVersion() + 1, localStorageIds), remoteInserts, remoteDeletes);
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
        stopwatch.split("local-data-transaction");
    }
    if (!remoteWriteOperation.isEmpty()) {
        Log.i(TAG, "We have something to write remotely.");
        Log.i(TAG, "WriteOperationResult :: " + remoteWriteOperation);
        StorageSyncValidations.validate(remoteWriteOperation, remoteManifest, needsForcePush, self);
        Optional<SignalStorageManifest> conflict = accountManager.writeStorageRecords(storageServiceKey, remoteWriteOperation.getManifest(), remoteWriteOperation.getInserts(), remoteWriteOperation.getDeletes());
        if (conflict.isPresent()) {
            Log.w(TAG, "Hit a conflict when trying to resolve the conflict! Retrying.");
            throw new RetryLaterException();
        }
        Log.i(TAG, "Saved new manifest. Now at version: " + remoteWriteOperation.getManifest().getVersion());
        SignalStore.storageService().setManifest(remoteWriteOperation.getManifest());
        stopwatch.split("remote-write");
        needsMultiDeviceSync = true;
    } else {
        Log.i(TAG, "No remote writes needed. Still at version: " + remoteManifest.getVersion());
    }
    if (needsForcePush && SignalStore.account().isPrimaryDevice()) {
        Log.w(TAG, "Scheduling a force push.");
        ApplicationDependencies.getJobManager().add(new StorageForcePushJob());
    }
    stopwatch.stop(TAG);
    return needsMultiDeviceSync;
}
Also used : SignalContactRecord(org.whispersystems.signalservice.api.storage.SignalContactRecord) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) StorageId(org.whispersystems.signalservice.api.storage.StorageId) GroupV2RecordProcessor(org.thoughtcrime.securesms.storage.GroupV2RecordProcessor) SignalGroupV2Record(org.whispersystems.signalservice.api.storage.SignalGroupV2Record) UnknownStorageIdDatabase(org.thoughtcrime.securesms.database.UnknownStorageIdDatabase) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey) SignalAccountRecord(org.whispersystems.signalservice.api.storage.SignalAccountRecord) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) IdDifferenceResult(org.thoughtcrime.securesms.storage.StorageSyncHelper.IdDifferenceResult) GroupV1RecordProcessor(org.thoughtcrime.securesms.storage.GroupV1RecordProcessor) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) Recipient(org.thoughtcrime.securesms.recipients.Recipient) WriteOperationResult(org.thoughtcrime.securesms.storage.StorageSyncHelper.WriteOperationResult) LinkedList(java.util.LinkedList) SignalGroupV1Record(org.whispersystems.signalservice.api.storage.SignalGroupV1Record) AccountRecordProcessor(org.thoughtcrime.securesms.storage.AccountRecordProcessor) SQLiteDatabase(net.zetetic.database.sqlcipher.SQLiteDatabase) ContactRecordProcessor(org.thoughtcrime.securesms.storage.ContactRecordProcessor) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException)

Example 2 with StorageKey

use of org.whispersystems.signalservice.api.storage.StorageKey in project Signal-Android by WhisperSystems.

the class MultiDeviceKeysUpdateJob method onRun.

@Override
public void onRun() throws IOException, UntrustedIdentityException {
    if (!Recipient.self().isRegistered()) {
        throw new NotPushRegisteredException();
    }
    if (!TextSecurePreferences.isMultiDevice(context)) {
        Log.i(TAG, "Not multi device, aborting...");
        return;
    }
    if (SignalStore.account().isLinkedDevice()) {
        Log.i(TAG, "Not primary device, aborting...");
        return;
    }
    SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
    StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    messageSender.sendSyncMessage(SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.fromNullable(storageServiceKey))), UnidentifiedAccessUtil.getAccessForSync(context));
}
Also used : NotPushRegisteredException(org.thoughtcrime.securesms.net.NotPushRegisteredException) SignalServiceMessageSender(org.whispersystems.signalservice.api.SignalServiceMessageSender) KeysMessage(org.whispersystems.signalservice.api.messages.multidevice.KeysMessage) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey)

Example 3 with StorageKey

use of org.whispersystems.signalservice.api.storage.StorageKey in project Signal-Android by WhisperSystems.

the class SignalServiceContent method createSynchronizeMessage.

private static SignalServiceSyncMessage createSynchronizeMessage(SignalServiceMetadata metadata, SignalServiceProtos.SyncMessage content) throws ProtocolInvalidKeyException, UnsupportedDataMessageException, InvalidMessageStructureException {
    if (content.hasSent()) {
        Map<SignalServiceAddress, Boolean> unidentifiedStatuses = new HashMap<>();
        SignalServiceProtos.SyncMessage.Sent sentContent = content.getSent();
        SignalServiceDataMessage dataMessage = createSignalServiceMessage(metadata, sentContent.getMessage());
        Optional<SignalServiceAddress> address = SignalServiceAddress.isValidAddress(sentContent.getDestinationUuid(), sentContent.getDestinationE164()) ? Optional.of(new SignalServiceAddress(ACI.parseOrThrow(sentContent.getDestinationUuid()), sentContent.getDestinationE164())) : Optional.<SignalServiceAddress>absent();
        if (!address.isPresent() && !dataMessage.getGroupContext().isPresent()) {
            throw new InvalidMessageStructureException("SyncMessage missing both destination and group ID!");
        }
        for (SignalServiceProtos.SyncMessage.Sent.UnidentifiedDeliveryStatus status : sentContent.getUnidentifiedStatusList()) {
            if (SignalServiceAddress.isValidAddress(status.getDestinationUuid(), status.getDestinationE164())) {
                SignalServiceAddress recipient = new SignalServiceAddress(ACI.parseOrThrow(status.getDestinationUuid()), status.getDestinationE164());
                unidentifiedStatuses.put(recipient, status.getUnidentified());
            } else {
                Log.w(TAG, "Encountered an invalid UnidentifiedDeliveryStatus in a SentTranscript! Ignoring.");
            }
        }
        return SignalServiceSyncMessage.forSentTranscript(new SentTranscriptMessage(address, sentContent.getTimestamp(), dataMessage, sentContent.getExpirationStartTimestamp(), unidentifiedStatuses, sentContent.getIsRecipientUpdate()));
    }
    if (content.hasRequest()) {
        return SignalServiceSyncMessage.forRequest(new RequestMessage(content.getRequest()));
    }
    if (content.getReadList().size() > 0) {
        List<ReadMessage> readMessages = new LinkedList<>();
        for (SignalServiceProtos.SyncMessage.Read read : content.getReadList()) {
            if (SignalServiceAddress.isValidAddress(read.getSenderUuid(), read.getSenderE164())) {
                SignalServiceAddress address = new SignalServiceAddress(ACI.parseOrThrow(read.getSenderUuid()), read.getSenderE164());
                readMessages.add(new ReadMessage(address, read.getTimestamp()));
            } else {
                Log.w(TAG, "Encountered an invalid ReadMessage! Ignoring.");
            }
        }
        return SignalServiceSyncMessage.forRead(readMessages);
    }
    if (content.getViewedList().size() > 0) {
        List<ViewedMessage> viewedMessages = new LinkedList<>();
        for (SignalServiceProtos.SyncMessage.Viewed viewed : content.getViewedList()) {
            if (SignalServiceAddress.isValidAddress(viewed.getSenderUuid(), viewed.getSenderE164())) {
                SignalServiceAddress address = new SignalServiceAddress(ACI.parseOrThrow(viewed.getSenderUuid()), viewed.getSenderE164());
                viewedMessages.add(new ViewedMessage(address, viewed.getTimestamp()));
            } else {
                Log.w(TAG, "Encountered an invalid ReadMessage! Ignoring.");
            }
        }
        return SignalServiceSyncMessage.forViewed(viewedMessages);
    }
    if (content.hasViewOnceOpen()) {
        if (SignalServiceAddress.isValidAddress(content.getViewOnceOpen().getSenderUuid(), content.getViewOnceOpen().getSenderE164())) {
            SignalServiceAddress address = new SignalServiceAddress(ACI.parseOrThrow(content.getViewOnceOpen().getSenderUuid()), content.getViewOnceOpen().getSenderE164());
            ViewOnceOpenMessage timerRead = new ViewOnceOpenMessage(address, content.getViewOnceOpen().getTimestamp());
            return SignalServiceSyncMessage.forViewOnceOpen(timerRead);
        } else {
            throw new InvalidMessageStructureException("ViewOnceOpen message has no sender!");
        }
    }
    if (content.hasVerified()) {
        if (SignalServiceAddress.isValidAddress(content.getVerified().getDestinationUuid(), content.getVerified().getDestinationE164())) {
            try {
                SignalServiceProtos.Verified verified = content.getVerified();
                SignalServiceAddress destination = new SignalServiceAddress(ACI.parseOrThrow(verified.getDestinationUuid()), verified.getDestinationE164());
                IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0);
                VerifiedMessage.VerifiedState verifiedState;
                if (verified.getState() == SignalServiceProtos.Verified.State.DEFAULT) {
                    verifiedState = VerifiedMessage.VerifiedState.DEFAULT;
                } else if (verified.getState() == SignalServiceProtos.Verified.State.VERIFIED) {
                    verifiedState = VerifiedMessage.VerifiedState.VERIFIED;
                } else if (verified.getState() == SignalServiceProtos.Verified.State.UNVERIFIED) {
                    verifiedState = VerifiedMessage.VerifiedState.UNVERIFIED;
                } else {
                    throw new InvalidMessageStructureException("Unknown state: " + verified.getState().getNumber(), metadata.getSender().getIdentifier(), metadata.getSenderDevice());
                }
                return SignalServiceSyncMessage.forVerified(new VerifiedMessage(destination, identityKey, verifiedState, System.currentTimeMillis()));
            } catch (InvalidKeyException e) {
                throw new ProtocolInvalidKeyException(e, metadata.getSender().getIdentifier(), metadata.getSenderDevice());
            }
        } else {
            throw new InvalidMessageStructureException("Verified message has no sender!");
        }
    }
    if (content.getStickerPackOperationList().size() > 0) {
        List<StickerPackOperationMessage> operations = new LinkedList<>();
        for (SignalServiceProtos.SyncMessage.StickerPackOperation operation : content.getStickerPackOperationList()) {
            byte[] packId = operation.hasPackId() ? operation.getPackId().toByteArray() : null;
            byte[] packKey = operation.hasPackKey() ? operation.getPackKey().toByteArray() : null;
            StickerPackOperationMessage.Type type = null;
            if (operation.hasType()) {
                switch(operation.getType()) {
                    case INSTALL:
                        type = StickerPackOperationMessage.Type.INSTALL;
                        break;
                    case REMOVE:
                        type = StickerPackOperationMessage.Type.REMOVE;
                        break;
                }
            }
            operations.add(new StickerPackOperationMessage(packId, packKey, type));
        }
        return SignalServiceSyncMessage.forStickerPackOperations(operations);
    }
    if (content.hasBlocked()) {
        List<String> numbers = content.getBlocked().getNumbersList();
        List<String> uuids = content.getBlocked().getUuidsList();
        List<SignalServiceAddress> addresses = new ArrayList<>(numbers.size() + uuids.size());
        List<byte[]> groupIds = new ArrayList<>(content.getBlocked().getGroupIdsList().size());
        for (String uuid : uuids) {
            Optional<SignalServiceAddress> address = SignalServiceAddress.fromRaw(uuid, null);
            if (address.isPresent()) {
                addresses.add(address.get());
            }
        }
        for (ByteString groupId : content.getBlocked().getGroupIdsList()) {
            groupIds.add(groupId.toByteArray());
        }
        return SignalServiceSyncMessage.forBlocked(new BlockedListMessage(addresses, groupIds));
    }
    if (content.hasConfiguration()) {
        Boolean readReceipts = content.getConfiguration().hasReadReceipts() ? content.getConfiguration().getReadReceipts() : null;
        Boolean unidentifiedDeliveryIndicators = content.getConfiguration().hasUnidentifiedDeliveryIndicators() ? content.getConfiguration().getUnidentifiedDeliveryIndicators() : null;
        Boolean typingIndicators = content.getConfiguration().hasTypingIndicators() ? content.getConfiguration().getTypingIndicators() : null;
        Boolean linkPreviews = content.getConfiguration().hasLinkPreviews() ? content.getConfiguration().getLinkPreviews() : null;
        return SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.fromNullable(readReceipts), Optional.fromNullable(unidentifiedDeliveryIndicators), Optional.fromNullable(typingIndicators), Optional.fromNullable(linkPreviews)));
    }
    if (content.hasFetchLatest() && content.getFetchLatest().hasType()) {
        switch(content.getFetchLatest().getType()) {
            case LOCAL_PROFILE:
                return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE);
            case STORAGE_MANIFEST:
                return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.STORAGE_MANIFEST);
            case SUBSCRIPTION_STATUS:
                return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.SUBSCRIPTION_STATUS);
        }
    }
    if (content.hasMessageRequestResponse()) {
        MessageRequestResponseMessage.Type type;
        switch(content.getMessageRequestResponse().getType()) {
            case ACCEPT:
                type = MessageRequestResponseMessage.Type.ACCEPT;
                break;
            case DELETE:
                type = MessageRequestResponseMessage.Type.DELETE;
                break;
            case BLOCK:
                type = MessageRequestResponseMessage.Type.BLOCK;
                break;
            case BLOCK_AND_DELETE:
                type = MessageRequestResponseMessage.Type.BLOCK_AND_DELETE;
                break;
            default:
                type = MessageRequestResponseMessage.Type.UNKNOWN;
                break;
        }
        MessageRequestResponseMessage responseMessage;
        if (content.getMessageRequestResponse().hasGroupId()) {
            responseMessage = MessageRequestResponseMessage.forGroup(content.getMessageRequestResponse().getGroupId().toByteArray(), type);
        } else {
            Optional<SignalServiceAddress> address = SignalServiceAddress.fromRaw(content.getMessageRequestResponse().getThreadUuid(), content.getMessageRequestResponse().getThreadE164());
            if (address.isPresent()) {
                responseMessage = MessageRequestResponseMessage.forIndividual(address.get(), type);
            } else {
                throw new InvalidMessageStructureException("Message request response has an invalid thread identifier!");
            }
        }
        return SignalServiceSyncMessage.forMessageRequestResponse(responseMessage);
    }
    if (content.hasOutgoingPayment()) {
        SignalServiceProtos.SyncMessage.OutgoingPayment outgoingPayment = content.getOutgoingPayment();
        switch(outgoingPayment.getPaymentDetailCase()) {
            case MOBILECOIN:
                {
                    SignalServiceProtos.SyncMessage.OutgoingPayment.MobileCoin mobileCoin = outgoingPayment.getMobileCoin();
                    Money.MobileCoin amount = Money.picoMobileCoin(mobileCoin.getAmountPicoMob());
                    Money.MobileCoin fee = Money.picoMobileCoin(mobileCoin.getFeePicoMob());
                    ByteString address = mobileCoin.getRecipientAddress();
                    Optional<SignalServiceAddress> recipient = SignalServiceAddress.fromRaw(outgoingPayment.getRecipientUuid(), null);
                    return SignalServiceSyncMessage.forOutgoingPayment(new OutgoingPaymentMessage(recipient, amount, fee, mobileCoin.getReceipt(), mobileCoin.getLedgerBlockIndex(), mobileCoin.getLedgerBlockTimestamp(), address.isEmpty() ? Optional.absent() : Optional.of(address.toByteArray()), Optional.of(outgoingPayment.getNote()), mobileCoin.getOutputPublicKeysList(), mobileCoin.getSpentKeyImagesList()));
                }
            default:
                return SignalServiceSyncMessage.empty();
        }
    }
    if (content.hasKeys() && content.getKeys().hasStorageService()) {
        byte[] storageKey = content.getKeys().getStorageService().toByteArray();
        return SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.of(new StorageKey(storageKey))));
    }
    if (content.hasContacts()) {
        return SignalServiceSyncMessage.forContacts(new ContactsMessage(createAttachmentPointer(content.getContacts().getBlob()), content.getContacts().getComplete()));
    }
    return SignalServiceSyncMessage.empty();
}
Also used : HashMap(java.util.HashMap) ByteString(com.google.protobuf.ByteString) OutgoingPaymentMessage(org.whispersystems.signalservice.api.messages.multidevice.OutgoingPaymentMessage) ArrayList(java.util.ArrayList) ByteString(com.google.protobuf.ByteString) BlockedListMessage(org.whispersystems.signalservice.api.messages.multidevice.BlockedListMessage) RequestMessage(org.whispersystems.signalservice.api.messages.multidevice.RequestMessage) ViewedMessage(org.whispersystems.signalservice.api.messages.multidevice.ViewedMessage) SentTranscriptMessage(org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage) Optional(org.whispersystems.libsignal.util.guava.Optional) ContactsMessage(org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage) LinkedList(java.util.LinkedList) SignalServiceSyncMessage(org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage) ProtocolInvalidKeyException(org.signal.libsignal.metadata.ProtocolInvalidKeyException) SignalServiceProtos(org.whispersystems.signalservice.internal.push.SignalServiceProtos) ReadMessage(org.whispersystems.signalservice.api.messages.multidevice.ReadMessage) VerifiedMessage(org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage) InvalidMessageStructureException(org.whispersystems.signalservice.api.InvalidMessageStructureException) KeysMessage(org.whispersystems.signalservice.api.messages.multidevice.KeysMessage) ViewOnceOpenMessage(org.whispersystems.signalservice.api.messages.multidevice.ViewOnceOpenMessage) StickerPackOperationMessage(org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage) SignalServiceAddress(org.whispersystems.signalservice.api.push.SignalServiceAddress) ConfigurationMessage(org.whispersystems.signalservice.api.messages.multidevice.ConfigurationMessage) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey) IdentityKey(org.whispersystems.libsignal.IdentityKey) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException) ProtocolInvalidKeyException(org.signal.libsignal.metadata.ProtocolInvalidKeyException) MessageRequestResponseMessage(org.whispersystems.signalservice.api.messages.multidevice.MessageRequestResponseMessage)

Example 4 with StorageKey

use of org.whispersystems.signalservice.api.storage.StorageKey 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 5 with StorageKey

use of org.whispersystems.signalservice.api.storage.StorageKey in project Signal-Android by signalapp.

the class StorageAccountRestoreJob method onRun.

@Override
protected void onRun() throws Exception {
    SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
    StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey();
    Log.i(TAG, "Retrieving manifest...");
    Optional<SignalStorageManifest> manifest = accountManager.getStorageManifest(storageServiceKey);
    if (!manifest.isPresent()) {
        Log.w(TAG, "Manifest did not exist or was undecryptable (bad key). Not restoring. Force-pushing.");
        ApplicationDependencies.getJobManager().add(new StorageForcePushJob());
        return;
    }
    Log.i(TAG, "Resetting the local manifest to an empty state so that it will sync later.");
    SignalStore.storageService().setManifest(SignalStorageManifest.EMPTY);
    Optional<StorageId> accountId = manifest.get().getAccountStorageId();
    if (!accountId.isPresent()) {
        Log.w(TAG, "Manifest had no account record! Not restoring.");
        return;
    }
    Log.i(TAG, "Retrieving account record...");
    List<SignalStorageRecord> records = accountManager.readStorageRecords(storageServiceKey, Collections.singletonList(accountId.get()));
    SignalStorageRecord record = records.size() > 0 ? records.get(0) : null;
    if (record == null) {
        Log.w(TAG, "Could not find account record, even though we had an ID! Not restoring.");
        return;
    }
    SignalAccountRecord accountRecord = record.getAccount().orNull();
    if (accountRecord == null) {
        Log.w(TAG, "The storage record didn't actually have an account on it! Not restoring.");
        return;
    }
    Log.i(TAG, "Applying changes locally...");
    SignalDatabase.getRawDatabase().beginTransaction();
    try {
        StorageSyncHelper.applyAccountStorageSyncUpdates(context, Recipient.self(), accountRecord, false);
        SignalDatabase.getRawDatabase().setTransactionSuccessful();
    } finally {
        SignalDatabase.getRawDatabase().endTransaction();
    }
    JobManager jobManager = ApplicationDependencies.getJobManager();
    if (accountRecord.getAvatarUrlPath().isPresent()) {
        Log.i(TAG, "Fetching avatar...");
        Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RetrieveProfileAvatarJob(Recipient.self(), accountRecord.getAvatarUrlPath().get()), LIFESPAN / 2);
        if (state.isPresent()) {
            Log.i(TAG, "Avatar retrieved successfully. " + state.get());
        } else {
            Log.w(TAG, "Avatar retrieval did not complete in time (or otherwise failed).");
        }
    } else {
        Log.i(TAG, "No avatar present. Not fetching.");
    }
    Log.i(TAG, "Refreshing attributes...");
    Optional<JobTracker.JobState> state = jobManager.runSynchronously(new RefreshAttributesJob(), LIFESPAN / 2);
    if (state.isPresent()) {
        Log.i(TAG, "Attributes refreshed successfully. " + state.get());
    } else {
        Log.w(TAG, "Attribute refresh did not complete in time (or otherwise failed).");
    }
}
Also used : SignalAccountRecord(org.whispersystems.signalservice.api.storage.SignalAccountRecord) SignalStorageManifest(org.whispersystems.signalservice.api.storage.SignalStorageManifest) SignalServiceAccountManager(org.whispersystems.signalservice.api.SignalServiceAccountManager) SignalStorageRecord(org.whispersystems.signalservice.api.storage.SignalStorageRecord) JobManager(org.thoughtcrime.securesms.jobmanager.JobManager) StorageId(org.whispersystems.signalservice.api.storage.StorageId) StorageKey(org.whispersystems.signalservice.api.storage.StorageKey)

Aggregations

StorageKey (org.whispersystems.signalservice.api.storage.StorageKey)10 SignalServiceAccountManager (org.whispersystems.signalservice.api.SignalServiceAccountManager)6 SignalStorageManifest (org.whispersystems.signalservice.api.storage.SignalStorageManifest)6 SignalStorageRecord (org.whispersystems.signalservice.api.storage.SignalStorageRecord)6 StorageId (org.whispersystems.signalservice.api.storage.StorageId)6 ArrayList (java.util.ArrayList)4 HashMap (java.util.HashMap)4 LinkedList (java.util.LinkedList)4 UnknownStorageIdDatabase (org.thoughtcrime.securesms.database.UnknownStorageIdDatabase)4 Recipient (org.thoughtcrime.securesms.recipients.Recipient)4 RetryLaterException (org.thoughtcrime.securesms.transport.RetryLaterException)4 InvalidKeyException (org.whispersystems.libsignal.InvalidKeyException)4 KeysMessage (org.whispersystems.signalservice.api.messages.multidevice.KeysMessage)4 SignalAccountRecord (org.whispersystems.signalservice.api.storage.SignalAccountRecord)3 NonNull (androidx.annotation.NonNull)2 Stream (com.annimon.stream.Stream)2 ByteString (com.google.protobuf.ByteString)2 IOException (java.io.IOException)2 Collections (java.util.Collections)2 List (java.util.List)2