Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MultiDeviceManager.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import Foundation
import Keychain
import PromiseKit
import ThreemaEssentials

public protocol MultiDeviceManagerProtocol {
    /// Maximum number of linked devices allowed including this device. This is only set if we are logged into the
    /// mediator
    var maximumNumberOfDeviceSlots: Int? { get }
    
    var thisDevice: DeviceInfo { get }
    
    /// Get sorted list of other linked/paired devices
    /// - Returns: List of other linked/paired devices
    /// - Throws: `MultiDeviceManagerError.multiDeviceNotActivated`, `MessageReceiverError`
    func sortedOtherDevices() async throws -> [DeviceInfo]
    
    func otherDevices() -> Promise<[DeviceInfo]>
    
    /// Drop other linked/paired device.
    /// - Parameter device: Linked/paired device that will dropped
    func drop(device: DeviceInfo) async throws
    
    func drop(device: DeviceInfo) -> Promise<Void>
    func disableMultiDevice(runForwardSecurityRefreshSteps: Bool) async throws
    
    /// Disable multi-device if we can ensure that no other device is left in the group
    func disableMultiDeviceIfNeeded()
    
    /// Reset `enableMultiDevice` if needed Keychain items are missing
    func resetEnableMultiDeviceIfNeeded()
}

extension MultiDeviceManagerProtocol {
    public func disableMultiDevice(runForwardSecurityRefreshSteps: Bool = true) async throws {
        try await disableMultiDevice(runForwardSecurityRefreshSteps: runForwardSecurityRefreshSteps)
    }
}

public enum MultiDeviceManagerError: Error {
    case multiDeviceNotActivated
}

public final class MultiDeviceManager: MultiDeviceManagerProtocol {
    private let serverConnector: ServerConnectorProtocol
    private let contactStore: ContactStoreProtocol
    private let userSettings: UserSettingsProtocol
    private let taskManager: TaskManagerProtocol
    private let entityManager: EntityManager
    private let keychainManager: any KeychainManagerProtocol
    
    public var maximumNumberOfDeviceSlots: Int? {
        serverConnector.maximumNumberOfDeviceSlots?.intValue
    }
    
    required init(
        serverConnector: ServerConnectorProtocol,
        contactStore: ContactStoreProtocol,
        userSettings: UserSettingsProtocol,
        taskManager: TaskManagerProtocol,
        entityManager: EntityManager,
        keychainManager: any KeychainManagerProtocol
    ) {
        self.serverConnector = serverConnector
        self.contactStore = contactStore
        self.userSettings = userSettings
        self.taskManager = taskManager
        self.entityManager = entityManager
        self.keychainManager = keychainManager
    }

    public convenience init() {
        let remoteSecretManager = RemoteSecretProvider.remoteSecretManager
        self.init(
            serverConnector: ServerConnector.shared(),
            contactStore: ContactStore.shared(),
            userSettings: UserSettings.shared(),
            taskManager: TaskManager(),
            entityManager: PersistenceManager(
                appGroupID: AppGroup.groupID(),
                userDefaults: AppGroup.userDefaults(),
                remoteSecretManager: remoteSecretManager
            ).entityManager,
            keychainManager: KeychainManager(remoteSecretManager: remoteSecretManager)
        )
    }

    /// Get device info from this device.
    /// - Returns: This device info
    public var thisDevice: DeviceInfo {
        DeviceInfo(
            deviceID: serverConnector.deviceID != nil ? serverConnector.deviceID!.paddedLittleEndian() : 0,
            label: "\(UIDevice().name)",
            lastLoginAt: Date(),
            badge: nil,
            platform: .ios,
            platformDetails: "\(ThreemaUtility.appAndBuildVersionPretty) • \(UIDevice.modelName) "
        )
    }

    public func sortedOtherDevices() async throws -> [DeviceInfo] {
        try await withCheckedThrowingContinuation { continuation in
            otherDevices()
                .done { devices in
                    // Sort devices by label, if label is the same sort by ID
                    // (so the ordering is consistent across refreshes)
                    let sortedDevices = devices.sorted {
                        if $0.label != $1.label {
                            $0.label.localizedCaseInsensitiveCompare($1.label) == .orderedAscending
                        }
                        else {
                            $0.deviceID < $1.deviceID
                        }
                    }
                    
                    continuation.resume(returning: sortedDevices)
                }
                .catch { error in
                    continuation.resume(throwing: error)
                }
        }
    }
    
    /// Get other linked/paired devices.
    /// - Returns: List of other linked/paired devices
    @available(*, deprecated, renamed: "sortedOtherDevices", message: "Only used for old code")
    public func otherDevices() -> Promise<[DeviceInfo]> {
        guard let deviceGroupKeys = serverConnector.deviceGroupKeys, let deviceID = serverConnector.deviceID else {
            return Promise { seal in seal.fulfill([DeviceInfo]()) }
        }

        let messageReceiver = MessageReceiver(
            serverConnector: serverConnector,
            userSettings: userSettings,
            mediatorMessageProtocol: MediatorMessageProtocol(deviceGroupKeys: deviceGroupKeys)
        )
        return messageReceiver.requestDevicesInfo(thisDeviceID: deviceID)
    }
    
