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

View file

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

View file

@ -105,7 +105,6 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
new SignalProtocolStoreImpl(context),
ReentrantSessionLock.INSTANCE,
BuildConfig.SIGNAL_AGENT,
TextSecurePreferences.isMultiDevice(context),
signalWebSocket,
Optional.of(new SecurityEventListener(context)),
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
* 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

@ -146,24 +146,22 @@ public class SignalServiceMessageSender {
private static final int RETRY_COUNT = 4;
private final PushServiceSocket socket;
private final SignalServiceProtocolStore store;
private final SignalServiceDataStore store;
private final SignalSessionLock sessionLock;
private final SignalServiceAddress localAddress;
private final Optional<EventListener> eventListener;
private final AttachmentService attachmentService;
private final MessagingService messagingService;
private final AtomicBoolean isMultiDevice;
private final ExecutorService executor;
private final long maxEnvelopeSize;
public SignalServiceMessageSender(SignalServiceConfiguration urls,
CredentialsProvider credentialsProvider,
SignalServiceProtocolStore store,
SignalServiceDataStore store,
SignalSessionLock sessionLock,
String signalAgent,
boolean isMultiDevice,
SignalWebSocket signalWebSocket,
Optional<EventListener> eventListener,
ClientZkProfileOperations clientZkProfileOperations,
@ -177,7 +175,6 @@ public class SignalServiceMessageSender {
this.localAddress = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
this.attachmentService = new AttachmentService(signalWebSocket);
this.messagingService = new MessagingService(signalWebSocket);
this.isMultiDevice = new AtomicBoolean(isMultiDevice);
this.eventListener = eventListener;
this.executor = executor != null ? executor : Executors.newSingleThreadExecutor();
this.maxEnvelopeSize = maxEnvelopeSize;
@ -403,7 +400,7 @@ public class SignalServiceMessageSender {
Optional<byte[]> groupId = message.getGroupId();
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);
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();
if (!message.getGroupContext().isPresent() && recipients.size() == 1) {
recipient = Optional.of(recipients.get(0));
@ -512,10 +509,6 @@ public class SignalServiceMessageSender {
socket.cancelInFlightRequests();
}
public void update(boolean isMultiDevice) {
this.isMultiDevice.set(isMultiDevice);
}
public SignalServiceAttachmentPointer uploadAttachment(SignalServiceAttachmentStream attachment) throws IOException {
byte[] attachmentKey = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getSecretKey).or(() -> Util.getSecretBytes(64));
byte[] attachmentIV = attachment.getResumableUploadSpec().transform(ResumableUploadSpec::getIV).or(() -> Util.getSecretBytes(16));
@ -1608,7 +1601,7 @@ public class SignalServiceMessageSender {
if (!unidentifiedAccess.isPresent()) {
try {
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) {
Log.i(TAG, "[sendMessage] Pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
} catch (IOException e) {
@ -1618,7 +1611,7 @@ public class SignalServiceMessageSender {
} else if (unidentifiedAccess.isPresent()) {
try {
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) {
Log.i(TAG, "[sendMessage] Unidentified pipe unavailable, falling back... (" + e.getClass().getSimpleName() + ": " + e.getMessage() + ")");
} catch (IOException e) {
@ -1633,7 +1626,7 @@ public class SignalServiceMessageSender {
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) {
Log.w(TAG, ike);
@ -1843,7 +1836,7 @@ public class SignalServiceMessageSender {
List<SendMessageResult> success = recipients.keySet()
.stream()
.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());
List<SendMessageResult> results = new ArrayList<>(success.size() + failures.size());