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.CredentialsProvider;
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 CredentialsProvider 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;
- public SignalDependencies(
- final SignalServiceAddress selfAddress,
+ private SignalWebSocket signalWebSocket;
+ private SignalServiceMessageReceiver messageReceiver;
+ private SignalServiceMessageSender messageSender;
+
+ private KeyBackupService keyBackupService;
+ private ProfileService profileService;
+ private SignalServiceCipher cipher;
+
+ SignalDependencies(
final ServiceEnvironmentConfig serviceEnvironmentConfig,
final String userAgent,
- final DynamicCredentialsProvider credentialsProvider,
+ final CredentialsProvider credentialsProvider,
final SignalServiceDataStore dataStore,
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);
+ this.serviceEnvironmentConfig = serviceEnvironmentConfig;
+ this.userAgent = userAgent;
+ this.credentialsProvider = credentialsProvider;
+ this.dataStore = dataStore;
+ this.executor = executor;
+ this.sessionLock = sessionLock;
+ }
- 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);
- }
+ public void resetAfterAddressChange() {
+ this.messageSender = null;
+ this.cipher = null;
+ }
- @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);
+ public ServiceEnvironmentConfig getServiceEnvironmentConfig() {
+ return serviceEnvironmentConfig;
}
public SignalServiceAccountManager getAccountManager() {
- return accountManager;
+ return getOrCreate(() -> accountManager,
+ () -> accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
+ credentialsProvider,
+ userAgent,
+ getGroupsV2Operations(),
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY));
+ }
+
+ public SignalServiceAccountManager createUnauthenticatedAccountManager(String number, String password) {
+ return new SignalServiceAccountManager(getServiceEnvironmentConfig().getSignalServiceConfiguration(),
+ null,
+ null,
+ number,
+ SignalServiceAddress.DEFAULT_DEVICE_ID,
+ password,
+ userAgent,
+ 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);
+ }
+
+ private ClientZkProfileOperations getClientZkProfileOperations() {
+ final var clientZkOperations = getClientZkOperations();
+ return clientZkOperations == null ? null : clientZkOperations.getProfileOperations();
}
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,
+ getClientZkProfileOperations(),
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY));
}
public SignalServiceMessageSender getMessageSender() {
- return messageSender;
+ return getOrCreate(() -> messageSender,
+ () -> messageSender = new SignalServiceMessageSender(serviceEnvironmentConfig.getSignalServiceConfiguration(),
+ credentialsProvider,
+ dataStore,
+ sessionLock,
+ userAgent,
+ getSignalWebSocket(),
+ Optional.absent(),
+ getClientZkProfileOperations(),
+ 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(getClientZkProfileOperations(),
+ getMessageReceiver(),
+ getSignalWebSocket()));
}
public SignalServiceCipher getCipher() {
- return cipher;
+ return getOrCreate(() -> cipher, () -> {
+ final var certificateValidator = new CertificateValidator(serviceEnvironmentConfig.getUnidentifiedSenderTrustRoot());
+ final var address = new SignalServiceAddress(credentialsProvider.getAci(), credentialsProvider.getE164());
+ final var deviceId = credentialsProvider.getDeviceId();
+ cipher = new SignalServiceCipher(address, deviceId, dataStore.aci(), 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();
}
}