Path: blob/main/SignalServiceKit/Environment/SSKEnvironment.swift
1 views
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public class SSKEnvironment: NSObject {
private static var _shared: SSKEnvironment?
public static var hasShared: Bool { _shared != nil }
@objc
public static var shared: SSKEnvironment { _shared! }
public static func setShared(_ env: SSKEnvironment?, isRunningTests: Bool) {
owsPrecondition((_shared == nil && env != nil) || isRunningTests)
_shared = env
}
#if TESTABLE_BUILD
public private(set) var contactManagerRef: any ContactManager
public private(set) var messageSenderRef: MessageSender
public private(set) var networkManagerRef: NetworkManager
public private(set) var paymentsHelperRef: PaymentsHelperSwift
public private(set) var groupsV2Ref: GroupsV2
#else
public let contactManagerRef: any ContactManager
public let messageSenderRef: MessageSender
public let networkManagerRef: NetworkManager
public let paymentsHelperRef: PaymentsHelperSwift
public let groupsV2Ref: GroupsV2
#endif
/// This should be deprecated.
public var contactManagerImplRef: OWSContactsManager { contactManagerRef as! OWSContactsManager }
@objc
public var contactManagerObjcRef: ContactsManagerProtocol { contactManagerRef }
public let pendingReceiptRecorderRef: PendingReceiptRecorder
public let profileManagerRef: ProfileManager
/// This should be deprecated.
public var profileManagerImplRef: OWSProfileManager { profileManagerRef as! OWSProfileManager }
public let messageReceiverRef: MessageReceiver
public let blockingManagerRef: BlockingManager
public let remoteConfigManagerRef: RemoteConfigManager
public let udManagerRef: OWSUDManager
public let messageDecrypterRef: OWSMessageDecrypter
public let groupMessageProcessorManagerRef: GroupMessageProcessorManager
public let ows2FAManagerRef: OWS2FAManager
@objc
public let receiptManagerRef: OWSReceiptManager
@objc
public let receiptSenderRef: ReceiptSender
public let reachabilityManagerRef: SSKReachabilityManager
public let syncManagerRef: SyncManagerProtocol
public let typingIndicatorsRef: TypingIndicators
public let stickerManagerRef: StickerManager
@objc
public let databaseStorageRef: SDSDatabaseStorage
public let signalServiceAddressCacheRef: SignalServiceAddressCache
public let signalServiceRef: OWSSignalServiceProtocol
public let storageServiceManagerRef: StorageServiceManager
public let sskPreferencesRef: SSKPreferences
public let groupV2UpdatesRef: GroupV2Updates
public let messageFetcherJobRef: MessageFetcherJob
public let versionedProfilesRef: VersionedProfiles
@objc
public let modelReadCachesRef: ModelReadCaches
public let earlyMessageManagerRef: EarlyMessageManager
public let messagePipelineSupervisorRef: MessagePipelineSupervisor
public let messageProcessorRef: MessageProcessor
public let paymentsCurrenciesRef: PaymentsCurrenciesSwift
@objc
public let paymentsEventsRef: PaymentsEvents
public let owsPaymentsLockRef: OWSPaymentsLock
public let mobileCoinHelperRef: MobileCoinHelper
public let spamChallengeResolverRef: SpamChallengeResolver
public let senderKeyStoreRef: OldSenderKeyStore
public let phoneNumberUtilRef: PhoneNumberUtil
public let webSocketFactoryRef: WebSocketFactory
public let systemStoryManagerRef: SystemStoryManagerProtocol
public let contactDiscoveryManagerRef: ContactDiscoveryManager
public let notificationPresenterRef: any NotificationPresenter
public let messageSendLogRef: MessageSendLog
public let preferencesRef: Preferences
public let proximityMonitoringManagerRef: OWSProximityMonitoringManager
public let avatarBuilderRef: AvatarBuilder
public let smJobQueuesRef: SignalMessagingJobQueues
public let groupCallManagerRef: GroupCallManager
public let profileFetcherRef: any ProfileFetcher
public let messageSenderJobQueueRef: MessageSenderJobQueue
public let localUserLeaveGroupJobQueueRef: LocalUserLeaveGroupJobQueue
public let callRecordDeleteAllJobQueueRef: CallRecordDeleteAllJobQueue
public let bulkDeleteInteractionJobQueueRef: BulkDeleteInteractionJobQueue
let donationReceiptCredentialRedemptionJobQueue: DonationReceiptCredentialRedemptionJobQueue
private let appExpiryRef: AppExpiry
private let aciSignalProtocolStoreRef: SignalProtocolStore
private let pniSignalProtocolStoreRef: SignalProtocolStore
init(
contactManager: any ContactManager,
messageSender: MessageSender,
pendingReceiptRecorder: PendingReceiptRecorder,
profileManager: ProfileManager,
networkManager: NetworkManager,
messageReceiver: MessageReceiver,
blockingManager: BlockingManager,
remoteConfigManager: RemoteConfigManager,
aciSignalProtocolStore: SignalProtocolStore,
pniSignalProtocolStore: SignalProtocolStore,
udManager: OWSUDManager,
messageDecrypter: OWSMessageDecrypter,
groupMessageProcessorManager: GroupMessageProcessorManager,
ows2FAManager: OWS2FAManager,
receiptManager: OWSReceiptManager,
receiptSender: ReceiptSender,
reachabilityManager: SSKReachabilityManager,
syncManager: SyncManagerProtocol,
typingIndicators: TypingIndicators,
stickerManager: StickerManager,
databaseStorage: SDSDatabaseStorage,
signalServiceAddressCache: SignalServiceAddressCache,
signalService: OWSSignalServiceProtocol,
storageServiceManager: StorageServiceManager,
sskPreferences: SSKPreferences,
groupsV2: GroupsV2,
groupV2Updates: GroupV2Updates,
messageFetcherJob: MessageFetcherJob,
versionedProfiles: VersionedProfiles,
modelReadCaches: ModelReadCaches,
earlyMessageManager: EarlyMessageManager,
messagePipelineSupervisor: MessagePipelineSupervisor,
appExpiry: AppExpiry,
messageProcessor: MessageProcessor,
paymentsHelper: PaymentsHelperSwift,
paymentsCurrencies: PaymentsCurrenciesSwift,
paymentsEvents: PaymentsEvents,
paymentsLock: OWSPaymentsLock,
mobileCoinHelper: MobileCoinHelper,
spamChallengeResolver: SpamChallengeResolver,
senderKeyStore: OldSenderKeyStore,
phoneNumberUtil: PhoneNumberUtil,
webSocketFactory: WebSocketFactory,
systemStoryManager: SystemStoryManagerProtocol,
contactDiscoveryManager: ContactDiscoveryManager,
notificationPresenter: any NotificationPresenter,
messageSendLog: MessageSendLog,
messageSenderJobQueue: MessageSenderJobQueue,
localUserLeaveGroupJobQueue: LocalUserLeaveGroupJobQueue,
callRecordDeleteAllJobQueue: CallRecordDeleteAllJobQueue,
bulkDeleteInteractionJobQueue: BulkDeleteInteractionJobQueue,
donationReceiptCredentialRedemptionJobQueue: DonationReceiptCredentialRedemptionJobQueue,
preferences: Preferences,
proximityMonitoringManager: OWSProximityMonitoringManager,
avatarBuilder: AvatarBuilder,
smJobQueues: SignalMessagingJobQueues,
groupCallManager: GroupCallManager,
profileFetcher: any ProfileFetcher,
) {
self.contactManagerRef = contactManager
self.messageSenderRef = messageSender
self.pendingReceiptRecorderRef = pendingReceiptRecorder
self.profileManagerRef = profileManager
self.networkManagerRef = networkManager
self.messageReceiverRef = messageReceiver
self.blockingManagerRef = blockingManager
self.remoteConfigManagerRef = remoteConfigManager
self.aciSignalProtocolStoreRef = aciSignalProtocolStore
self.pniSignalProtocolStoreRef = pniSignalProtocolStore
self.udManagerRef = udManager
self.messageDecrypterRef = messageDecrypter
self.groupMessageProcessorManagerRef = groupMessageProcessorManager
self.ows2FAManagerRef = ows2FAManager
self.receiptManagerRef = receiptManager
self.receiptSenderRef = receiptSender
self.syncManagerRef = syncManager
self.reachabilityManagerRef = reachabilityManager
self.typingIndicatorsRef = typingIndicators
self.stickerManagerRef = stickerManager
self.databaseStorageRef = databaseStorage
self.signalServiceAddressCacheRef = signalServiceAddressCache
self.signalServiceRef = signalService
self.storageServiceManagerRef = storageServiceManager
self.sskPreferencesRef = sskPreferences
self.groupsV2Ref = groupsV2
self.groupV2UpdatesRef = groupV2Updates
self.messageFetcherJobRef = messageFetcherJob
self.versionedProfilesRef = versionedProfiles
self.modelReadCachesRef = modelReadCaches
self.earlyMessageManagerRef = earlyMessageManager
self.messagePipelineSupervisorRef = messagePipelineSupervisor
self.appExpiryRef = appExpiry
self.messageProcessorRef = messageProcessor
self.paymentsHelperRef = paymentsHelper
self.paymentsCurrenciesRef = paymentsCurrencies
self.paymentsEventsRef = paymentsEvents
self.owsPaymentsLockRef = paymentsLock
self.mobileCoinHelperRef = mobileCoinHelper
self.spamChallengeResolverRef = spamChallengeResolver
self.senderKeyStoreRef = senderKeyStore
self.phoneNumberUtilRef = phoneNumberUtil
self.webSocketFactoryRef = webSocketFactory
self.systemStoryManagerRef = systemStoryManager
self.contactDiscoveryManagerRef = contactDiscoveryManager
self.notificationPresenterRef = notificationPresenter
self.messageSendLogRef = messageSendLog
self.messageSenderJobQueueRef = messageSenderJobQueue
self.localUserLeaveGroupJobQueueRef = localUserLeaveGroupJobQueue
self.callRecordDeleteAllJobQueueRef = callRecordDeleteAllJobQueue
self.bulkDeleteInteractionJobQueueRef = bulkDeleteInteractionJobQueue
self.donationReceiptCredentialRedemptionJobQueue = donationReceiptCredentialRedemptionJobQueue
self.preferencesRef = preferences
self.proximityMonitoringManagerRef = proximityMonitoringManager
self.avatarBuilderRef = avatarBuilder
self.smJobQueuesRef = smJobQueues
self.groupCallManagerRef = groupCallManager
self.profileFetcherRef = profileFetcher
}
public func signalProtocolStoreRef(for identity: OWSIdentity) -> SignalProtocolStore {
switch identity {
case .aci:
return aciSignalProtocolStoreRef
case .pni:
return pniSignalProtocolStoreRef
}
}
/// Warms (or re-warms) various caches throughout the app.
///
/// This may be called multiple times within a single process.
///
/// Re-warming helps ensure the NSE sees the same state as the Main App.
@MainActor
func warmCaches(appReadiness: AppReadiness, dependenciesBridge: DependenciesBridge) {
// Note: All of these methods must be safe to invoke repeatedly.
self.verifyPniAndPniIdentityKey(dependenciesBridge: dependenciesBridge)
self.fixLocalRecipientIfNeeded(dependenciesBridge: dependenciesBridge)
SignalProxy.warmCaches(appReadiness: appReadiness)
self.signalServiceRef.warmCaches()
self.profileManagerRef.warmCaches()
dependenciesBridge.svr.warmCaches()
self.typingIndicatorsRef.warmCaches()
self.paymentsHelperRef.warmCaches()
self.paymentsCurrenciesRef.warmCaches()
StoryManager.setup(appReadiness: appReadiness)
dependenciesBridge.db.read { tx in appExpiryRef.warmCaches(with: tx) }
}
@MainActor
private func verifyPniAndPniIdentityKey(dependenciesBridge: DependenciesBridge) {
let databaseStorage = self.databaseStorageRef
let tsAccountManager = dependenciesBridge.tsAccountManager
let mustHavePni: Bool
let mustHavePniIdentityKey: Bool
switch tsAccountManager.registrationStateWithMaybeSneakyTransaction {
case .provisioned:
mustHavePni = true
mustHavePniIdentityKey = true
case .registered:
mustHavePni = true
mustHavePniIdentityKey = true
default:
mustHavePni = false
mustHavePniIdentityKey = false
}
guard mustHavePni || mustHavePniIdentityKey else {
return
}
let (hasPni, hasPniIdentityKey) = databaseStorage.read { tx -> (Bool, Bool) in
let hasPni = tsAccountManager.localIdentifiers(tx: tx)!.pni != nil
let hasPniIdentityKey = dependenciesBridge.identityManager.identityKeyPair(for: .pni, tx: tx) != nil
return (hasPni, hasPniIdentityKey)
}
if (!hasPni && mustHavePni) || (!hasPniIdentityKey && mustHavePniIdentityKey) {
Logger.warn("Deregistering because PNI state is missing (hasPni: \(hasPni); hasPniIdentityKey: \(hasPniIdentityKey))")
databaseStorage.write { tx in
dependenciesBridge.registrationStateChangeManager.setIsDeregisteredOrDelinked(true, tx: tx)
}
}
}
/// Ensures the local SignalRecipient is correct.
///
/// This primarily serves to ensure the local SignalRecipient has its own
/// Pni (a one-time migration), but it also helps ensure that the value is
/// always consistent with TSAccountManager's values.
private func fixLocalRecipientIfNeeded(dependenciesBridge: DependenciesBridge) {
let blockedRecipientStore = dependenciesBridge.blockedRecipientStore
let databaseStorage = self.databaseStorageRef
let recipientMerger = dependenciesBridge.recipientMerger
let tsAccountManager = dependenciesBridge.tsAccountManager
databaseStorage.write { tx in
guard let localIdentifiers = tsAccountManager.localIdentifiers(tx: tx) else {
return // Not registered yet.
}
guard let phoneNumber = E164(localIdentifiers.phoneNumber) else {
return // Registered with an invalid phone number.
}
let localRecipient = recipientMerger.applyMergeForLocalAccount(
aci: localIdentifiers.aci,
phoneNumber: phoneNumber,
pni: localIdentifiers.pni,
tx: tx,
)
blockedRecipientStore.setBlocked(false, recipientId: localRecipient.id, tx: tx)
}
}
#if TESTABLE_BUILD
public func setContactManagerForUnitTests(_ contactManager: any ContactManager) {
self.contactManagerRef = contactManager
}
public func setMessageSenderForUnitTests(_ messageSender: MessageSender) {
self.messageSenderRef = messageSender
}
public func setNetworkManagerForUnitTests(_ networkManager: NetworkManager) {
self.networkManagerRef = networkManager
}
public func setPaymentsHelperForUnitTests(_ paymentsHelper: PaymentsHelperSwift) {
self.paymentsHelperRef = paymentsHelper
}
public func setGroupsV2ForUnitTests(_ groupsV2: GroupsV2) {
self.groupsV2Ref = groupsV2
}
#endif
}