Rewrite the AppDependencies system.

This commit is contained in:
Greyson Parrelli
2024-05-22 15:44:24 -04:00
committed by Cody Henthorne
parent a0131bf39b
commit b6a4e1f145
560 changed files with 2909 additions and 3036 deletions

View File

@@ -0,0 +1,346 @@
package org.thoughtcrime.securesms.dependencies
import android.annotation.SuppressLint
import android.app.Application
import androidx.annotation.MainThread
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.BehaviorSubject
import io.reactivex.rxjava3.subjects.Subject
import okhttp3.OkHttpClient
import org.signal.core.util.concurrent.DeadlockDetector
import org.signal.core.util.resettableLazy
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
import org.thoughtcrime.securesms.components.TypingStatusRepository
import org.thoughtcrime.securesms.components.TypingStatusSender
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl
import org.thoughtcrime.securesms.database.DatabaseObserver
import org.thoughtcrime.securesms.database.PendingRetryReceiptCache
import org.thoughtcrime.securesms.groups.GroupsV2Authorization
import org.thoughtcrime.securesms.jobmanager.JobManager
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
import org.thoughtcrime.securesms.notifications.MessageNotifier
import org.thoughtcrime.securesms.payments.Payments
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess
import org.thoughtcrime.securesms.recipients.LiveRecipientCache
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager
import org.thoughtcrime.securesms.service.DeletedCallEventManager
import org.thoughtcrime.securesms.service.ExpiringMessageManager
import org.thoughtcrime.securesms.service.ExpiringStoriesManager
import org.thoughtcrime.securesms.service.PendingRetryReceiptManager
import org.thoughtcrime.securesms.service.ScheduledMessageManager
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager
import org.thoughtcrime.securesms.shakereport.ShakeToReport
import org.thoughtcrime.securesms.util.AppForegroundObserver
import org.thoughtcrime.securesms.util.EarlyMessageCache
import org.thoughtcrime.securesms.util.FrameRateTracker
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import org.whispersystems.signalservice.api.SignalServiceDataStore
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver
import org.whispersystems.signalservice.api.SignalServiceMessageSender
import org.whispersystems.signalservice.api.SignalWebSocket
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations
import org.whispersystems.signalservice.api.services.CallLinksService
import org.whispersystems.signalservice.api.services.DonationsService
import org.whispersystems.signalservice.api.services.ProfileService
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork
import java.util.function.Supplier
/**
* Location for storing and retrieving application-scoped singletons. Users must call
* [.init] before using any of the methods, preferably early on in
* [Application.onCreate].
*
* All future application-scoped singletons should be written as normal objects, then placed here
* to manage their singleton-ness.
*/
@SuppressLint("StaticFieldLeak")
object AppDependencies {
private lateinit var _application: Application
private lateinit var provider: Provider
// Needs special initialization because it needs to be created on the main thread
private lateinit var _appForegroundObserver: AppForegroundObserver
@JvmStatic
@MainThread
fun init(application: Application, provider: Provider) {
if (this::_application.isInitialized || this::provider.isInitialized) {
throw IllegalStateException("Already initialized!")
}
_application = application
AppDependencies.provider = provider
_appForegroundObserver = provider.provideAppForegroundObserver()
_appForegroundObserver.begin()
}
@JvmStatic
val isInitialized: Boolean
get() = this::_application.isInitialized
@JvmStatic
val application: Application
get() = _application
@JvmStatic
val appForegroundObserver: AppForegroundObserver
get() = _appForegroundObserver
@JvmStatic
val recipientCache: LiveRecipientCache by lazy {
provider.provideRecipientCache()
}
@JvmStatic
val jobManager: JobManager by lazy {
provider.provideJobManager()
}
@JvmStatic
val frameRateTracker: FrameRateTracker by lazy {
provider.provideFrameRateTracker()
}
@JvmStatic
val megaphoneRepository: MegaphoneRepository by lazy {
provider.provideMegaphoneRepository()
}
@JvmStatic
val earlyMessageCache: EarlyMessageCache by lazy {
provider.provideEarlyMessageCache()
}
@JvmStatic
val typingStatusRepository: TypingStatusRepository by lazy {
provider.provideTypingStatusRepository()
}
@JvmStatic
val typingStatusSender: TypingStatusSender by lazy {
provider.provideTypingStatusSender()
}
@JvmStatic
val databaseObserver: DatabaseObserver by lazy {
provider.provideDatabaseObserver()
}
@JvmStatic
val trimThreadsByDateManager: TrimThreadsByDateManager by lazy {
provider.provideTrimThreadsByDateManager()
}
@JvmStatic
val viewOnceMessageManager: ViewOnceMessageManager by lazy {
provider.provideViewOnceMessageManager()
}
@JvmStatic
val expiringMessageManager: ExpiringMessageManager by lazy {
provider.provideExpiringMessageManager()
}
@JvmStatic
val deletedCallEventManager: DeletedCallEventManager by lazy {
provider.provideDeletedCallEventManager()
}
@JvmStatic
val signalCallManager: SignalCallManager by lazy {
provider.provideSignalCallManager()
}
@JvmStatic
val shakeToReport: ShakeToReport by lazy {
provider.provideShakeToReport()
}
@JvmStatic
val pendingRetryReceiptManager: PendingRetryReceiptManager by lazy {
provider.providePendingRetryReceiptManager()
}
@JvmStatic
val pendingRetryReceiptCache: PendingRetryReceiptCache by lazy {
provider.providePendingRetryReceiptCache()
}
@JvmStatic
val messageNotifier: MessageNotifier by lazy {
provider.provideMessageNotifier()
}
@JvmStatic
val giphyMp4Cache: GiphyMp4Cache by lazy {
provider.provideGiphyMp4Cache()
}
@JvmStatic
val exoPlayerPool: SimpleExoPlayerPool by lazy {
provider.provideExoPlayerPool()
}
@JvmStatic
val deadlockDetector: DeadlockDetector by lazy {
provider.provideDeadlockDetector()
}
@JvmStatic
val expireStoriesManager: ExpiringStoriesManager by lazy {
provider.provideExpiringStoriesManager()
}
@JvmStatic
val scheduledMessageManager: ScheduledMessageManager by lazy {
provider.provideScheduledMessageManager()
}
@JvmStatic
val androidCallAudioManager: AudioManagerCompat by lazy {
provider.provideAndroidCallAudioManager()
}
private val _webSocketObserver: Subject<WebSocketConnectionState> = BehaviorSubject.create()
/**
* An observable that emits the current state of the WebSocket connection across the various lifecycles
* of the [signalWebSocket].
*/
@JvmStatic
val webSocketObserver: Observable<WebSocketConnectionState> = _webSocketObserver
private val _networkModule = resettableLazy {
NetworkDependenciesModule(application, provider, _webSocketObserver)
}
private val networkModule by _networkModule
@JvmStatic
val signalServiceNetworkAccess: SignalServiceNetworkAccess
get() = networkModule.signalServiceNetworkAccess
@JvmStatic
val protocolStore: SignalServiceDataStoreImpl
get() = networkModule.protocolStore
@JvmStatic
val signalServiceMessageSender: SignalServiceMessageSender
get() = networkModule.signalServiceMessageSender
@JvmStatic
val signalServiceAccountManager: SignalServiceAccountManager
get() = networkModule.signalServiceAccountManager
@JvmStatic
val signalServiceMessageReceiver: SignalServiceMessageReceiver
get() = networkModule.signalServiceMessageReceiver
@JvmStatic
val incomingMessageObserver: IncomingMessageObserver
get() = networkModule.incomingMessageObserver
@JvmStatic
val libsignalNetwork: LibSignalNetwork
get() = networkModule.libsignalNetwork
@JvmStatic
val signalWebSocket: SignalWebSocket
get() = networkModule.signalWebSocket
@JvmStatic
val groupsV2Authorization: GroupsV2Authorization
get() = networkModule.groupsV2Authorization
@JvmStatic
val groupsV2Operations: GroupsV2Operations
get() = networkModule.groupsV2Operations
@JvmStatic
val clientZkReceiptOperations
get() = networkModule.clientZkReceiptOperations
@JvmStatic
val payments: Payments
get() = networkModule.payments
@JvmStatic
val callLinksService: CallLinksService
get() = networkModule.callLinksService
@JvmStatic
val profileService: ProfileService
get() = networkModule.profileService
@JvmStatic
val donationsService: DonationsService
get() = networkModule.donationsService
@JvmStatic
val okHttpClient: OkHttpClient
get() = networkModule.okHttpClient
@JvmStatic
val signalOkHttpClient: OkHttpClient
get() = networkModule.signalOkHttpClient
@JvmStatic
fun resetProtocolStores() {
networkModule.resetProtocolStores()
}
@JvmStatic
fun resetNetwork() {
networkModule.closeConnections()
_networkModule.reset()
}
interface Provider {
fun provideGroupsV2Operations(signalServiceConfiguration: SignalServiceConfiguration): GroupsV2Operations
fun provideSignalServiceAccountManager(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): SignalServiceAccountManager
fun provideSignalServiceMessageSender(signalWebSocket: SignalWebSocket, protocolStore: SignalServiceDataStore, signalServiceConfiguration: SignalServiceConfiguration): SignalServiceMessageSender
fun provideSignalServiceMessageReceiver(signalServiceConfiguration: SignalServiceConfiguration): SignalServiceMessageReceiver
fun provideSignalServiceNetworkAccess(): SignalServiceNetworkAccess
fun provideRecipientCache(): LiveRecipientCache
fun provideJobManager(): JobManager
fun provideFrameRateTracker(): FrameRateTracker
fun provideMegaphoneRepository(): MegaphoneRepository
fun provideEarlyMessageCache(): EarlyMessageCache
fun provideMessageNotifier(): MessageNotifier
fun provideIncomingMessageObserver(): IncomingMessageObserver
fun provideTrimThreadsByDateManager(): TrimThreadsByDateManager
fun provideViewOnceMessageManager(): ViewOnceMessageManager
fun provideExpiringStoriesManager(): ExpiringStoriesManager
fun provideExpiringMessageManager(): ExpiringMessageManager
fun provideDeletedCallEventManager(): DeletedCallEventManager
fun provideTypingStatusRepository(): TypingStatusRepository
fun provideTypingStatusSender(): TypingStatusSender
fun provideDatabaseObserver(): DatabaseObserver
fun providePayments(signalServiceAccountManager: SignalServiceAccountManager): Payments
fun provideShakeToReport(): ShakeToReport
fun provideAppForegroundObserver(): AppForegroundObserver
fun provideSignalCallManager(): SignalCallManager
fun providePendingRetryReceiptManager(): PendingRetryReceiptManager
fun providePendingRetryReceiptCache(): PendingRetryReceiptCache
fun provideSignalWebSocket(signalServiceConfigurationSupplier: Supplier<SignalServiceConfiguration>, libSignalNetworkSupplier: Supplier<LibSignalNetwork?>): SignalWebSocket
fun provideProtocolStore(): SignalServiceDataStoreImpl
fun provideGiphyMp4Cache(): GiphyMp4Cache
fun provideExoPlayerPool(): SimpleExoPlayerPool
fun provideAndroidCallAudioManager(): AudioManagerCompat
fun provideDonationsService(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): DonationsService
fun provideCallLinksService(signalServiceConfiguration: SignalServiceConfiguration, groupsV2Operations: GroupsV2Operations): CallLinksService
fun provideProfileService(profileOperations: ClientZkProfileOperations, signalServiceMessageReceiver: SignalServiceMessageReceiver, signalWebSocket: SignalWebSocket): ProfileService
fun provideDeadlockDetector(): DeadlockDetector
fun provideClientZkReceiptOperations(signalServiceConfiguration: SignalServiceConfiguration): ClientZkReceiptOperations
fun provideScheduledMessageManager(): ScheduledMessageManager
fun provideLibsignalNetwork(config: SignalServiceConfiguration): LibSignalNetwork
}
}

