BackgroundTaskManager.swift 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2018-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. @objc class BackgroundTaskManager: NSObject {
  22. @objc static let shared = BackgroundTaskManager()
  23. private var backgroundTasks: [String: UIBackgroundTaskIdentifier]
  24. private var backgroundTaskIdentifierCounter = AtomicInteger()
  25. private var backgroundTaskQueue:DispatchQueue
  26. private override init() {
  27. backgroundTasks = [String: UIBackgroundTaskIdentifier]()
  28. backgroundTaskQueue = DispatchQueue(label: "ch.threema.backgroundTaskQueue", attributes: [])
  29. }
  30. // MARK: Public functions
  31. // Create a new background task. The completion handler is called on the main thread.
  32. @objc func newBackgroundTask(key: String, timeout: Int, completionHandler: (() -> Void)?) {
  33. backgroundTaskQueue.async {
  34. // remove already existing background task first
  35. if let foundBackgroundTask = self.backgroundTasks[key] {
  36. self.cancelBackgroundTaskWithoutDisconnect(identifier: foundBackgroundTask, key: key)
  37. }
  38. var bgTask: UIBackgroundTaskIdentifier? = nil
  39. bgTask = UIApplication.shared.beginBackgroundTask(withName: key, expirationHandler: {
  40. self.backgroundTaskQueue.async {
  41. let identifier: UIBackgroundTaskIdentifier? = self.backgroundTasks[key]
  42. if identifier != nil {
  43. self.backgroundTasks.removeValue(forKey: key)
  44. }
  45. self.disconnectFromServer(completionHandler: {
  46. PendingMessagesManager.shared.save()
  47. if identifier != nil {
  48. UIApplication.shared.endBackgroundTask(identifier!)
  49. }
  50. })
  51. }
  52. })
  53. self.backgroundTasks.updateValue(bgTask!, forKey: key)
  54. self.backgroundTaskQueue.asyncAfter(deadline: .now() + Double(timeout)) {
  55. let identifier: UIBackgroundTaskIdentifier? = self.backgroundTasks[key]
  56. if identifier != nil {
  57. self.backgroundTasks.removeValue(forKey: key)
  58. self.disconnectFromServer(completionHandler: {
  59. PendingMessagesManager.shared.save()
  60. if identifier != nil {
  61. UIApplication.shared.endBackgroundTask(identifier!)
  62. }
  63. })
  64. }
  65. }
  66. if completionHandler != nil {
  67. DispatchQueue.main.async {
  68. completionHandler!();
  69. }
  70. }
  71. }
  72. }
  73. @objc func cancelBackgroundTask(key: String) {
  74. backgroundTaskQueue.async {
  75. if let identifier: UIBackgroundTaskIdentifier = self.backgroundTasks[key] {
  76. self.backgroundTasks.removeValue(forKey: key)
  77. self.disconnectFromServer(completionHandler: {
  78. UIApplication.shared.endBackgroundTask(identifier)
  79. })
  80. } else {
  81. self.disconnectFromServer(completionHandler: nil)
  82. }
  83. }
  84. }
  85. @objc func counter(identifier: String) -> String {
  86. return identifier + String(backgroundTaskIdentifierCounter.incrementAndGet())
  87. }
  88. private func cancelBackgroundTaskWithoutDisconnect(identifier: UIBackgroundTaskIdentifier, key: String) {
  89. self.backgroundTasks.removeValue(forKey: key)
  90. UIApplication.shared.endBackgroundTask(identifier)
  91. }
  92. private func disconnectFromServer(completionHandler: (() -> Void)?) {
  93. if self.backgroundTasks.count != 0 || VoIPCallStateManager.shared.currentCallState() != .idle {
  94. if completionHandler != nil {
  95. completionHandler!()
  96. }
  97. return
  98. }
  99. DispatchQueue.main.async {
  100. if AppDelegate.shared().isAppInBackground() {
  101. ValidationLogger.shared()?.logString("Background Task: There is no running background task")
  102. if WCSessionManager.shared.isRunningWCSession() == true {
  103. ValidationLogger.shared()?.logString("Threema Web: Disconnect webclient disconnectFromServer")
  104. WCSessionManager.shared.pauseAllRunningSessions()
  105. }
  106. ServerConnector.shared().disconnectWait()
  107. MessageQueue.shared().save()
  108. PendingMessagesManager.shared.save()
  109. }
  110. if completionHandler != nil {
  111. self.backgroundTaskQueue.async {
  112. completionHandler!()
  113. return
  114. }
  115. }
  116. }
  117. }
  118. }
  119. public final class AtomicInteger {
  120. private let lock = DispatchSemaphore(value: 1)
  121. private var _value: Int
  122. public init(value initialValue: Int = 0) {
  123. _value = initialValue
  124. }
  125. public var value: Int {
  126. get {
  127. lock.wait()
  128. defer { lock.signal() }
  129. return _value
  130. }
  131. set {
  132. lock.wait()
  133. defer { lock.signal() }
  134. _value = newValue
  135. }
  136. }
  137. public func decrementAndGet() -> Int {
  138. lock.wait()
  139. defer { lock.signal() }
  140. _value -= 1
  141. return _value
  142. }
  143. public func incrementAndGet() -> Int {
  144. lock.wait()
  145. defer { lock.signal() }
  146. _value += 1
  147. return _value
  148. }
  149. }