Create libsignal dependencies only when required

This commit is contained in:
AsamK 2021-08-30 15:07:12 +02:00
parent 1f0c2d5c78
commit 626406a43c
7 changed files with 138 additions and 130 deletions

View file

@ -164,8 +164,7 @@ public class Manager implements Closeable {
return LEGACY_LOCK::unlock;
}
};
this.dependencies = new SignalDependencies(account.getSelfAddress(),
serviceEnvironmentConfig,
this.dependencies = new SignalDependencies(serviceEnvironmentConfig,
userAgent,
credentialsProvider,
account.getSignalProtocolStore(),
@ -186,8 +185,6 @@ public class Manager implements Closeable {
avatarStore,
account.getProfileStore()::getProfileKey,
unidentifiedAccessHelper::getAccessFor,
dependencies::getProfileService,
dependencies::getMessageReceiver,
this::resolveSignalServiceAddress);
final GroupV2Helper groupV2Helper = new GroupV2Helper(profileHelper::getRecipientProfileKeyCredential,
this::getRecipientProfile,
@ -220,8 +217,7 @@ public class Manager implements Closeable {
this::resolveSignalServiceAddress);
this.context = new Context(account,
dependencies.getAccountManager(),
dependencies.getMessageReceiver(),
dependencies,
stickerPackStore,
sendHelper,
groupHelper,
@ -1149,10 +1145,6 @@ public class Manager implements Closeable {
}
public SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address) {
if (address.matches(account.getSelfAddress())) {
return account.getSelfAddress();
}
return resolveSignalServiceAddress(resolveRecipient(address));
}

View file

@ -3,7 +3,6 @@ package org.asamk.signal.manager;
import org.asamk.signal.manager.config.ServiceConfig;
import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
@ -18,32 +17,41 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.services.ProfileService;
import org.whispersystems.signalservice.api.util.SleepTimer;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
public class SignalDependencies {
private final SignalServiceAccountManager accountManager;
private final GroupsV2Api groupsV2Api;
private final GroupsV2Operations groupsV2Operations;
private final Object LOCK = new Object();
private final SignalWebSocket signalWebSocket;
private final SignalServiceMessageReceiver messageReceiver;
private final SignalServiceMessageSender messageSender;
private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final DynamicCredentialsProvider credentialsProvider;
private final SignalServiceDataStore dataStore;
private final ExecutorService executor;
private final SignalSessionLock sessionLock;
private final KeyBackupService keyBackupService;
private final ProfileService profileService;
private final SignalServiceCipher cipher;
private SignalServiceAccountManager accountManager;
private GroupsV2Api groupsV2Api;
private GroupsV2Operations groupsV2Operations;
private ClientZkOperations clientZkOperations;
private SignalWebSocket signalWebSocket;
private SignalServiceMessageReceiver messageReceiver;
private SignalServiceMessageSender messageSender;
private KeyBackupService keyBackupService;
private ProfileService profileService;
private SignalServiceCipher cipher;
public SignalDependencies(
final SignalServiceAddress selfAddress,
final ServiceEnvironmentConfig serviceEnvironmentConfig,
final String userAgent,
final DynamicCredentialsProvider credentialsProvider,
@ -51,100 +59,134 @@ public class SignalDependencies {
final ExecutorService executor,
final SignalSessionLock sessionLock
) {
this.groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration())) : null;
final SleepTimer timer = new UptimeSleepTimer();
this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
this.groupsV2Api = accountManager.getGroupsV2Api();
this.keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10);
final ClientZkProfileOperations clientZkProfileOperations = capabilities.isGv2() ? ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration()).getProfileOperations() : null;
this.messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
clientZkProfileOperations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
final var healthMonitor = new SignalWebSocketHealthMonitor(timer);
final WebSocketFactory webSocketFactory = new WebSocketFactory() {
@Override
public WebSocketConnection createWebSocket() {
return new WebSocketConnection("normal",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.of(credentialsProvider),
userAgent,
healthMonitor);
}
@Override
public WebSocketConnection createUnidentifiedWebSocket() {
return new WebSocketConnection("unidentified",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.absent(),
userAgent,
healthMonitor);
}
};
this.signalWebSocket = new SignalWebSocket(webSocketFactory);
healthMonitor.monitor(signalWebSocket);
this.profileService = new ProfileService(clientZkProfileOperations, messageReceiver, signalWebSocket);
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
this.cipher = new SignalServiceCipher(selfAddress, dataStore, sessionLock, certificateValidator);
this.messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
dataStore,
sessionLock,
userAgent,
signalWebSocket,
Optional.absent(),
clientZkProfileOperations,
executor,
ServiceConfig.MAX_ENVELOPE_SIZE,
ServiceConfig.AUTOMATIC_NETWORK_RETRY);
this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
this.credentialsProvider = credentialsProvider;
this.dataStore = dataStore;
this.executor = executor;
this.sessionLock = sessionLock;
}
public SignalServiceAccountManager getAccountManager() {
return accountManager;
return getOrCreate(() -> accountManager,
() -> accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
getGroupsV2Operations(),
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public GroupsV2Api getGroupsV2Api() {
return groupsV2Api;
return getOrCreate(() -> groupsV2Api, () -> groupsV2Api = getAccountManager().getGroupsV2Api());
}
public GroupsV2Operations getGroupsV2Operations() {
return groupsV2Operations;
return getOrCreate(() -> groupsV2Operations,
() -> groupsV2Operations = capabilities.isGv2() ? new GroupsV2Operations(ClientZkOperations.create(
serviceEnvironmentConfig.getSignalServiceConfiguration())) : null);
}
private ClientZkOperations getClientZkOperations() {
return getOrCreate(() -> clientZkOperations,
() -> clientZkOperations = capabilities.isGv2()
? ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration())
: null);
}
public SignalWebSocket getSignalWebSocket() {
return signalWebSocket;
return getOrCreate(() -> signalWebSocket, () -> {
final var timer = new UptimeSleepTimer();
final var healthMonitor = new SignalWebSocketHealthMonitor(timer);
final var webSocketFactory = new WebSocketFactory() {
@Override
public WebSocketConnection createWebSocket() {
return new WebSocketConnection("normal",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.of(credentialsProvider),
userAgent,
healthMonitor);
}
@Override
public WebSocketConnection createUnidentifiedWebSocket() {
return new WebSocketConnection("unidentified",
serviceEnvironmentConfig.getSignalServiceConfiguration(),
Optional.absent(),
userAgent,
healthMonitor);
}
};
signalWebSocket = new SignalWebSocket(webSocketFactory);
healthMonitor.monitor(signalWebSocket);
});
}
public SignalServiceMessageReceiver getMessageReceiver() {
return messageReceiver;
return getOrCreate(() -> messageReceiver,
() -> messageReceiver = new SignalServiceMessageReceiver(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
userAgent,
getClientZkOperations().getProfileOperations(),
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public SignalServiceMessageSender getMessageSender() {
return messageSender;
return getOrCreate(() -> messageSender,
() -> messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
credentialsProvider,
dataStore,
sessionLock,
userAgent,
getSignalWebSocket(),
Optional.absent(),
getClientZkOperations().getProfileOperations(),
executor,
ServiceConfig.MAX_ENVELOPE_SIZE,
ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public KeyBackupService getKeyBackupService() {
return keyBackupService;
return getOrCreate(() -> keyBackupService,
() -> keyBackupService = getAccountManager().getKeyBackupService(ServiceConfig.getIasKeyStore(),
serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
10));
}
public ProfileService getProfileService() {
return profileService;
return getOrCreate(() -> profileService,
() -> profileService = new ProfileService(getClientZkOperations().getProfileOperations(),
getMessageReceiver(),
getSignalWebSocket()));
}
public SignalServiceCipher getCipher() {
return cipher;
return getOrCreate(() -> cipher, () -> {
final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
final var address = new SignalServiceAddress(credentialsProvider.getUuid(), credentialsProvider.getE164());
cipher = new SignalServiceCipher(address, dataStore, sessionLock, certificateValidator);
});
}
private <T> T getOrCreate(Supplier<T> supplier, Callable creator) {
var value = supplier.get();
if (value != null) {
return value;
}
synchronized (LOCK) {
value = supplier.get();
if (value != null) {
return value;
}
creator.call();
return supplier.get();
}
}
private interface Callable {
void call();
}
}

View file

@ -1,8 +0,0 @@
package org.asamk.signal.manager.helper;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
public interface MessageReceiverProvider {
SignalServiceMessageReceiver getMessageReceiver();
}

View file

@ -45,8 +45,6 @@ public final class ProfileHelper {
private final AvatarStore avatarStore;
private final ProfileKeyProvider profileKeyProvider;
private final UnidentifiedAccessProvider unidentifiedAccessProvider;
private final ProfileServiceProvider profileServiceProvider;
private final MessageReceiverProvider messageReceiverProvider;
private final SignalServiceAddressResolver addressResolver;
public ProfileHelper(
@ -55,8 +53,6 @@ public final class ProfileHelper {
final AvatarStore avatarStore,
final ProfileKeyProvider profileKeyProvider,
final UnidentifiedAccessProvider unidentifiedAccessProvider,
final ProfileServiceProvider profileServiceProvider,
final MessageReceiverProvider messageReceiverProvider,
final SignalServiceAddressResolver addressResolver
) {
this.account = account;
@ -64,8 +60,6 @@ public final class ProfileHelper {
this.avatarStore = avatarStore;
this.profileKeyProvider = profileKeyProvider;
this.unidentifiedAccessProvider = unidentifiedAccessProvider;
this.profileServiceProvider = profileServiceProvider;
this.messageReceiverProvider = messageReceiverProvider;
this.addressResolver = addressResolver;
}
@ -218,7 +212,7 @@ public final class ProfileHelper {
}
private SignalServiceProfile retrieveProfileSync(String username) throws IOException {
return messageReceiverProvider.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent());
return dependencies.getMessageReceiver().retrieveProfileByUsername(username, Optional.absent());
}
private ProfileAndCredential retrieveProfileAndCredential(
@ -287,7 +281,7 @@ public final class ProfileHelper {
Optional<UnidentifiedAccess> unidentifiedAccess,
SignalServiceProfile.RequestType requestType
) {
var profileService = profileServiceProvider.getProfileService();
var profileService = dependencies.getProfileService();
Single<ServiceResponse<ProfileAndCredential>> responseSingle;
try {

View file

@ -1,8 +0,0 @@
package org.asamk.signal.manager.helper;
import org.whispersystems.signalservice.api.services.ProfileService;
public interface ProfileServiceProvider {
ProfileService getProfileService();
}

View file

@ -1,19 +1,17 @@
package org.asamk.signal.manager.jobs;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.StickerPackStore;
import org.asamk.signal.manager.helper.GroupHelper;
import org.asamk.signal.manager.helper.ProfileHelper;
import org.asamk.signal.manager.helper.SendHelper;
import org.asamk.signal.manager.helper.SyncHelper;
import org.asamk.signal.manager.storage.SignalAccount;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
public class Context {
private final SignalAccount account;
private final SignalServiceAccountManager accountManager;
private final SignalServiceMessageReceiver messageReceiver;
private final SignalDependencies dependencies;
private final StickerPackStore stickerPackStore;
private final SendHelper sendHelper;
private final GroupHelper groupHelper;
@ -22,8 +20,7 @@ public class Context {
public Context(
final SignalAccount account,
final SignalServiceAccountManager accountManager,
final SignalServiceMessageReceiver messageReceiver,
final SignalDependencies dependencies,
final StickerPackStore stickerPackStore,
final SendHelper sendHelper,
final GroupHelper groupHelper,
@ -31,8 +28,7 @@ public class Context {
final ProfileHelper profileHelper
) {
this.account = account;
this.accountManager = accountManager;
this.messageReceiver = messageReceiver;
this.dependencies = dependencies;
this.stickerPackStore = stickerPackStore;
this.sendHelper = sendHelper;
this.groupHelper = groupHelper;
@ -44,12 +40,8 @@ public class Context {
return account;
}
public SignalServiceAccountManager getAccountManager() {
return accountManager;
}
public SignalServiceMessageReceiver getMessageReceiver() {
return messageReceiver;
public SignalDependencies getDependencies() {
return dependencies;
}
public StickerPackStore getStickerPackStore() {

View file

@ -32,7 +32,9 @@ public class RetrieveStickerPackJob implements Job {
}
logger.debug("Retrieving sticker pack {}.", Hex.toStringCondensed(packId.serialize()));
try {
final var manifest = context.getMessageReceiver().retrieveStickerManifest(packId.serialize(), packKey);
final var manifest = context.getDependencies()
.getMessageReceiver()
.retrieveStickerManifest(packId.serialize(), packKey);
final var stickerIds = new HashSet<Integer>();
if (manifest.getCover().isPresent()) {
@ -43,7 +45,9 @@ public class RetrieveStickerPackJob implements Job {
}
for (var id : stickerIds) {
final var inputStream = context.getMessageReceiver().retrieveSticker(packId.serialize(), packKey, id);
final var inputStream = context.getDependencies()
.getMessageReceiver()
.retrieveSticker(packId.serialize(), packKey, id);
context.getStickerPackStore().storeSticker(packId, id, o -> IOUtils.copyStream(inputStream, o));
}