View File

@@ -1,731 +0,0 @@
package org.thoughtcrime.securesms.dependencies;
import android.annotation.SuppressLint;
import android.app.Application;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.signal.core.util.concurrent.DeadlockDetector;
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
import org.thoughtcrime.securesms.components.TypingStatusRepository;
import org.thoughtcrime.securesms.components.TypingStatusSender;
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl;
import org.thoughtcrime.securesms.database.DatabaseObserver;
import org.thoughtcrime.securesms.database.PendingRetryReceiptCache;
import org.thoughtcrime.securesms.groups.GroupsV2Authorization;
import org.thoughtcrime.securesms.groups.GroupsV2AuthorizationMemoryValueCache;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.megaphone.MegaphoneRepository;
import org.thoughtcrime.securesms.messages.IncomingMessageObserver;
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.payments.Payments;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.push.SignalServiceTrustStore;
import org.thoughtcrime.securesms.recipients.LiveRecipientCache;
import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
import org.thoughtcrime.securesms.service.DeletedCallEventManager;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.service.ExpiringStoriesManager;
import org.thoughtcrime.securesms.service.PendingRetryReceiptManager;
import org.thoughtcrime.securesms.service.ScheduledMessageManager;
import org.thoughtcrime.securesms.service.TrimThreadsByDateManager;
import org.thoughtcrime.securesms.service.webrtc.SignalCallManager;
import org.thoughtcrime.securesms.shakereport.ShakeToReport;
import org.thoughtcrime.securesms.util.AppForegroundObserver;
import org.thoughtcrime.securesms.util.EarlyMessageCache;
import org.thoughtcrime.securesms.util.FrameRateTracker;
import org.thoughtcrime.securesms.video.exo.ExoPlayerPool;
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache;
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool;
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceDataStore;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.SignalWebSocket;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.TrustStore;
import org.whispersystems.signalservice.api.services.CallLinksService;
import org.whispersystems.signalservice.api.services.DonationsService;
import org.whispersystems.signalservice.api.services.ProfileService;
import org.whispersystems.signalservice.api.util.Tls12SocketFactory;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager;
import org.whispersystems.signalservice.internal.util.Util;
import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.function.Supplier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
/**
* Location for storing and retrieving application-scoped singletons. Users must call
* {@link #init(Application, Provider)} before using any of the methods, preferably early on in
* {@link Application#onCreate()}.
*
* All future application-scoped singletons should be written as normal objects, then placed here
* to manage their singleton-ness.
*/
@SuppressLint("StaticFieldLeak")
public class ApplicationDependencies {
private static final Object LOCK = new Object();
private static final Object FRAME_RATE_TRACKER_LOCK = new Object();
private static final Object JOB_MANAGER_LOCK = new Object();
private static final Object SIGNAL_HTTP_CLIENT_LOCK = new Object();
private static final Object LIBSIGNAL_NETWORK_LOCK = new Object();
private static Application application;
private static Provider provider;
private static AppForegroundObserver appForegroundObserver;
private static volatile SignalServiceAccountManager accountManager;
private static volatile SignalServiceMessageSender messageSender;
private static volatile SignalServiceMessageReceiver messageReceiver;
private static volatile IncomingMessageObserver incomingMessageObserver;
private static volatile LiveRecipientCache recipientCache;
private static volatile JobManager jobManager;
private static volatile FrameRateTracker frameRateTracker;
private static volatile MegaphoneRepository megaphoneRepository;
private static volatile GroupsV2Authorization groupsV2Authorization;
private static volatile GroupsV2Operations groupsV2Operations;
private static volatile EarlyMessageCache earlyMessageCache;
private static volatile TypingStatusRepository typingStatusRepository;
private static volatile TypingStatusSender typingStatusSender;
private static volatile DatabaseObserver databaseObserver;
private static volatile TrimThreadsByDateManager trimThreadsByDateManager;
private static volatile ViewOnceMessageManager viewOnceMessageManager;
private static volatile ExpiringStoriesManager expiringStoriesManager;
private static volatile ExpiringMessageManager expiringMessageManager;
private static volatile DeletedCallEventManager deletedCallEventManager;
private static volatile Payments payments;
private static volatile SignalCallManager signalCallManager;
private static volatile ShakeToReport shakeToReport;
private static volatile OkHttpClient okHttpClient;
private static volatile OkHttpClient signalOkHttpClient;
private static volatile PendingRetryReceiptManager pendingRetryReceiptManager;
private static volatile PendingRetryReceiptCache pendingRetryReceiptCache;
private static volatile SignalWebSocket signalWebSocket;
private static volatile MessageNotifier messageNotifier;
private static volatile SignalServiceDataStoreImpl protocolStore;
private static volatile GiphyMp4Cache giphyMp4Cache;
private static volatile SimpleExoPlayerPool exoPlayerPool;
private static volatile AudioManagerCompat audioManagerCompat;
private static volatile DonationsService donationsService;
private static volatile CallLinksService callLinksService;
private static volatile ProfileService profileService;
private static volatile DeadlockDetector deadlockDetector;
private static volatile ClientZkReceiptOperations clientZkReceiptOperations;
private static volatile ScheduledMessageManager scheduledMessagesManager;
private static volatile LibSignalNetwork libsignalNetwork;
@MainThread
public static void init(@NonNull Application application, @NonNull Provider provider) {
synchronized (LOCK) {
if (ApplicationDependencies.application != null || ApplicationDependencies.provider != null) {
throw new IllegalStateException("Already initialized!");
}
ApplicationDependencies.application = application;
ApplicationDependencies.provider = provider;
ApplicationDependencies.appForegroundObserver = provider.provideAppForegroundObserver();
ApplicationDependencies.appForegroundObserver.begin();
}
}
@VisibleForTesting
public static boolean isInitialized() {
return ApplicationDependencies.application != null;
}
public static @NonNull Application getApplication() {
return application;
}
public static @NonNull SignalServiceAccountManager getSignalServiceAccountManager() {
SignalServiceAccountManager local = accountManager;
if (local != null) {
return local;
}
synchronized (LOCK) {
if (accountManager == null) {
accountManager = provider.provideSignalServiceAccountManager(getSignalServiceNetworkAccess().getConfiguration(), getGroupsV2Operations());
}
return accountManager;
}
}
public static @NonNull GroupsV2Authorization getGroupsV2Authorization() {
if (groupsV2Authorization == null) {
synchronized (LOCK) {
if (groupsV2Authorization == null) {
GroupsV2Authorization.ValueCache authCache = new GroupsV2AuthorizationMemoryValueCache(SignalStore.groupsV2AciAuthorizationCache());
groupsV2Authorization = new GroupsV2Authorization(getSignalServiceAccountManager().getGroupsV2Api(), authCache);
}
}
}
return groupsV2Authorization;
}
public static @NonNull GroupsV2Operations getGroupsV2Operations() {
if (groupsV2Operations == null) {
synchronized (LOCK) {
if (groupsV2Operations == null) {
groupsV2Operations = provider.provideGroupsV2Operations(getSignalServiceNetworkAccess().getConfiguration());
}
}
}
return groupsV2Operations;
}
public static @NonNull SignalServiceMessageSender getSignalServiceMessageSender() {
SignalServiceMessageSender local = messageSender;
if (local != null) {
return local;
}
synchronized (LOCK) {
if (messageSender == null) {
messageSender = provider.provideSignalServiceMessageSender(getSignalWebSocket(), getProtocolStore(), getSignalServiceNetworkAccess().getConfiguration());
}
return messageSender;
}
}
public static @NonNull SignalServiceMessageReceiver getSignalServiceMessageReceiver() {
synchronized (LOCK) {
if (messageReceiver == null) {
messageReceiver = provider.provideSignalServiceMessageReceiver(getSignalServiceNetworkAccess().getConfiguration());
}
return messageReceiver;
}
}
public static void resetSignalServiceMessageReceiver() {
synchronized (LOCK) {
messageReceiver = null;
}
}
public static void closeConnections() {
synchronized (LOCK) {
if (incomingMessageObserver != null) {
incomingMessageObserver.terminateAsync();
}
if (messageSender != null) {
messageSender.cancelInFlightRequests();
}
incomingMessageObserver = null;
messageReceiver = null;
accountManager = null;
messageSender = null;
}
}
public static void resetAllNetworkConnections() {
synchronized (LOCK) {
closeConnections();
if (libsignalNetwork != null) {
libsignalNetwork.resetSettings(getSignalServiceNetworkAccess().getConfiguration());
}
if (signalWebSocket != null) {
signalWebSocket.forceNewWebSockets();
}
}
}
public static @NonNull SignalServiceNetworkAccess getSignalServiceNetworkAccess() {
return provider.provideSignalServiceNetworkAccess();
}
public static @NonNull LiveRecipientCache getRecipientCache() {
if (recipientCache == null) {
synchronized (LOCK) {
if (recipientCache == null) {
recipientCache = provider.provideRecipientCache();
}
}
}
return recipientCache;
}
public static @NonNull JobManager getJobManager() {
if (jobManager == null) {
synchronized (JOB_MANAGER_LOCK) {
if (jobManager == null) {
jobManager = provider.provideJobManager();
}
}
}
return jobManager;
}
public static @NonNull FrameRateTracker getFrameRateTracker() {
if (frameRateTracker == null) {
synchronized (FRAME_RATE_TRACKER_LOCK) {
if (frameRateTracker == null) {
frameRateTracker = provider.provideFrameRateTracker();
}
}
}
return frameRateTracker;
}
public static @NonNull MegaphoneRepository getMegaphoneRepository() {
if (megaphoneRepository == null) {
synchronized (LOCK) {
if (megaphoneRepository == null) {
megaphoneRepository = provider.provideMegaphoneRepository();
}
}
}
return megaphoneRepository;
}
public static @NonNull EarlyMessageCache getEarlyMessageCache() {
if (earlyMessageCache == null) {
synchronized (LOCK) {
if (earlyMessageCache == null) {
earlyMessageCache = provider.provideEarlyMessageCache();
}
}
}
return earlyMessageCache;
}
public static @NonNull MessageNotifier getMessageNotifier() {
if (messageNotifier == null) {
synchronized (LOCK) {
if (messageNotifier == null) {
messageNotifier = provider.provideMessageNotifier();
}
}
}
return messageNotifier;
}
public static @NonNull IncomingMessageObserver getIncomingMessageObserver() {
IncomingMessageObserver local = incomingMessageObserver;
if (local != null) {
return local;
}
synchronized (LOCK) {
if (incomingMessageObserver == null) {
incomingMessageObserver = provider.provideIncomingMessageObserver();
}
return incomingMessageObserver;
}
}
public static @NonNull TrimThreadsByDateManager getTrimThreadsByDateManager() {
if (trimThreadsByDateManager == null) {
synchronized (LOCK) {
if (trimThreadsByDateManager == null) {
trimThreadsByDateManager = provider.provideTrimThreadsByDateManager();
}
}
}
return trimThreadsByDateManager;
}
public static @NonNull ViewOnceMessageManager getViewOnceMessageManager() {
if (viewOnceMessageManager == null) {
synchronized (LOCK) {
if (viewOnceMessageManager == null) {
viewOnceMessageManager = provider.provideViewOnceMessageManager();
}
}
}
return viewOnceMessageManager;
}
public static @NonNull ExpiringStoriesManager getExpireStoriesManager() {
if (expiringStoriesManager == null) {
synchronized (LOCK) {
if (expiringStoriesManager == null) {
expiringStoriesManager = provider.provideExpiringStoriesManager();
}
}
}
return expiringStoriesManager;
}
public static @NonNull PendingRetryReceiptManager getPendingRetryReceiptManager() {
if (pendingRetryReceiptManager == null) {
synchronized (LOCK) {
if (pendingRetryReceiptManager == null) {
pendingRetryReceiptManager = provider.providePendingRetryReceiptManager();
}
}
}
return pendingRetryReceiptManager;
}
public static @NonNull ExpiringMessageManager getExpiringMessageManager() {
if (expiringMessageManager == null) {
synchronized (LOCK) {
if (expiringMessageManager == null) {
expiringMessageManager = provider.provideExpiringMessageManager();
}
}
}
return expiringMessageManager;
}
public static @NonNull DeletedCallEventManager getDeletedCallEventManager() {
if (deletedCallEventManager == null) {
synchronized (LOCK) {
if (deletedCallEventManager == null) {
deletedCallEventManager = provider.provideDeletedCallEventManager();
}
}
}
return deletedCallEventManager;
}
public static @NonNull ScheduledMessageManager getScheduledMessageManager() {
if (scheduledMessagesManager == null) {
synchronized (LOCK) {
if (scheduledMessagesManager == null) {
scheduledMessagesManager = provider.provideScheduledMessageManager();
}
}
}
return scheduledMessagesManager;
}
public static TypingStatusRepository getTypingStatusRepository() {
if (typingStatusRepository == null) {
synchronized (LOCK) {
if (typingStatusRepository == null) {
typingStatusRepository = provider.provideTypingStatusRepository();
}
}
}
return typingStatusRepository;
}
public static TypingStatusSender getTypingStatusSender() {
if (typingStatusSender == null) {
synchronized (LOCK) {
if (typingStatusSender == null) {
typingStatusSender = provider.provideTypingStatusSender();
}
}
}
return typingStatusSender;
}
public static @NonNull DatabaseObserver getDatabaseObserver() {
if (databaseObserver == null) {
synchronized (LOCK) {
if (databaseObserver == null) {
databaseObserver = provider.provideDatabaseObserver();
}
}
}
return databaseObserver;
}
public static @NonNull Payments getPayments() {
if (payments == null) {
synchronized (LOCK) {
if (payments == null) {
payments = provider.providePayments(getSignalServiceAccountManager());
}
}
}
return payments;
}
public static @NonNull ShakeToReport getShakeToReport() {
if (shakeToReport == null) {
synchronized (LOCK) {
if (shakeToReport == null) {
shakeToReport = provider.provideShakeToReport();
}
}
}
return shakeToReport;
}
public static @NonNull SignalCallManager getSignalCallManager() {
if (signalCallManager == null) {
synchronized (LOCK) {
if (signalCallManager == null) {
signalCallManager = provider.provideSignalCallManager();
}
}
}
return signalCallManager;
}
public static @NonNull OkHttpClient getOkHttpClient() {
if (okHttpClient == null) {
synchronized (LOCK) {
if (okHttpClient == null) {
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new StandardUserAgentInterceptor())
.dns(SignalServiceNetworkAccess.DNS)
.build();
}
}
}
return okHttpClient;
}
public static @NonNull OkHttpClient getSignalOkHttpClient() {
if (signalOkHttpClient == null) {
synchronized (SIGNAL_HTTP_CLIENT_LOCK) {
if (signalOkHttpClient == null) {
try {
OkHttpClient baseClient = ApplicationDependencies.getOkHttpClient();
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustStore trustStore = new SignalServiceTrustStore(ApplicationDependencies.getApplication());
TrustManager[] trustManagers = BlacklistingTrustManager.createFor(trustStore);
sslContext.init(null, trustManagers, null);
signalOkHttpClient = baseClient.newBuilder()
.sslSocketFactory(new Tls12SocketFactory(sslContext.getSocketFactory()), (X509TrustManager) trustManagers[0])
.connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS))
.build();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new AssertionError(e);
}
}
}
}
return signalOkHttpClient;
}
public static @NonNull AppForegroundObserver getAppForegroundObserver() {
return appForegroundObserver;
}
public static @NonNull PendingRetryReceiptCache getPendingRetryReceiptCache() {
if (pendingRetryReceiptCache == null) {
synchronized (LOCK) {
if (pendingRetryReceiptCache == null) {
pendingRetryReceiptCache = provider.providePendingRetryReceiptCache();
}
}
}
return pendingRetryReceiptCache;
}
public static @NonNull SignalWebSocket getSignalWebSocket() {
if (signalWebSocket == null) {
synchronized (LOCK) {
if (signalWebSocket == null) {
signalWebSocket = provider.provideSignalWebSocket(() -> getSignalServiceNetworkAccess().getConfiguration(), ApplicationDependencies::getLibsignalNetwork);
}
}
}
return signalWebSocket;
}
public static @NonNull SignalServiceDataStoreImpl getProtocolStore() {
if (protocolStore == null) {
synchronized (LOCK) {
if (protocolStore == null) {
protocolStore = provider.provideProtocolStore();
}
}
}
return protocolStore;
}
public static void resetProtocolStores() {
synchronized (LOCK) {
protocolStore = null;
}
}
public static @NonNull GiphyMp4Cache getGiphyMp4Cache() {
if (giphyMp4Cache == null) {
synchronized (LOCK) {
if (giphyMp4Cache == null) {
giphyMp4Cache = provider.provideGiphyMp4Cache();
}
}
}
return giphyMp4Cache;
}
public static @NonNull ExoPlayerPool getExoPlayerPool() {
if (exoPlayerPool == null) {
synchronized (LOCK) {
if (exoPlayerPool == null) {
exoPlayerPool = provider.provideExoPlayerPool();
}
}
}
return exoPlayerPool;
}
public static @NonNull AudioManagerCompat getAndroidCallAudioManager() {
if (audioManagerCompat == null) {
synchronized (LOCK) {
if (audioManagerCompat == null) {
audioManagerCompat = provider.provideAndroidCallAudioManager();
}
}
}
return audioManagerCompat;
}
public static @NonNull DonationsService getDonationsService() {
if (donationsService == null) {
synchronized (LOCK) {
if (donationsService == null) {
donationsService = provider.provideDonationsService(getSignalServiceNetworkAccess().getConfiguration(), getGroupsV2Operations());
}
}
}
return donationsService;
}
public static @NonNull CallLinksService getCallLinksService() {
if (callLinksService == null) {
synchronized (LOCK) {
if (callLinksService == null) {
callLinksService = provider.provideCallLinksService(getSignalServiceNetworkAccess().getConfiguration(), getGroupsV2Operations());
}
}
}
return callLinksService;
}
public static @NonNull ProfileService getProfileService() {
if (profileService == null) {
synchronized (LOCK) {
if (profileService == null) {
profileService = provider.provideProfileService(ApplicationDependencies.getGroupsV2Operations().getProfileOperations(),
ApplicationDependencies.getSignalServiceMessageReceiver(),
ApplicationDependencies.getSignalWebSocket());
}
}
}
return profileService;
}
public static @NonNull ClientZkReceiptOperations getClientZkReceiptOperations() {
if (clientZkReceiptOperations == null) {
synchronized (LOCK) {
if (clientZkReceiptOperations == null) {
clientZkReceiptOperations = provider.provideClientZkReceiptOperations(getSignalServiceNetworkAccess().getConfiguration());
}
}
}
return clientZkReceiptOperations;
}
public static @NonNull DeadlockDetector getDeadlockDetector() {
if (deadlockDetector == null) {
synchronized (LOCK) {
if (deadlockDetector == null) {
deadlockDetector = provider.provideDeadlockDetector();
}
}
}
return deadlockDetector;
}
public static @NonNull LibSignalNetwork getLibsignalNetwork() {
if (libsignalNetwork == null) {
synchronized (LIBSIGNAL_NETWORK_LOCK) {
if (libsignalNetwork == null) {
libsignalNetwork = provider.provideLibsignalNetwork(getSignalServiceNetworkAccess().getConfiguration());
}
}
}
return libsignalNetwork;
}
public interface Provider {
@NonNull GroupsV2Operations provideGroupsV2Operations(@NonNull SignalServiceConfiguration signalServiceConfiguration);
@NonNull SignalServiceAccountManager provideSignalServiceAccountManager(@NonNull SignalServiceConfiguration signalServiceConfiguration, @NonNull GroupsV2Operations groupsV2Operations);
@NonNull SignalServiceMessageSender provideSignalServiceMessageSender(@NonNull SignalWebSocket signalWebSocket, @NonNull SignalServiceDataStore protocolStore, @NonNull SignalServiceConfiguration signalServiceConfiguration);
@NonNull SignalServiceMessageReceiver provideSignalServiceMessageReceiver(@NonNull SignalServiceConfiguration signalServiceConfiguration);
@NonNull SignalServiceNetworkAccess provideSignalServiceNetworkAccess();
@NonNull LiveRecipientCache provideRecipientCache();
@NonNull JobManager provideJobManager();
@NonNull FrameRateTracker provideFrameRateTracker();
@NonNull MegaphoneRepository provideMegaphoneRepository();
@NonNull EarlyMessageCache provideEarlyMessageCache();
@NonNull MessageNotifier provideMessageNotifier();
@NonNull IncomingMessageObserver provideIncomingMessageObserver();
@NonNull TrimThreadsByDateManager provideTrimThreadsByDateManager();
@NonNull ViewOnceMessageManager provideViewOnceMessageManager();
@NonNull ExpiringStoriesManager provideExpiringStoriesManager();
@NonNull ExpiringMessageManager provideExpiringMessageManager();
@NonNull DeletedCallEventManager provideDeletedCallEventManager();
@NonNull TypingStatusRepository provideTypingStatusRepository();
@NonNull TypingStatusSender provideTypingStatusSender();
@NonNull DatabaseObserver provideDatabaseObserver();
@NonNull Payments providePayments(@NonNull SignalServiceAccountManager signalServiceAccountManager);
@NonNull ShakeToReport provideShakeToReport();
@NonNull AppForegroundObserver provideAppForegroundObserver();
@NonNull SignalCallManager provideSignalCallManager();
@NonNull PendingRetryReceiptManager providePendingRetryReceiptManager();
@NonNull PendingRetryReceiptCache providePendingRetryReceiptCache();
@NonNull SignalWebSocket provideSignalWebSocket(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier, @NonNull Supplier<LibSignalNetwork> libSignalNetworkSupplier);
@NonNull SignalServiceDataStoreImpl provideProtocolStore();
@NonNull GiphyMp4Cache provideGiphyMp4Cache();
@NonNull SimpleExoPlayerPool provideExoPlayerPool();
@NonNull AudioManagerCompat provideAndroidCallAudioManager();
@NonNull DonationsService provideDonationsService(@NonNull SignalServiceConfiguration signalServiceConfiguration, @NonNull GroupsV2Operations groupsV2Operations);
@NonNull CallLinksService provideCallLinksService(@NonNull SignalServiceConfiguration signalServiceConfiguration, @NonNull GroupsV2Operations groupsV2Operations);
@NonNull ProfileService provideProfileService(@NonNull ClientZkProfileOperations profileOperations, @NonNull SignalServiceMessageReceiver signalServiceMessageReceiver, @NonNull SignalWebSocket signalWebSocket);
@NonNull DeadlockDetector provideDeadlockDetector();
@NonNull ClientZkReceiptOperations provideClientZkReceiptOperations(@NonNull SignalServiceConfiguration signalServiceConfiguration);
@NonNull ScheduledMessageManager provideScheduledMessageManager();
@NonNull LibSignalNetwork provideLibsignalNetwork(@NonNull SignalServiceConfiguration config);
}
}

