Skip to content

Commit

Permalink
Two sync loop experimental solution to decrypt push notifications (#1082
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
Velin92 authored Jun 22, 2023
1 parent 652fd3c commit afc4332
Show file tree
Hide file tree
Showing 18 changed files with 184 additions and 60 deletions.
6 changes: 6 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
6860721DB3091BE08164C132 /* MapAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B48B7AD4908C5C374517B892 /* MapAssets.xcassets */; };
68AC3C84E2B438036B174E30 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */; };
695825D20A761C678809345D /* MessageForwardingScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52135BD9E0E7A091688F627A /* MessageForwardingScreenModels.swift */; };
69ABFBAF05D7EF11E7C88CEA /* EncryptionSyncListenerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68356CB936A8814A3FEA66A8 /* EncryptionSyncListenerProxy.swift */; };
69BCBB4FB2DC3D61A28D3FD8 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */; };
69C7B956B74BEC3DB88224EA /* NavigationSplitCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */; };
6A0E7551E0D1793245F34CDD /* ClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09A267106B9585D3D0CFC0D /* ClientError.swift */; };
Expand Down Expand Up @@ -672,6 +673,7 @@
F7BC744FFA7FE248FAE7F570 /* UserIndicatorToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F57C8022B8A871A1DCD1750A /* UserIndicatorToastView.swift */; };
F86102DC2C68BBBB0521BAAE /* SoftLogoutScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */; };
F8E725D42023ECA091349245 /* AudioRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EAAF82432B0B53881CF826 /* AudioRoomTimelineItem.swift */; };
F91B4629E4AF51A4FE8E7608 /* EncryptionSyncListenerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68356CB936A8814A3FEA66A8 /* EncryptionSyncListenerProxy.swift */; };
F94000E3D91B11C527DA8807 /* UserProfileCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 923485F85E1D765EF9D20E88 /* UserProfileCell.swift */; };
F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72EFC8C634469F9262659C7 /* NSItemProvider.swift */; };
F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; };
Expand Down Expand Up @@ -992,6 +994,7 @@
6654859746B0BE9611459391 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = cs; path = cs.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadingPreprocessor.swift; sourceTree = "<group>"; };
66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = "<group>"; };
68356CB936A8814A3FEA66A8 /* EncryptionSyncListenerProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSyncListenerProxy.swift; sourceTree = "<group>"; };
6861FE915C7B5466E6962BBA /* StartChatScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreen.swift; sourceTree = "<group>"; };
686BCFA37AC6C67FF973CE67 /* OnboardingBackgroundImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundImage.swift; sourceTree = "<group>"; };
69219A908D7C22E6EE6689AE /* UserNotificationCenterSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationCenterSpy.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2519,6 +2522,7 @@
832FC81F760220239E285294 /* Proxy */ = {
isa = PBXGroup;
children = (
68356CB936A8814A3FEA66A8 /* EncryptionSyncListenerProxy.swift */,
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */,
);
path = Proxy;
Expand Down Expand Up @@ -3766,6 +3770,7 @@
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */,
DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */,
24A75F72EEB7561B82D726FD /* Date.swift in Sources */,
F91B4629E4AF51A4FE8E7608 /* EncryptionSyncListenerProxy.swift in Sources */,
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */,
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */,
A3E390675E9730C176B59E1B /* ImageProviderProtocol.swift in Sources */,
Expand Down Expand Up @@ -4006,6 +4011,7 @@
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */,
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */,
B5903E48CF43259836BF2DBF /* EncryptedRoomTimelineView.swift in Sources */,
69ABFBAF05D7EF11E7C88CEA /* EncryptionSyncListenerProxy.swift in Sources */,
F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */,
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{
"identity" : "compound-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/compound-ios.git",
"location" : "https://github.com/vector-im/compound-ios",
"state" : {
"revision" : "d1a28b8a311e33ddb517d10391037f1547a3c7b6"
}
Expand Down
10 changes: 10 additions & 0 deletions ElementX/Sources/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
selector: #selector(applicationDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)

NotificationCenter.default.addObserver(self,
selector: #selector(applicationWillTerminate),
name: UIApplication.willTerminateNotification,
object: nil)
}

@objc
private func applicationWillTerminate() {
userSession?.clientProxy.stopSync()
}

@objc
Expand Down
6 changes: 3 additions & 3 deletions ElementX/Sources/Application/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.pusherProfileTag, storageType: .userDefaults(store))
var pusherProfileTag: String?