    public func drop(device: DeviceInfo) async throws {
        try await withCheckedThrowingContinuation { continuation in
            drop(device: device)
                .done {
                    continuation.resume()
                }
                .catch { error in
                    continuation.resume(throwing: error)
                }
        }
    }

    /// Drop other linked/paired device.
    /// - Parameter device: Linked/paired device that will dropped
    @available(*, deprecated, message: "Only used for old code. Use async version otherwise")
    public func drop(device: DeviceInfo) -> Promise<Void> {
        guard let deviceGroupKeys = serverConnector.deviceGroupKeys else {
            return Promise()
        }

        let messageReceiver = MessageReceiver(
            serverConnector: serverConnector,
            userSettings: userSettings,
            mediatorMessageProtocol: MediatorMessageProtocol(deviceGroupKeys: deviceGroupKeys)
        )
        return messageReceiver.requestDropDevice(device: device)
    }
    
    private func drop(devices: [DeviceInfo]) -> Promise<Void> {
        let drops: [Promise<Void>] = devices.map { deviceInfo in
            self.drop(device: deviceInfo)
        }

        return when(fulfilled: drops)
    }
    
    /// Disable multi device and update feature mask
    /// - Parameter runForwardSecurityRefreshSteps: Run refresh steps for all solicitedContactIdentities
    public func disableMultiDevice(runForwardSecurityRefreshSteps: Bool = true) async throws {
        try await withCheckedThrowingContinuation { continuation in
            otherDevices()
                .then { otherDevices -> Promise<Void> in
                    DDLogNotice("Drop other devices")
                    return self.drop(devices: otherDevices)
                }
                .then { () -> Promise<Void> in
                    DDLogNotice("Drop this device")
                    return self.drop(device: self.thisDevice)
                }
                .then { () -> Promise<Void> in
                    DDLogNotice("Deactivate multi device")
                    self.serverConnector.deactivateMultiDevice()
                                                            
                    return Promise()
                }
                .done {
                    continuation.resume()
                }
                .catch { error in
                    DDLogWarn("Disable multi device failed: \(error)")
                    continuation.resume(throwing: error)
                }
        }
        
        // TODO: (SE-199) Remove everything below
        
        do {
            DDLogNotice("Update own feature mask")

            try await FeatureMask.updateLocal()
            
            DDLogNotice("Contact status update start")
            
            let updateTask: Task<Void, Error> = Task {
                try await contactStore.updateStatusForAllContacts(ignoreInterval: true)
            }
            
            // The request time out is 30s thus we wait for 40s for it to complete
            switch try await Task.timeout(updateTask, 40) {
            case .result:
                break
            case let .error(error):
                DDLogError("Contact status update error: \(error ?? "nil")")
            case .timeout:
                DDLogWarn("Contact status update time out")
            }
        }
        catch {
            // We should still try the next steps if this fails and don't report this error back to the caller
            DDLogWarn("Feature mask or contact status update error: \(error)")
        }
        
        DDLogNotice("Reset import status of all contacts")
        // Reset the `contactImportStatus`, as without the MD, we should update a contact from the linked iOS
        // contact.
        await contactStore.resetImportStatusForAllContacts(entityManager: entityManager)
        
        // Run refresh steps for all solicitedContactIdentities.
        // (See _Application Update Steps_ in the Threema Protocols for details.)
        guard runForwardSecurityRefreshSteps else {
            return
        }
        
        DDLogNotice("Fetch solicited contacts")
        let solicitedContactIdentities = await entityManager.perform {
            self.entityManager.entityFetcher.solicitedContactIdentities()
        }
        
        await ForwardSecurityRefreshSteps().run(for: solicitedContactIdentities.map {
            ThreemaIdentity($0)
        })
    }
    
    public func disableMultiDeviceIfNeeded() {
        guard userSettings.enableMultiDevice else {
            return
        }
        
        let task = TaskDefinitionDisableMultiDeviceIfNeeded()
        taskManager.add(taskDefinition: task)
    }
    
    public func resetEnableMultiDeviceIfNeeded() {
        guard userSettings.enableMultiDevice else {
            return
        }
        
        do {
            if try keychainManager.loadMultiDeviceID() == nil {
                DDLogInfo("Resetting enableMultiDevice because keychain item is missing.")
                userSettings.enableMultiDevice = false
            }
        }
        catch {
            DDLogError("Fetching multi-device ID to reset failed: \(error)")
        }
    }
}

Messung
CHZG
849710093

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104842
#Domains=499168