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:
Mauro
2023-06-22 19:23:33 +02:00
committed by GitHub
parent 652fd3cf76
commit afc4332716
18 changed files with 184 additions and 60 deletions
+30 -14
View File
@@ -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")
}
+3 -3
View File
@@ -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
}
+29 -5
View File
@@ -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()
}
}