/// 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, initialValue: [], 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

// MARK: - Other

Expand Down
13 changes: 0 additions & 13 deletions ElementX/Sources/Other/Extensions/UNNotificationContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ extension UNNotificationContent {
userInfo[NotificationConstants.UserInfoKey.receiverIdentifier] as? String
}

@objc var notificationID: String? {
userInfo[NotificationConstants.UserInfoKey.notificationIdentifier] as? String
}

@objc var roomID: String? {
userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String
}
Expand Down Expand Up @@ -74,15 +70,6 @@ extension UNMutableNotificationContent {
}
}

override var notificationID: String? {
get {
userInfo[NotificationConstants.UserInfoKey.notificationIdentifier] as? String
}
set {
userInfo[NotificationConstants.UserInfoKey.notificationIdentifier] = newValue
}
}

func addMediaAttachment(using mediaProvider: MediaProviderProtocol?,
mediaSource: MediaSourceProxy) async -> UNMutableNotificationContent {
guard let mediaProvider else {
Expand Down
2 changes: 1 addition & 1 deletion ElementX/Sources/Other/SharedUserDefaultsKeys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
//

enum SharedUserDefaultsKeys: String {
case servedNotificationIdentifiers
case isEncryptionSyncEnabled
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ struct DeveloperOptionsScreenViewStateBindings {
var shouldCollapseRoomStateEvents: Bool
var userSuggestionsEnabled: Bool
var readReceiptsEnabled: Bool
var isEncryptionSyncEnabled: Bool
}

enum DeveloperOptionsScreenViewAction {
case changedShouldCollapseRoomStateEvents
case changedUserSuggestionsEnabled
case changedReadReceiptsEnabled
case changedIsEncryptionSyncEnabled
case clearCache
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
appSettings = ServiceLocator.shared.settings
let bindings = DeveloperOptionsScreenViewStateBindings(shouldCollapseRoomStateEvents: appSettings.shouldCollapseRoomStateEvents,
userSuggestionsEnabled: appSettings.userSuggestionsEnabled,
readReceiptsEnabled: appSettings.readReceiptsEnabled)
readReceiptsEnabled: appSettings.readReceiptsEnabled,
isEncryptionSyncEnabled: appSettings.isEncryptionSyncEnabled)
let state = DeveloperOptionsScreenViewState(bindings: bindings)

super.init(initialViewState: state)
Expand All @@ -45,6 +46,8 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
appSettings.userSuggestionsEnabled = state.bindings.userSuggestionsEnabled
case .changedReadReceiptsEnabled:
appSettings.readReceiptsEnabled = state.bindings.readReceiptsEnabled
case .changedIsEncryptionSyncEnabled:
appSettings.isEncryptionSyncEnabled = state.bindings.isEncryptionSyncEnabled
case .clearCache:
callback?(.clearCache)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ struct DeveloperOptionsScreen: View {
.onChange(of: context.readReceiptsEnabled) { _ in
context.send(viewAction: .changedReadReceiptsEnabled)
}

Toggle(isOn: $context.isEncryptionSyncEnabled) {
Text("Use notification encryption sync")
Text("requires app reboot")
}
.onChange(of: context.isEncryptionSyncEnabled) { _ in
context.send(viewAction: .changedIsEncryptionSyncEnabled)
}
}

Section {
Expand Down
58 changes: 53 additions & 5 deletions ElementX/Sources/Services/Client/ClientProxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ class ClientProxy: ClientProxyProtocol {

private var roomListService: RoomListService?
private var roomListStateUpdateTaskHandle: TaskHandle?


private var encryptionSyncService: EncryptionSync?
private var isEncryptionSyncing = false

var roomSummaryProvider: RoomSummaryProviderProtocol?
var inviteSummaryProvider: RoomSummaryProviderProtocol?

Expand Down Expand Up @@ -103,26 +106,38 @@ class ClientProxy: ClientProxyProtocol {
}

var isSyncing: Bool {
roomListService?.isSyncing() ?? false
roomListService?.isSyncing() ?? false && isEncryptionSyncing
}

func startSync() {
MXLog.info("Starting sync")
guard !isSyncing else {
return
}


startEncryptionSyncService()
roomListService?.sync()
}

func stopSync() {
MXLog.info("Stopping sync")
stopEncryptionSyncService()

do {
try roomListService?.stopSync()
} catch {
MXLog.error("Failed stopping room list service with error: \(error)")
}
}

private func stopEncryptionSyncService() {
guard isEncryptionSyncing else {
return
}
isEncryptionSyncing = false
encryptionSyncService?.stop()
MXLog.info("Stopping Encryption Sync service")
}

func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
await Task.dispatch(on: clientQueue) {
Expand Down Expand Up @@ -359,14 +374,47 @@ class ClientProxy: ClientProxyProtocol {
self.avatarURLSubject.value = urlString.flatMap(URL.init)
}
}


private func startEncryptionSyncService() {
guard ServiceLocator.shared.settings.isEncryptionSyncEnabled else {
return
}
configureEncryptionSyncService()
}

private func configureEncryptionSyncService() {
do {
let listener = EncryptionSyncListenerProxy { [weak self] reason in
switch reason {
case .done:
MXLog.info("Encryption Sync has finished for user: \(self?.userID ?? "unknown")")
case .error(let msg):
MXLog.error("Encryption Sync has terminated for user: \(self?.userID ?? "unknown") for reason: \(msg)")
guard let self else {
return
}
Task {
self.configureEncryptionSyncService()
}
}
}
let encryptionSync = try client.mainEncryptionSync(id: "Main App", listener: listener)
encryptionSync.reloadCaches()
isEncryptionSyncing = true
encryptionSyncService = encryptionSync
MXLog.info("Encryption sync started for user: \(userID)")
} catch {
MXLog.error("Configure encryption sync failed with error: \(error)")
}
}

private func configureRoomListService() async {
guard roomListService == nil else {
fatalError("This shouldn't be called more than once")
}

do {
let roomListService = try client.roomListServiceWithEncryption()
let roomListService = try ServiceLocator.shared.settings.isEncryptionSyncEnabled ? client.roomListService() : client.roomListServiceWithEncryption()
roomListStateUpdateTaskHandle = roomListService.state(listener: RoomListStateListenerProxy { [weak self] state in
guard let self else { return }
MXLog.info("Received room list update: \(state)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

import MatrixRustSDK

final class EncryptionSyncListenerProxy: EncryptionSyncListener {
private let didTerminateClosure: (EncryptionSyncTerminationReason) -> Void

init(_ didTerminateClosure: @escaping (EncryptionSyncTerminationReason) -> Void) {
self.didTerminateClosure = didTerminateClosure
}

func didTerminate(reason: EncryptionSyncTerminationReason) {
didTerminateClosure(reason)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,6 @@ protocol NotificationItemProxyProtocol {
var isEncrypted: Bool { get }
}

extension NotificationItemProxyProtocol {
var id: String? {
let identifiers = receiverID + roomID + event.eventID
guard let data = identifiers.data(using: .utf8) else {
return nil
}
let digest = SHA256.hash(data: data)
return digest.compactMap { String(format: "%02x", $0) }.joined()
}
}

struct NotificationItemProxy: NotificationItemProxyProtocol {
let notificationItem: NotificationItem
let receiverID: String
Expand Down Expand Up @@ -168,7 +157,6 @@ extension NotificationItemProxyProtocol {
notification.receiverID = receiverID
notification.roomID = roomID
notification.eventID = event.eventID
notification.notificationID = id
notification.sound = isNoisy ? UNNotificationSound(named: UNNotificationSoundName(rawValue: "message.caf")) : nil
// So that the UI groups notification that are received for the same room but also for the same user
notification.threadIdentifier = "\(receiverID)\(roomID)"
Expand Down
Loading

0 comments on commit afc4332

Please sign in to comment.