Give the service direct knowledge of linked device status.

This commit is contained in:
Greyson Parrelli 2021-07-30 17:38:34 -04:00 committed by Cody Henthorne
parent 75421b1af8
commit c1c9ca7c4c
5 changed files with 27 additions and 25 deletions

View file

@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.crypto.storage;
import android.content.Context; import android.content.Context;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair; import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.InvalidKeyIdException; import org.whispersystems.libsignal.InvalidKeyIdException;
@ -14,7 +15,7 @@ import org.whispersystems.libsignal.state.PreKeyStore;
import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyRecord;
import org.whispersystems.libsignal.state.SignedPreKeyStore; import org.whispersystems.libsignal.state.SignedPreKeyStore;
import org.whispersystems.signalservice.api.SignalServiceProtocolStore; import org.whispersystems.signalservice.api.SignalServiceDataStore;
import org.whispersystems.signalservice.api.SignalServiceSessionStore; import org.whispersystems.signalservice.api.SignalServiceSessionStore;
import org.whispersystems.signalservice.api.push.DistributionId; import org.whispersystems.signalservice.api.push.DistributionId;
@ -23,8 +24,9 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class SignalProtocolStoreImpl implements SignalServiceProtocolStore { public class SignalProtocolStoreImpl implements SignalServiceDataStore {
private final Context context;
private final PreKeyStore preKeyStore; private final PreKeyStore preKeyStore;
private final SignedPreKeyStore signedPreKeyStore; private final SignedPreKeyStore signedPreKeyStore;
private final IdentityKeyStore identityKeyStore; private final IdentityKeyStore identityKeyStore;
@ -32,6 +34,7 @@ public class SignalProtocolStoreImpl implements SignalServiceProtocolStore {
private final SignalSenderKeyStore senderKeyStore; private final SignalSenderKeyStore senderKeyStore;
public SignalProtocolStoreImpl(Context context) { public SignalProtocolStoreImpl(Context context) {
this.context = context;
this.preKeyStore = new TextSecurePreKeyStore(context); this.preKeyStore = new TextSecurePreKeyStore(context);
this.signedPreKeyStore = new TextSecurePreKeyStore(context); this.signedPreKeyStore = new TextSecurePreKeyStore(context);
this.identityKeyStore = new TextSecureIdentityKeyStore(context); this.identityKeyStore = new TextSecureIdentityKeyStore(context);
@ -173,4 +176,9 @@ public class SignalProtocolStoreImpl implements SignalServiceProtocolStore {
public void clearSenderKeySharedWith(Collection<SignalProtocolAddress> addresses) { public void clearSenderKeySharedWith(Collection<SignalProtocolAddress> addresses) {
senderKeyStore.clearSenderKeySharedWith(addresses); senderKeyStore.clearSenderKeySharedWith(addresses);
} }
@Override
public boolean isMultiDevice() {
return TextSecurePreferences.isMultiDevice(context);
}
} }

View file

@ -181,8 +181,6 @@ public class ApplicationDependencies {
synchronized (LOCK) { synchronized (LOCK) {
if (messageSender == null) { if (messageSender == null) {
messageSender = provider.provideSignalServiceMessageSender(getSignalWebSocket()); messageSender = provider.provideSignalServiceMessageSender(getSignalWebSocket());
} else {
messageSender.update(TextSecurePreferences.isMultiDevice(application));
} }
return messageSender; return messageSender;
} }

View file

@ -105,7 +105,6 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
new SignalProtocolStoreImpl(context), new SignalProtocolStoreImpl(context),
ReentrantSessionLock.INSTANCE, ReentrantSessionLock.INSTANCE,
BuildConfig.SIGNAL_AGENT, BuildConfig.SIGNAL_AGENT,
TextSecurePreferences.isMultiDevice(context),
signalWebSocket, signalWebSocket,
Optional.of(new SecurityEventListener(context)), Optional.of(new SecurityEventListener(context)),
provideClientZkOperations().getProfileOperations(), provideClientZkOperations().getProfileOperations(),

View file

@ -6,5 +6,9 @@ import org.whispersystems.libsignal.state.SignalProtocolStore;
* And extension of the normal protocol store interface that has additional methods that are needed * And extension of the normal protocol store interface that has additional methods that are needed
* in the service layer, but not the protocol layer. * in the service layer, but not the protocol layer.
*/ */
public interface SignalServiceProtocolStore extends SignalProtocolStore, SignalServiceSessionStore, SignalServiceSenderKeyStore { public interface SignalServiceDataStore extends SignalProtocolStore, SignalServiceSessionStore, SignalServiceSenderKeyStore {
/**
* @return True if the active account has linked devices, otherwise false.
*/
boolean isMultiDevice();
} }

View file

@ -145,25 +145,23 @@ public class SignalServiceMessageSender {
private static final int RETRY_COUNT = 4; private static final int RETRY_COUNT = 4;
private final PushServiceSocket socket; private final PushServiceSocket socket;
private final SignalServiceProtocolStore store; private final SignalServiceDataStore store;
private final SignalSessionLock sessionLock; private final SignalSessionLock sessionLock;
private final SignalServiceAddress localAddress; private final SignalServiceAddress localAddress;
private final Optional<EventListener> eventListener; private final Optional<EventListener> eventListener;
private final AttachmentService attachmentService; private final AttachmentService attachmentService;
private final MessagingService messagingService; private final MessagingService messagingService;
private final AtomicBoolean isMultiDevice;
private final ExecutorService executor; private final ExecutorService executor;
private final long maxEnvelopeSize; private final long maxEnvelopeSize;
public SignalServiceMessageSender(SignalServiceConfiguration urls, public SignalServiceMessageSender(SignalServiceConfiguration urls,
CredentialsProvider credentialsProvider, CredentialsProvider credentialsProvider,
SignalServiceProtocolStore store, SignalServiceDataStore store,
SignalSessionLock sessionLock, SignalSessionLock sessionLock,
String signalAgent, String signalAgent,
boolean isMultiDevice,
SignalWebSocket signalWebSocket, SignalWebSocket signalWebSocket,
Optional<EventListener> eventListener, Optional<EventListener> eventListener,
ClientZkProfileOperations clientZkProfileOperations, ClientZkProfileOperations clientZkProfileOperations,
@ -177,7 +175,6 @@ public class SignalServiceMessageSender {
this.localAddress = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164()); this.localAddress = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
this.attachmentService = new AttachmentService(signalWebSocket); this.attachmentService = new AttachmentService(signalWebSocket);
this.messagingService = new MessagingService(signalWebSocket); this.messagingService = new MessagingService(signalWebSocket);
this.isMultiDevice = new AtomicBoolean(isMultiDevice);
this.eventListener = eventListener; this.eventListener = eventListener;
this.executor = executor != null ? executor : Executors.newSingleThreadExecutor(); this.executor = executor != null ? executor : Executors.newSingleThreadExecutor();
this.maxEnvelopeSize = maxEnvelopeSize; this.maxEnvelopeSize = maxEnvelopeSize;
@ -403,7 +400,7 @@ public class SignalServiceMessageSender {
Optional<byte[]> groupId = message.getGroupId(); Optional<byte[]> groupId = message.getGroupId();
List<SendMessageResult> results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId.orNull(), false); List<SendMessageResult> results = sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, contentHint, groupId.orNull(), false);
if (isMultiDevice.get()) { if (store.isMultiDevice()) {
Content syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.absent(), message.getTimestamp(), results, isRecipientUpdate); Content syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.absent(), message.getTimestamp(), results, isRecipientUpdate);
EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.absent()); EnvelopeContent syncMessageContent = EnvelopeContent.encrypted(syncMessage, ContentHint.IMPLICIT, Optional.absent());
@ -443,7 +440,7 @@ public class SignalServiceMessageSender {
} }
} }
if (needsSyncInResults || isMultiDevice.get()) { if (needsSyncInResults || store.isMultiDevice()) {
Optional<SignalServiceAddress> recipient = Optional.absent(); Optional<SignalServiceAddress> recipient = Optional.absent();
if (!message.getGroupContext().isPresent() && recipients.size() == 1) { if (!message.getGroupContext().isPresent() && recipients.size() == 1) {
recipient = Optional.of(recipients.get(0)); recipient = Optional.of(recipients.get(0));
@ -512,10 +509,6 @@ public class SignalServiceMessageSender {
socket.cancelInFlightRequests(); socket.cancelInFlightRequests();
} }
public void update(boolean isMultiDevice) {
this.isMultiDevice.set(isMultiDevice);
}
public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment) throws IOException { public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment) throws IOException {
byte[] attachmentKey = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getSecretKey).or(() -> Util.getSecretBytes(64)); byte[] attachmentKey = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getSecretKey).or(() -> Util.getSecretBytes(64));
byte[] attachmentIV = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getIV).or(() -> Util.getSecretBytes(16)); byte[] attachmentIV = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getIV).or(() -> Util.getSecretBytes(16));
@ -1608,7 +1601,7 @@ public class SignalServiceMessageSender {
if (!unidentifiedAccess.isPresent()) { if (!unidentifiedAccess.isPresent()) {
try { try {
SendMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.send(messages, Optional.absent()).blockingGet()).getResultOrThrow(); SendMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.send(messages, Optional.absent()).blockingGet()).getResultOrThrow();
return SendMessageResult.success(recipient, messages.getDevices(), false, response.getNeedsSync() || isMultiDevice.get(), System.currentTimeMillis() - startTime, content.getContent()); return SendMessageResult.success(recipient, messages.getDevices(), false, response.getNeedsSync() || store.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
} catch (WebSocketUnavailableException e) { } catch (WebSocketUnavailableException e) {
Log.i(TAG, "[sendMessage] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")"); Log.i(TAG, "[sendMessage] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
} catch (IOException e) { } catch (IOException e) {
@ -1618,7 +1611,7 @@ public class SignalServiceMessageSender {
} else if (unidentifiedAccess.isPresent()) { } else if (unidentifiedAccess.isPresent()) {
try { try {
SendMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.send(messages, unidentifiedAccess).blockingGet()).getResultOrThrow(); SendMessageResponse response = new MessagingService.SendResponseProcessor<>(messagingService.send(messages, unidentifiedAccess).blockingGet()).getResultOrThrow();
return SendMessageResult.success(recipient, messages.getDevices(), true, response.getNeedsSync() || isMultiDevice.get(), System.currentTimeMillis() - startTime, content.getContent()); return SendMessageResult.success(recipient, messages.getDevices(), true, response.getNeedsSync() || store.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
} catch (WebSocketUnavailableException e) { } catch (WebSocketUnavailableException e) {
Log.i(TAG, "[sendMessage] Unidentified pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")"); Log.i(TAG, "[sendMessage] Unidentified pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
} catch (IOException e) { } catch (IOException e) {
@ -1633,7 +1626,7 @@ public class SignalServiceMessageSender {
SendMessageResponse response = socket.sendMessage(messages, unidentifiedAccess); SendMessageResponse response = socket.sendMessage(messages, unidentifiedAccess);
return SendMessageResult.success(recipient, messages.getDevices(), unidentifiedAccess.isPresent(), response.getNeedsSync() || isMultiDevice.get(), System.currentTimeMillis() - startTime, content.getContent()); return SendMessageResult.success(recipient, messages.getDevices(), unidentifiedAccess.isPresent(), response.getNeedsSync() || store.isMultiDevice(), System.currentTimeMillis() - startTime, content.getContent());
} catch (InvalidKeyException ike) { } catch (InvalidKeyException ike) {
Log.w(TAG, ike); Log.w(TAG, ike);
@ -1843,7 +1836,7 @@ public class SignalServiceMessageSender {
List<SendMessageResult> success = recipients.keySet() List<SendMessageResult> success = recipients.keySet()
.stream() .stream()
.filter(r -> !unregistered.contains(r.getUuid().get())) .filter(r -> !unregistered.contains(r.getUuid().get()))
.map(a -> SendMessageResult.success(a, recipients.get(a), true, isMultiDevice.get(), -1, Optional.of(content))) .map(a -> SendMessageResult.success(a, recipients.get(a), true, store.isMultiDevice(), -1, Optional.of(content)))
.collect(Collectors.toList()); .collect(Collectors.toList());
List<SendMessageResult> results = new ArrayList<>(success.size() + failures.size()); List<SendMessageResult> results = new ArrayList<>(success.size() + failures.size());