Background improvements

This commit is contained in:
Ali 2023-02-10 22:17:59 +04:00
parent f2d01c4f8a
commit 5e367f191d
3 changed files with 116 additions and 39 deletions

View file

@ -935,7 +935,14 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
return .single(nil)
}
}
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in application.beginBackgroundTask(withName: name, expirationHandler: expiration) }, endBackgroundTask: { id in application.endBackgroundTask(id) }, backgroundTimeRemaining: { application.backgroundTimeRemaining }, acquireIdleExtension: {
let wakeupManager = SharedWakeupManager(beginBackgroundTask: { name, expiration in
let id = application.beginBackgroundTask(withName: name, expirationHandler: expiration)
Logger.shared.log("App \(self.episodeId)", "Begin background task \(name): \(id)")
return id
}, endBackgroundTask: { id in
print("App \(self.episodeId)", "End background task \(id)")
application.endBackgroundTask(id)
}, backgroundTimeRemaining: { application.backgroundTimeRemaining }, acquireIdleExtension: {
return applicationBindings.pushIdleTimerExtension()
}, activeAccounts: sharedContext.activeAccountContexts |> map { ($0.0?.account, $0.1.map { ($0.0, $0.1.account) }) }, liveLocationPolling: liveLocationPolling, watchTasks: watchTasks, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: self.hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager, accountUserInterfaceInUse: { id in
return sharedContext.accountUserInterfaceInUse(id)
@ -1639,9 +1646,6 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
extendNow = true
}
}
#if DEBUG
extendNow = false
#endif
sharedApplicationContext.wakeupManager.allowBackgroundTimeExtension(timeout: 2.0, extendNow: extendNow)
})
@ -1713,7 +1717,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Logger.shared.log("App \(self.episodeId)", "register for notifications: didRegisterForRemoteNotificationsWithDeviceToken (deviceToken: \(deviceToken))")
Logger.shared.log("App \(self.episodeId)", "register for notifications: didRegisterForRemoteNotificationsWithDeviceToken (deviceToken: \(hexString(deviceToken)))")
self.notificationTokenPromise.set(.single(deviceToken))
}

View file