View File

@@ -104,9 +104,9 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
* Implementation of {@link AppDependencies.Provider} that provides real app dependencies.
*/
public class ApplicationDependencyProvider implements ApplicationDependencies.Provider {
public class ApplicationDependencyProvider implements AppDependencies.Provider {
private final Application context;

View File

@@ -0,0 +1,160 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.dependencies
import android.app.Application
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.subjects.Subject
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import org.signal.core.util.resettableLazy
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl
import org.thoughtcrime.securesms.groups.GroupsV2Authorization
import org.thoughtcrime.securesms.groups.GroupsV2AuthorizationMemoryValueCache
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.messages.IncomingMessageObserver
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor
import org.thoughtcrime.securesms.payments.Payments
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess
import org.thoughtcrime.securesms.push.SignalServiceTrustStore
import org.whispersystems.signalservice.api.SignalServiceAccountManager
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver
import org.whispersystems.signalservice.api.SignalServiceMessageSender
import org.whispersystems.signalservice.api.SignalWebSocket
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations
import org.whispersystems.signalservice.api.push.TrustStore
import org.whispersystems.signalservice.api.services.CallLinksService
import org.whispersystems.signalservice.api.services.DonationsService
import org.whispersystems.signalservice.api.services.ProfileService
import org.whispersystems.signalservice.api.util.Tls12SocketFactory
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState
import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager
import org.whispersystems.signalservice.internal.util.Util
import org.whispersystems.signalservice.internal.websocket.LibSignalNetwork
import java.security.KeyManagementException
import java.security.NoSuchAlgorithmException
import javax.net.ssl.SSLContext
import javax.net.ssl.X509TrustManager
/**
* A subset of [AppDependencies] that relies on the network. We bundle them together because when the network
* needs to get reset, we just throw out the whole thing and recreate it.
*/
class NetworkDependenciesModule(
private val application: Application,
private val provider: AppDependencies.Provider,
private val webSocketStateSubject: Subject<WebSocketConnectionState>
) {
private val disposables: CompositeDisposable = CompositeDisposable()
val signalServiceNetworkAccess: SignalServiceNetworkAccess by lazy {
provider.provideSignalServiceNetworkAccess()
}
private val _protocolStore = resettableLazy {
provider.provideProtocolStore()
}
val protocolStore: SignalServiceDataStoreImpl by _protocolStore
private val _signalServiceMessageSender = resettableLazy {
provider.provideSignalServiceMessageSender(signalWebSocket, protocolStore, signalServiceNetworkAccess.getConfiguration())
}
val signalServiceMessageSender: SignalServiceMessageSender by _signalServiceMessageSender
val incomingMessageObserver: IncomingMessageObserver by lazy {
provider.provideIncomingMessageObserver()
}
val signalServiceAccountManager: SignalServiceAccountManager by lazy {
provider.provideSignalServiceAccountManager(signalServiceNetworkAccess.getConfiguration(), groupsV2Operations)
}
val libsignalNetwork: LibSignalNetwork by lazy {
provider.provideLibsignalNetwork(signalServiceNetworkAccess.getConfiguration())
}
val signalWebSocket: SignalWebSocket by lazy {
provider.provideSignalWebSocket({ signalServiceNetworkAccess.getConfiguration() }, { libsignalNetwork }).also {
disposables += it.webSocketState.subscribe { webSocketStateSubject.onNext(it) }
}
}
val groupsV2Authorization: GroupsV2Authorization by lazy {
val authCache: GroupsV2Authorization.ValueCache = GroupsV2AuthorizationMemoryValueCache(SignalStore.groupsV2AciAuthorizationCache())
GroupsV2Authorization(signalServiceAccountManager.groupsV2Api, authCache)
}
val groupsV2Operations: GroupsV2Operations by lazy {
provider.provideGroupsV2Operations(signalServiceNetworkAccess.getConfiguration())
}
val clientZkReceiptOperations: ClientZkReceiptOperations by lazy {
provider.provideClientZkReceiptOperations(signalServiceNetworkAccess.getConfiguration())
}
val signalServiceMessageReceiver: SignalServiceMessageReceiver by lazy {
provider.provideSignalServiceMessageReceiver(signalServiceNetworkAccess.getConfiguration())
}
val payments: Payments by lazy {
provider.providePayments(signalServiceAccountManager)
}
val callLinksService: CallLinksService by lazy {
provider.provideCallLinksService(signalServiceNetworkAccess.getConfiguration(), groupsV2Operations)
}
val profileService: ProfileService by lazy {
provider.provideProfileService(groupsV2Operations.profileOperations, signalServiceMessageReceiver, signalWebSocket)
}
val donationsService: DonationsService by lazy {
provider.provideDonationsService(signalServiceNetworkAccess.getConfiguration(), groupsV2Operations)
}
val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.addInterceptor(StandardUserAgentInterceptor())
.dns(SignalServiceNetworkAccess.DNS)
.build()
}
val signalOkHttpClient: OkHttpClient by lazy {
try {
val baseClient = okHttpClient
val sslContext = SSLContext.getInstance("TLS")
val trustStore: TrustStore = SignalServiceTrustStore(application)
val trustManagers = BlacklistingTrustManager.createFor(trustStore)
sslContext.init(null, trustManagers, null)
baseClient.newBuilder()
.sslSocketFactory(Tls12SocketFactory(sslContext.socketFactory), trustManagers[0] as X509TrustManager)
.connectionSpecs(Util.immutableList(ConnectionSpec.RESTRICTED_TLS))
.build()
} catch (e: NoSuchAlgorithmException) {
throw AssertionError(e)
} catch (e: KeyManagementException) {
throw AssertionError(e)
}
}
fun closeConnections() {
incomingMessageObserver.terminateAsync()
if (_signalServiceMessageSender.isInitialized()) {
signalServiceMessageSender.cancelInFlightRequests()
}
disposables.clear()
}
fun resetProtocolStores() {
_protocolStore.reset()
_signalServiceMessageSender.reset()
}
}