Wait during registration for storage service restore and re-populate local profile if available
This commit is contained in:
parent
2fde911cab
commit
724b483d00
@ -174,8 +174,19 @@ public class AccountManager: NSObject {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}.done { (_) -> Void in
|
||||
}.done {
|
||||
self.completeRegistration()
|
||||
}.then { _ -> Promise<Void> in
|
||||
BenchEventStart(title: "waiting for initial storage service restore", eventId: "initial-storage-service-restore")
|
||||
return StorageServiceManager.shared.restoreOrCreateManifestIfNecessary().asVoid().ensure {
|
||||
BenchEventComplete(eventId: "initial-storage-service-restore")
|
||||
}.timeout(seconds: 60)
|
||||
}.then { _ -> Promise<Void> in
|
||||
// In the case that we restored our profile from a previous registration,
|
||||
// re-upload it so that the user does not need to refill in all the details.
|
||||
// Right now the avatar will always be lost since we do not store avatars in
|
||||
// the storage service.
|
||||
self.profileManager.reuploadLocalProfilePromise()
|
||||
}
|
||||
|
||||
registrationPromise.retainUntilComplete()
|
||||
@ -236,15 +247,32 @@ public class AccountManager: NSObject {
|
||||
}
|
||||
}.then {
|
||||
self.deviceService.updateCapabilities()
|
||||
}.then { _ -> Promise<Void> in
|
||||
}.done {
|
||||
self.completeRegistration()
|
||||
}.then { _ -> Promise<Void> in
|
||||
BenchEventStart(title: "waiting for initial storage service restore", eventId: "initial-storage-service-restore")
|
||||
|
||||
self.databaseStorage.asyncWrite { transaction in
|
||||
OWSSyncManager.shared().sendKeysSyncRequestMessage(transaction: transaction)
|
||||
}
|
||||
|
||||
let storageServiceRestorePromise = firstly {
|
||||
NotificationCenter.default.observe(once: .OWSSyncManagerKeysSyncDidComplete).asVoid()
|
||||
}.then {
|
||||
StorageServiceManager.shared.restoreOrCreateManifestIfNecessary().asVoid()
|
||||
}.ensure {
|
||||
BenchEventComplete(eventId: "initial-storage-service-restore")
|
||||
}.timeout(seconds: 60)
|
||||
|
||||
// we wait a bit for the initial syncs to come in before proceeding to the inbox
|
||||
// because we want to present the inbox already populated with groups and contacts,
|
||||
// rather than have the trickle in moments later.
|
||||
// TODO: Eventually, we can rely entirely on the storage service and will no longer
|
||||
// need to do any initial sync beyond the "keys" sync. For now, we try and do both
|
||||
// operations in parallel.
|
||||
BenchEventStart(title: "waiting for initial contact and group sync", eventId: "initial-contact-sync")
|
||||
|
||||
return firstly {
|
||||
let initialSyncMessagePromise = firstly {
|
||||
OWSSyncManager.shared().sendInitialSyncRequestsAwaitingCreatedThreadOrdering(timeoutSeconds: 60)
|
||||
}.done(on: .global() ) { orderedThreadIds in
|
||||
Logger.debug("orderedThreadIds: \(orderedThreadIds)")
|
||||
@ -265,6 +293,8 @@ public class AccountManager: NSObject {
|
||||
}.ensure {
|
||||
BenchEventComplete(eventId: "initial-contact-sync")
|
||||
}
|
||||
|
||||
return when(fulfilled: [storageServiceRestorePromise, initialSyncMessagePromise])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -483,6 +483,12 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat
|
||||
case ProfileViewMode_Registration:
|
||||
self.navigationItem.hidesBackButton = YES;
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
|
||||
// During registration, if you have a name pre-populatd we want
|
||||
// to enable the save button even if you haven't edited anything.
|
||||
if (self.givenNameTextField.text.length > 0) {
|
||||
forceSaveButtonEnabled = YES;
|
||||
}
|
||||
break;
|
||||
case ProfileViewMode_ExperienceUpgrade:
|
||||
self.navigationItem.rightBarButtonItem = nil;
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
|
||||
@objc(OWSStorageServiceManager)
|
||||
class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
public class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
|
||||
@objc
|
||||
static let shared = StorageServiceManager()
|
||||
public static let shared = StorageServiceManager()
|
||||
|
||||
var tsAccountManager: TSAccountManager {
|
||||
return SSKEnvironment.shared.tsAccountManager
|
||||
@ -18,35 +19,24 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
super.init()
|
||||
|
||||
AppReadiness.runNowOrWhenAppDidBecomeReady {
|
||||
self.registrationStateDidChange()
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(self.registrationStateDidChange),
|
||||
name: .RegistrationStateDidChange,
|
||||
object: nil
|
||||
)
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(self.willResignActive),
|
||||
name: .OWSApplicationWillResignActive,
|
||||
object: nil
|
||||
)
|
||||
|
||||
guard self.tsAccountManager.isRegisteredAndReady else { return }
|
||||
|
||||
// Schedule a restore. This will do nothing unless we've never
|
||||
// registered a manifest before.
|
||||
self.restoreOrCreateManifestIfNecessary()
|
||||
|
||||
// If we have any pending changes since we last launch, back them up now.
|
||||
self.backupPendingChanges()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func registrationStateDidChange() {
|
||||
guard self.tsAccountManager.isRegisteredAndReady else { return }
|
||||
|
||||
// Schedule a restore. This will do nothing unless we've never
|
||||
// registered a manifest before.
|
||||
restoreOrCreateManifestIfNecessary()
|
||||
|
||||
// If we have any pending changes since we last launch, back them up now.
|
||||
backupPendingChanges()
|
||||
}
|
||||
|
||||
@objc private func willResignActive() {
|
||||
// If we have any pending changes, start a back up immediately
|
||||
// to try and make sure the service doesn't get stale. If for
|
||||
@ -58,7 +48,7 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
// MARK: -
|
||||
|
||||
@objc
|
||||
func recordPendingDeletions(deletedIds: [AccountId]) {
|
||||
public func recordPendingDeletions(deletedIds: [AccountId]) {
|
||||
let operation = StorageServiceOperation.recordPendingDeletions(deletedIds)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
|
||||
@ -68,7 +58,7 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
}
|
||||
|
||||
@objc
|
||||
func recordPendingDeletions(deletedAddresses: [SignalServiceAddress]) {
|
||||
public func recordPendingDeletions(deletedAddresses: [SignalServiceAddress]) {
|
||||
let operation = StorageServiceOperation.recordPendingDeletions(deletedAddresses)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
|
||||
@ -78,7 +68,7 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
}
|
||||
|
||||
@objc
|
||||
func recordPendingUpdates(updatedIds: [AccountId]) {
|
||||
public func recordPendingUpdates(updatedIds: [AccountId]) {
|
||||
let operation = StorageServiceOperation.recordPendingUpdates(updatedIds)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
|
||||
@ -88,7 +78,7 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
}
|
||||
|
||||
@objc
|
||||
func recordPendingUpdates(updatedAddresses: [SignalServiceAddress]) {
|
||||
public func recordPendingUpdates(updatedAddresses: [SignalServiceAddress]) {
|
||||
let operation = StorageServiceOperation.recordPendingUpdates(updatedAddresses)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
|
||||
@ -98,15 +88,17 @@ class StorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
}
|
||||
|
||||
@objc
|
||||
func backupPendingChanges() {
|
||||
public func backupPendingChanges() {
|
||||
let operation = StorageServiceOperation(mode: .backup)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
}
|
||||
|
||||
@objc
|
||||
func restoreOrCreateManifestIfNecessary() {
|
||||
@discardableResult
|
||||
public func restoreOrCreateManifestIfNecessary() -> AnyPromise {
|
||||
let operation = StorageServiceOperation(mode: .restoreOrCreate)
|
||||
StorageServiceOperation.operationQueue.addOperation(operation)
|
||||
return AnyPromise(operation.promise)
|
||||
}
|
||||
|
||||
// MARK: - Backup Scheduling
|
||||
@ -183,14 +175,28 @@ class StorageServiceOperation: OWSOperation {
|
||||
}
|
||||
private let mode: Mode
|
||||
|
||||
let promise: Promise<Void>
|
||||
private let resolver: Resolver<Void>
|
||||
|
||||
fileprivate init(mode: Mode) {
|
||||
self.mode = mode
|
||||
(self.promise, self.resolver) = Promise<Void>.pending()
|
||||
super.init()
|
||||
self.remainingRetries = 4
|
||||
}
|
||||
|
||||
// MARK: - Run
|
||||
|
||||
override func didSucceed() {
|
||||
super.didSucceed()
|
||||
resolver.fulfill(())
|
||||
}
|
||||
|
||||
override func didFail(error: Error) {
|
||||
super.didFail(error: error)
|
||||
resolver.reject(error)
|
||||
}
|
||||
|
||||
// Called every retry, this is where the bulk of the operation's work should go.
|
||||
override public func run() {
|
||||
Logger.info("\(mode)")
|
||||
|
||||
@ -148,6 +148,12 @@ extension StorageServiceProtoContactRecord {
|
||||
mergeState = .needsUpdate(recipient.accountId)
|
||||
}
|
||||
|
||||
// The only thing we currently want to preserve for the local user is profile
|
||||
// information. Everything else doesn't make sense to store / update and can
|
||||
// lead to us being in a weird state such as thinking our own safety number changed.
|
||||
// If more data needs to be restored for the local user it should be done above this line.
|
||||
guard !address.isLocalAddress else { return mergeState }
|
||||
|
||||
// If our local identity differs from the service, use the service's value.
|
||||
if let identityKey = identity?.key, let identityState = identity?.state?.verificationState,
|
||||
localIdentityKey != identityKey || localIdentityState != identityState {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
@ -7,6 +7,7 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const OWSSyncManagerConfigurationSyncDidCompleteNotification;
|
||||
extern NSString *const OWSSyncManagerKeysSyncDidCompleteNotification;
|
||||
|
||||
@class AnyPromise;
|
||||
@class OWSContactsManager;
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NSString *const OWSSyncManagerConfigurationSyncDidCompleteNotification = @"OWSSyncManagerConfigurationSyncDidCompleteNotification";
|
||||
NSString *const OWSSyncManagerKeysSyncDidCompleteNotification = @"OWSSyncManagerKeysSyncDidCompleteNotification";
|
||||
|
||||
NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManagerLastMessageKey";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -39,7 +39,8 @@ extension OWSSyncManager: SyncManagerProtocolSwift {
|
||||
NotificationCenter.default.observe(once: .IncomingContactSyncDidComplete).asVoid(),
|
||||
NotificationCenter.default.observe(once: .IncomingGroupSyncDidComplete).asVoid(),
|
||||
NotificationCenter.default.observe(once: .OWSSyncManagerConfigurationSyncDidComplete).asVoid(),
|
||||
NotificationCenter.default.observe(once: .OWSBlockingManagerBlockedSyncDidComplete).asVoid()
|
||||
NotificationCenter.default.observe(once: .OWSBlockingManagerBlockedSyncDidComplete).asVoid(),
|
||||
NotificationCenter.default.observe(once: .OWSSyncManagerKeysSyncDidComplete).asVoid()
|
||||
])
|
||||
}
|
||||
|
||||
@ -74,6 +75,10 @@ extension OWSSyncManager: SyncManagerProtocolSwift {
|
||||
}
|
||||
|
||||
KeyBackupService.storeSyncedKey(type: .storageService, data: syncMessage.storageService, transaction: transaction)
|
||||
|
||||
transaction.addAsyncCompletion {
|
||||
NotificationCenter.default.postNotificationNameAsync(.OWSSyncManagerKeysSyncDidComplete, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
|
||||
@ -1050,6 +1050,13 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
||||
return;
|
||||
}
|
||||
|
||||
// We also keep track of our local profile key under a special hard coded address,
|
||||
// update it accordingly. This should generally only happen if we're restoring
|
||||
// our profile data from the storage service.
|
||||
if (address.isLocalAddress) {
|
||||
[self setLocalProfileKey:profileKey transaction:transaction];
|
||||
}
|
||||
|
||||
OWSUserProfile *userProfile = [OWSUserProfile getOrBuildUserProfileForAddress:address transaction:transaction];
|
||||
|
||||
OWSAssertDebug(userProfile);
|
||||
@ -1082,6 +1089,13 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
|
||||
|
||||
OWSUserProfile *userProfile = [OWSUserProfile getOrBuildUserProfileForAddress:address transaction:transaction];
|
||||
[userProfile updateWithGivenName:givenName familyName:familyName transaction:transaction completion:nil];
|
||||
|
||||
if (address.isLocalAddress) {
|
||||
[self.localUserProfile updateWithGivenName:givenName
|
||||
familyName:familyName
|
||||
transaction:transaction
|
||||
completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSData *)profileKeyDataForAddress:(SignalServiceAddress *)address
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
|
||||
@objc(OWSFakeStorageServiceManager)
|
||||
class FakeStorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
@ -13,5 +14,5 @@ class FakeStorageServiceManager: NSObject, StorageServiceManagerProtocol {
|
||||
func recordPendingUpdates(updatedAddresses: [SignalServiceAddress]) {}
|
||||
|
||||
func backupPendingChanges() {}
|
||||
func restoreOrCreateManifestIfNecessary() {}
|
||||
func restoreOrCreateManifestIfNecessary() -> AnyPromise { AnyPromise(Promise.value(())) }
|
||||
}
|
||||
|
||||
@ -293,10 +293,7 @@ public class KeyBackupService: NSObject {
|
||||
default:
|
||||
// Most keys derive directly from the master key.
|
||||
// Only a few exceptions derive from another derived key.
|
||||
guard let masterKey = cacheQueue.sync(execute: { cachedMasterKey }) else {
|
||||
owsFailDebug("missing master key")
|
||||
return nil
|
||||
}
|
||||
guard let masterKey = cacheQueue.sync(execute: { cachedMasterKey }) else { return nil }
|
||||
return masterKey
|
||||
}
|
||||
}
|
||||
@ -543,15 +540,13 @@ public class KeyBackupService: NSObject {
|
||||
}
|
||||
|
||||
// Only continue if we didn't previously have a master key or our master key has changed
|
||||
guard masterKey != previousMasterKey else { return }
|
||||
guard masterKey != previousMasterKey, tsAccountManager.isRegisteredAndReady else { return }
|
||||
|
||||
// Trigger a re-creation of the storage manifest, our keys have changed
|
||||
storageServiceManager.restoreOrCreateManifestIfNecessary()
|
||||
|
||||
if tsAccountManager.isRegisteredAndReady {
|
||||
// Sync our new keys with linked devices.
|
||||
syncManager.sendKeysSyncMessage()
|
||||
}
|
||||
// Sync our new keys with linked devices.
|
||||
syncManager.sendKeysSyncMessage()
|
||||
}
|
||||
|
||||
public static func storeSyncedKey(type: DerivedKey, data: Data?, transaction: SDSAnyWriteTransaction) {
|
||||
|
||||
@ -324,11 +324,15 @@ NSUInteger const kUserProfileSchemaVersion = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Profile changes, record updates with storage service
|
||||
[self.storageServiceManager recordPendingUpdatesWithUpdatedAddresses:@[ self.address ]];
|
||||
|
||||
BOOL isLocalUserProfile = [self.address.phoneNumber isEqualToString:kLocalProfileUniqueId];
|
||||
|
||||
// Profile changes, record updates with storage service
|
||||
if (self.tsAccountManager.isRegisteredAndReady) {
|
||||
[self.storageServiceManager
|
||||
recordPendingUpdatesWithUpdatedAddresses:@[ isLocalUserProfile ? self.tsAccountManager.localAddress
|
||||
: self.address ]];
|
||||
}
|
||||
|
||||
[transaction
|
||||
addAsyncCompletionWithQueue:dispatch_get_main_queue()
|
||||
block:^{
|
||||
|
||||
@ -14,7 +14,9 @@ public protocol StorageServiceManagerProtocol {
|
||||
func recordPendingUpdates(updatedAddresses: [SignalServiceAddress])
|
||||
|
||||
func backupPendingChanges()
|
||||
func restoreOrCreateManifestIfNecessary()
|
||||
|
||||
@discardableResult
|
||||
func restoreOrCreateManifestIfNecessary() -> AnyPromise
|
||||
}
|
||||
|
||||
public struct StorageService {
|
||||
@ -306,7 +308,6 @@ public struct StorageService {
|
||||
case 404:
|
||||
status = .notFound
|
||||
default:
|
||||
owsFailDebug("invalid response \(response.statusCode)")
|
||||
if response.statusCode >= 500 {
|
||||
// This is a server error, retry
|
||||
return resolver.reject(StorageError.retryableAssertion)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user