@ -175,12 +175,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private var spotlightDataContext: SpotlightDataContext?
private var widgetDataContext: WidgetDataContext?
private weak var appDelegate: AppDelegate?
private var invalidatedApsToken: Data?
init(mainWindow: Window1?, sharedContainerPath: String, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager<TelegramAccountManagerTypes>, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, hasInAppPurchases: Bool, rootPath: String, legacyBasePath: String?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, firebaseSecretStream: Signal<[String: String], NoError>, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }, appDelegate: AppDelegate?) {
assert(Queue.mainQueue().isCurrent())
precondition(!testHasInstance)
testHasInstance = true
self.appDelegate = appDelegate
self.mainWindow = mainWindow
self.applicationBindings = applicationBindings
self.sharedContainerPath = sharedContainerPath
@ -902,10 +907,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return true
})
self.registeredNotificationTokensDisposable.set((combineLatest(queue: .mainQueue(), settings, self.activeAccountContexts)
|> mapToSignal { settings, activeAccountsAndInfo -> Signal<Never, NoError> in
let updatedApsToken = self.apsNotificationToken |> distinctUntilChanged(isEqual: { $0 == $1 })
self.registeredNotificationTokensDisposable.set((combineLatest(
queue: .mainQueue(),
settings,
self.activeAccountContexts,
updatedApsToken
)
|> mapToSignal { settings, activeAccountsAndInfo, apsNotificationToken -> Signal<(Bool, Data?), NoError> in
let (primary, activeAccounts, _) = activeAccountsAndInfo
var applied: [Signal<Never, NoError>] = []
var appliedApsList: [Signal<Bool?, NoError>] = []
var appliedVoipList: [Signal<Never, NoError>] = []
var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.account.testingEnvironment }).map({ $0.account.peerId.id })
var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.account.testingEnvironment }).map({ $0.account.peerId.id })
@ -928,18 +940,19 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
for (_, account, _) in activeAccounts {
let appliedAps: Signal<Never, NoError>
let appliedAps: Signal<Bool, NoError>
let appliedVoip: Signal<Never, NoError>
if !activeProductionUserIds.contains(account.account.peerId.id) && !activeTestingUserIds.contains(account.account.peerId.id) {
appliedAps = self.apsNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 })
|> mapToSignal { token -> Signal<Never, NoError> in
guard let token = token else {
return .complete()
if let apsNotificationToken {
appliedAps = account.engine.accountData.unregisterNotificationToken(token: apsNotificationToken, type: .aps(encrypt: false), otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
|> map { _ -> Bool in
}
return account.engine.accountData.unregisterNotificationToken(token: token, type: .aps(encrypt: false), otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
|> then(.single(true))
} else {
appliedAps = .single(true)
}
appliedVoip = self.voipNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 })
|> mapToSignal { token -> Signal<Never, NoError> in
@ -949,19 +962,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return account.engine.accountData.unregisterNotificationToken(token: token, type: .voip, otherAccountUserIds: (account.account.testingEnvironment ? allTestingUserIds : allProductionUserIds).filter({ $0 != account.account.peerId.id }))
}
} else {
appliedAps = self.apsNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 })
|> mapToSignal { token -> Signal<Never, NoError> in
guard let token = token else {
return .complete()
}
let encrypt: Bool
if #available(iOS 10.0, *) {
encrypt = true
} else {
encrypt = false
}
return account.engine.accountData.registerNotificationToken(token: token, type: .aps(encrypt: encrypt), sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
if let apsNotificationToken {
appliedAps = account.engine.accountData.registerNotificationToken(token: apsNotificationToken, type: .aps(encrypt: true), sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
} else {
appliedAps = .single(true)
}
appliedVoip = self.voipNotificationToken
|> distinctUntilChanged(isEqual: { $0 == $1 })
@ -970,15 +974,47 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return .complete()
}
return account.engine.accountData.registerNotificationToken(token: token, type: .voip, sandbox: sandbox, otherAccountUserIds: (account.account.testingEnvironment ? activeTestingUserIds : activeProductionUserIds).filter({ $0 != account.account.peerId.id }), excludeMutedChats: !settings.includeMuted)
|> ignoreValues
}
}
applied.append(appliedAps)
applied.append(appliedVoip)
appliedApsList.append(Signal<Bool?, NoError>.single(nil) |> then(appliedAps |> map(Optional.init)))
appliedVoipList.append(appliedVoip)
}
return combineLatest(applied)
|> ignoreValues
}).start())
let allApsSuccess = combineLatest(appliedApsList)
|> map { values -> Bool in
return !values.contains(false)
}
let allVoipSuccess = combineLatest(appliedVoipList)
return combineLatest(
allApsSuccess,
Signal<Void, NoError>.single(Void())
|> then(
allVoipSuccess
|> map { _ -> Void in
return Void()
}
)
)
|> map { allApsSuccess, _ -> (Bool, Data?) in
return (allApsSuccess, apsNotificationToken)
}
}
|> deliverOnMainQueue).start(next: { [weak self] allApsSuccess, apsToken in
guard let self, let appDelegate = self.appDelegate else {
return
}
if !allApsSuccess {
if self.invalidatedApsToken != apsToken {
self.invalidatedApsToken = apsToken
appDelegate.requestNotificationTokenInvalidation()
}
}
}))
}
public func beginNewAuth(testingEnvironment: Bool) {

View file

@ -54,6 +54,7 @@ public final class SharedWakeupManager {
private var inForeground: Bool = false
private var hasActiveAudioSession: Bool = false
private var activeExplicitExtensionTimer: SwiftSignalKit.Timer?
private var activeExplicitExtensionTask: UIBackgroundTaskIdentifier?
private var allowBackgroundTimeExtensionDeadline: Double?
private var allowBackgroundTimeExtensionDeadlineTimer: SwiftSignalKit.Timer?
private var isInBackgroundExtension: Bool = false
@ -87,6 +88,10 @@ public final class SharedWakeupManager {
if value {
strongSelf.activeExplicitExtensionTimer?.invalidate()
strongSelf.activeExplicitExtensionTimer = nil
if let activeExplicitExtensionTask = strongSelf.activeExplicitExtensionTask {
strongSelf.activeExplicitExtensionTask = nil
strongSelf.endBackgroundTask(activeExplicitExtensionTask)
}
strongSelf.allowBackgroundTimeExtensionDeadlineTimer?.invalidate()
strongSelf.allowBackgroundTimeExtensionDeadlineTimer = nil
}
@ -198,7 +203,7 @@ public final class SharedWakeupManager {
func allowBackgroundTimeExtension(timeout: Double, extendNow: Bool = false) {
let shouldCheckTasks = self.allowBackgroundTimeExtensionDeadline == nil
self.allowBackgroundTimeExtensionDeadline = CACurrentMediaTime() + timeout
self.allowBackgroundTimeExtensionDeadline = CFAbsoluteTimeGetCurrent() + timeout
self.allowBackgroundTimeExtensionDeadlineTimer?.invalidate()
self.allowBackgroundTimeExtensionDeadlineTimer = SwiftSignalKit.Timer(timeout: timeout, repeat: false, completion: { [weak self] in
@ -213,15 +218,35 @@ public final class SharedWakeupManager {
if extendNow {
if self.activeExplicitExtensionTimer == nil {
self.activeExplicitExtensionTimer = SwiftSignalKit.Timer(timeout: 20.0, repeat: false, completion: { [weak self] in
let activeExplicitExtensionTimer = SwiftSignalKit.Timer(timeout: 20.0, repeat: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.activeExplicitExtensionTimer?.invalidate()
strongSelf.activeExplicitExtensionTimer = nil
if let activeExplicitExtensionTask = strongSelf.activeExplicitExtensionTask {
strongSelf.activeExplicitExtensionTask = nil
strongSelf.endBackgroundTask(activeExplicitExtensionTask)
}
strongSelf.checkTasks()
}, queue: .mainQueue())
self.activeExplicitExtensionTimer?.start()
self.activeExplicitExtensionTimer = activeExplicitExtensionTimer
activeExplicitExtensionTimer.start()
self.activeExplicitExtensionTask = self.beginBackgroundTask("explicit-extension") { [weak self, weak activeExplicitExtensionTimer] in
guard let self, let activeExplicitExtensionTimer else {
return
}
if self.activeExplicitExtensionTimer === activeExplicitExtensionTimer {
self.activeExplicitExtensionTimer?.invalidate()
self.activeExplicitExtensionTimer = nil
if let activeExplicitExtensionTask = self.activeExplicitExtensionTask {
self.activeExplicitExtensionTask = nil
self.endBackgroundTask(activeExplicitExtensionTask)
}
self.checkTasks()
}
}
}
}
if shouldCheckTasks || extendNow {
@ -313,12 +338,12 @@ public final class SharedWakeupManager {
hasTasksForBackgroundExtension = true
}
let canBeginBackgroundExtensionTasks = self.allowBackgroundTimeExtensionDeadline.flatMap({ CACurrentMediaTime() < $0 }) ?? false
let canBeginBackgroundExtensionTasks = self.allowBackgroundTimeExtensionDeadline.flatMap({ CFAbsoluteTimeGetCurrent() < $0 }) ?? false
if hasTasksForBackgroundExtension {
if canBeginBackgroundExtensionTasks {
var endTaskId: UIBackgroundTaskIdentifier?
let currentTime = CACurrentMediaTime()
let currentTime = CFAbsoluteTimeGetCurrent()
if let (taskId, startTime, timer) = self.currentTask {
if startTime < currentTime + 1.0 {
self.currentTask = nil
@ -328,16 +353,28 @@ public final class SharedWakeupManager {
}
if self.currentTask == nil {
let handleExpiration:() -> Void = { [weak self] in
var actualTaskId: UIBackgroundTaskIdentifier?
let handleExpiration: () -> Void = { [weak self] in
guard let strongSelf = self else {
return
}
if let actualTaskId {
strongSelf.endBackgroundTask(actualTaskId)
if let (taskId, _, timer) = strongSelf.currentTask, taskId == actualTaskId {
timer.invalidate()
strongSelf.currentTask = nil
}
}
strongSelf.isInBackgroundExtension = false
strongSelf.checkTasks()
}
if let taskId = self.beginBackgroundTask("background-wakeup", {
handleExpiration()
}) {
actualTaskId = taskId
let timer = SwiftSignalKit.Timer(timeout: min(30.0, max(0.0, self.backgroundTimeRemaining() - 5.0)), repeat: false, completion: {
handleExpiration()
}, queue: Queue.mainQueue())