mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Two sync loop experimental solution to decrypt push notifications (#1082)
* Experimental two sync loop solution * better logging * improved the code and handled invite notifications display better * improved invite notifications * new exerimental branch * new sync loop * code updated * code improvements * code improvements * fix typo * code improvements * removed some unused code and added a respawn * fixing some NSE issues * code improvements * new version of the branch * more logging * running the nse process ONLY IF necessary * finally works! made also the feature flag * also the encryption value of the room list api will depend on the flag now * changelog * code improvements * code improvement * updated proj * fixing some compilation error after the rebase * opt-in for the encryption sync * fix
This commit is contained in:
@@ -24,6 +24,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||
private var handler: ((UNNotificationContent) -> Void)?
|
||||
private var modifiedContent: UNMutableNotificationContent?
|
||||
private var userSession: NSEUserSession?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest,
|
||||
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
@@ -71,9 +72,26 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
|
||||
do {
|
||||
let userSession = try NSEUserSession(credentials: credentials)
|
||||
|
||||
guard let itemProxy = try await userSession.notificationItemProxy(roomID: roomId, eventID: eventId) else {
|
||||
MXLog.info("\(tag) no notification for this event")
|
||||
self.userSession = userSession
|
||||
var itemProxy = await userSession.notificationItemProxy(roomID: roomId, eventID: eventId)
|
||||
if settings.isEncryptionSyncEnabled,
|
||||
itemProxy?.isEncrypted == true,
|
||||
let _ = try? userSession.startEncryptionSync() {
|
||||
// TODO: The following wait with a timeout should be handled by the SDK
|
||||
// We try to decrypt the notification for 10 seconds at most
|
||||
let date = Date()
|
||||
repeat {
|
||||
// if the sync terminated we try one last time then we break from the loop
|
||||
guard userSession.isSyncing else {
|
||||
itemProxy = await userSession.notificationItemProxy(roomID: roomId, eventID: eventId)
|
||||
break
|
||||
}
|
||||
itemProxy = await userSession.notificationItemProxy(roomID: roomId, eventID: eventId)
|
||||
} while itemProxy?.isEncrypted == true && date.timeIntervalSinceNow > -10
|
||||
}
|
||||
|
||||
guard let itemProxy else {
|
||||
MXLog.info("\(tag) no notification for the event, discard")
|
||||
return discard()
|
||||
}
|
||||
|
||||
@@ -110,31 +128,29 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
return discard()
|
||||
}
|
||||
|
||||
guard let identifier = modifiedContent.notificationID,
|
||||
!settings.servedNotificationIdentifiers.contains(identifier) else {
|
||||
MXLog.info("\(tag) notify: notification already served")
|
||||
return discard()
|
||||
}
|
||||
|
||||
settings.servedNotificationIdentifiers.insert(identifier)
|
||||
handler?(modifiedContent)
|
||||
handler = nil
|
||||
self.modifiedContent = nil
|
||||
cleanUp()
|
||||
}
|
||||
|
||||
private func discard() {
|
||||
MXLog.info("\(tag) discard")
|
||||
|
||||
handler?(UNMutableNotificationContent())
|
||||
handler = nil
|
||||
modifiedContent = nil
|
||||
cleanUp()
|
||||
}
|
||||
|
||||
private var tag: String {
|
||||
"[NSE][\(Unmanaged.passUnretained(self).toOpaque())][\(Unmanaged.passUnretained(Thread.current).toOpaque())]"
|
||||
}
|
||||
|
||||
private func cleanUp() {
|
||||
handler = nil
|
||||
modifiedContent = nil
|
||||
userSession?.stopEncryptionSync()
|
||||
}
|
||||
|
||||
deinit {
|
||||
cleanUp()
|
||||
NSELogger.logMemory(with: tag)
|
||||
MXLog.info("\(tag) deinit")
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ final class NSESettings {
|
||||
/// UserDefaults to be used on reads and writes.
|
||||
private static var store: UserDefaults! = UserDefaults(suiteName: suiteName)
|
||||
|
||||
/// A set of all the notification identifiers that have been served so far, it's reset every time the app is launched
|
||||
@UserPreference(key: SharedUserDefaultsKeys.servedNotificationIdentifiers, defaultValue: [], storageType: .userDefaults(store))
|
||||
var servedNotificationIdentifiers: Set<String>
|
||||
/// Tag describing if the app and the NSE should use the encryption sync
|
||||
@UserPreference(key: SharedUserDefaultsKeys.isEncryptionSyncEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var isEncryptionSyncEnabled
|
||||
}
|
||||
|
||||
@@ -19,11 +19,18 @@ import MatrixRustSDK
|
||||
|
||||
final class NSEUserSession {
|
||||
private let client: ClientProtocol
|
||||
private let userID: String
|
||||
private var encryptionSyncService: EncryptionSync?
|
||||
private(set) lazy var mediaProvider: MediaProviderProtocol = MediaProvider(mediaLoader: MediaLoader(client: client),
|
||||
imageCache: .onlyOnDisk,
|
||||
backgroundTaskService: nil)
|
||||
|
||||
var isSyncing: Bool {
|
||||
encryptionSyncService != nil
|
||||
}
|
||||
|
||||
init(credentials: KeychainCredentials) throws {
|
||||
userID = credentials.userID
|
||||
let builder = ClientBuilder()
|
||||
.basePath(path: URL.sessionsBaseDirectory.path)
|
||||
.username(username: credentials.userID)
|
||||
@@ -32,18 +39,35 @@ final class NSEUserSession {
|
||||
try client.restoreSession(session: credentials.restorationToken.session)
|
||||
}
|
||||
|
||||
func notificationItemProxy(roomID: String, eventID: String) async throws -> NotificationItemProxyProtocol? {
|
||||
let userID = try client.userId()
|
||||
return await Task.dispatch(on: .global()) {
|
||||
func startEncryptionSync() throws {
|
||||
let listener = EncryptionSyncListenerProxy { [weak self] reason in
|
||||
MXLog.info("NSE: Encryption sync terminated for user: \(self?.userID ?? "unknown") with reason: \(reason)")
|
||||
self?.encryptionSyncService = nil
|
||||
}
|
||||
encryptionSyncService = try client.notificationEncryptionSync(id: "NSE", listener: listener, numIters: 2)
|
||||
MXLog.info("NSE: Encryption sync started for user: \(userID)")
|
||||
}
|
||||
|
||||
func notificationItemProxy(roomID: String, eventID: String) async -> NotificationItemProxyProtocol? {
|
||||
await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
guard let notification = try self.client.getNotificationItem(roomId: roomID, eventId: eventID) else {
|
||||
return nil
|
||||
}
|
||||
return NotificationItemProxy(notificationItem: notification, receiverID: userID)
|
||||
return NotificationItemProxy(notificationItem: notification, receiverID: self.userID)
|
||||
} catch {
|
||||
MXLog.error("NSE: Could not get notification's content creating an empty notification instead, error: \(error)")
|
||||
return EmptyNotificationItemProxy(eventID: eventID, roomID: roomID, receiverID: userID)
|
||||
return EmptyNotificationItemProxy(eventID: eventID, roomID: roomID, receiverID: self.userID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopEncryptionSync() {
|
||||
encryptionSyncService?.stop()
|
||||
}
|
||||
|
||||
deinit {
|
||||
MXLog.info("NSE: NSEUserSession deinit called for user: \(userID)")
|
||||
stopEncryptionSync()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user