Give the service direct knowledge of linked device status.
This commit is contained in:
parent
75421b1af8
commit
c1c9ca7c4c
5 changed files with 27 additions and 25 deletions
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
|
@ -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());
|
||||||
|
|
Loading…
Add table
Reference in a new issue