From ad6937378ef5d289aa9d1b2778121bd97dbf75b1 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 6 Apr 2018 12:00:09 -0400 Subject: [PATCH 1/3] Fix screen lock edge cases. --- Signal/src/util/OWSScreenLock.swift | 6 + Signal/src/util/OWSScreenLockUI.m | 177 ++++++++++++++-------------- 2 files changed, 96 insertions(+), 87 deletions(-) diff --git a/Signal/src/util/OWSScreenLock.swift b/Signal/src/util/OWSScreenLock.swift index 496f00958..f549a39e4 100644 --- a/Signal/src/util/OWSScreenLock.swift +++ b/Signal/src/util/OWSScreenLock.swift @@ -24,6 +24,7 @@ import LocalAuthentication 0 ] + @objc public static let ScreenLockWasEnabled = Notification.Name("ScreenLockWasEnabled") @objc public static let ScreenLockDidChange = Notification.Name("ScreenLockDidChange") let primaryStorage: OWSPrimaryStorage @@ -86,8 +87,13 @@ import LocalAuthentication AssertIsOnMainThread() assert(OWSStorage.isStorageReady()) + let isEnabling = value && !isScreenLockEnabled() + self.dbConnection.setBool(value, forKey: OWSScreenLock_Key_IsScreenLockEnabled, inCollection: OWSScreenLock_Collection) + if isEnabling { + NotificationCenter.default.postNotificationNameAsync(OWSScreenLock.ScreenLockWasEnabled, object: nil) + } NotificationCenter.default.postNotificationNameAsync(OWSScreenLock.ScreenLockDidChange, object: nil) } diff --git a/Signal/src/util/OWSScreenLockUI.m b/Signal/src/util/OWSScreenLockUI.m index 1a298cd41..9692e9a6f 100644 --- a/Signal/src/util/OWSScreenLockUI.m +++ b/Signal/src/util/OWSScreenLockUI.m @@ -31,12 +31,11 @@ NS_ASSUME_NONNULL_BEGIN // UI is dismissing. @property (nonatomic) BOOL shouldClearAuthUIWhenActive; -@property (nonatomic, nullable) NSDate *appEnteredBackgroundDate; -@property (nonatomic, nullable) NSDate *appEnteredForegroundDate; -@property (nonatomic, nullable) NSDate *lastUnlockSuccessDate; - @property (nonatomic, nullable) NSTimer *inactiveTimer; +@property (nonatomic, nullable) NSDate *screenLockCountdownDate; +@property (nonatomic) BOOL isScreenLockUnlocked; + @end #pragma mark - @@ -99,6 +98,10 @@ NS_ASSUME_NONNULL_BEGIN selector:@selector(screenLockDidChange:) name:OWSScreenLock.ScreenLockDidChange object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(screenLockWasEnabled:) + name:OWSScreenLock.ScreenLockWasEnabled + object:nil]; } - (void)setupWithRootWindow:(UIWindow *)rootWindow @@ -115,38 +118,57 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Methods -- (void)setAppIsInactive:(BOOL)appIsInactive +- (void)tryToActivateScreenLockUponBecomingActive { - BOOL didChange = _appIsInactive != appIsInactive; + OWSAssert(!self.appIsInactive); + + if (!self.isScreenLockUnlocked) { + // Screen lock is already activated. + DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 0", self.logTag); + return; + } + if (!self.screenLockCountdownDate) { + // We became inactive, but never started a countdown. + DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 1", self.logTag); + return; + } + NSTimeInterval countdownInterval = fabs([self.screenLockCountdownDate timeIntervalSinceNow]); + OWSAssert(countdownInterval >= 0); + NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout; + OWSAssert(screenLockTimeout >= 0); + if (countdownInterval >= screenLockTimeout) { + self.isScreenLockUnlocked = NO; + DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive YES 1 (%0.3f >= %0.3f)", + self.logTag, + countdownInterval, + screenLockTimeout); + } else { + DDLogVerbose(@"%@ tryToActivateScreenLockUponBecomingActive NO 2 (%0.3f < %0.3f)", + self.logTag, + countdownInterval, + screenLockTimeout); + } +} +- (void)setAppIsInactive:(BOOL)appIsInactive +{ _appIsInactive = appIsInactive; - if (didChange) { - // If app is inactive for more than N seconds, - // treat this as "entering the background" for the purposes - // of Screen Lock. - if (!appIsInactive) { - [self.inactiveTimer invalidate]; - self.inactiveTimer = nil; - } else if (!self.isShowingScreenLockUI) { - [self.inactiveTimer invalidate]; - self.inactiveTimer = [NSTimer weakScheduledTimerWithTimeInterval:45.f - target:self - selector:@selector(inactiveTimerDidFire) - userInfo:nil - repeats:NO]; - } + if (!appIsInactive) { + [self tryToActivateScreenLockUponBecomingActive]; + + self.screenLockCountdownDate = nil; } + [self startInactiveTimerIfNecessary]; + [self ensureScreenProtection]; } - (void)setAppIsInBackground:(BOOL)appIsInBackground { - if (appIsInBackground) { - if (!_appIsInBackground) { - [self markAppAsInBackground]; - } + if (appIsInBackground && !_appIsInBackground) { + [self startScreenLockCountdownIfNecessary]; } _appIsInBackground = appIsInBackground; @@ -154,31 +176,16 @@ NS_ASSUME_NONNULL_BEGIN [self ensureScreenProtection]; } -- (void)markAppAsInBackground +- (void)startScreenLockCountdownIfNecessary { - // Record the time when app entered background. - BOOL shouldResetEnteredBackgroundDate = NO; - if (!self.appEnteredBackgroundDate) { - // If this is the first time we're entering the - // background, record the date. - shouldResetEnteredBackgroundDate = YES; - } - if (self.hasUnlockedScreenLock) { - // If we've unlocked the screen lock, record the date. - shouldResetEnteredBackgroundDate = YES; - } else { - // If we're returning to the background _without_ - // having unlocked the screen lock, DO NOT update this - // value as that would reset the unlock timeout. - } - if (shouldResetEnteredBackgroundDate) { - self.appEnteredBackgroundDate = [NSDate new]; + if (!self.screenLockCountdownDate) { + DDLogVerbose(@"%@ startScreenLockCountdownIfNecessary.", self.logTag); + self.screenLockCountdownDate = [NSDate new]; } self.didLastUnlockAttemptFail = NO; - [self.inactiveTimer invalidate]; - self.inactiveTimer = nil; + [self clearInactiveTimer]; } - (void)ensureScreenProtection @@ -232,8 +239,11 @@ NS_ASSUME_NONNULL_BEGIN [OWSScreenLock.sharedManager tryToUnlockScreenLockWithSuccess:^{ DDLogInfo(@"%@ unlock screen lock succeeded.", self.logTag); + self.isShowingScreenLockUI = NO; - self.lastUnlockSuccessDate = [NSDate new]; + + self.isScreenLockUnlocked = YES; + [self ensureScreenProtection]; } failure:^(NSError *error) { @@ -284,17 +294,6 @@ NS_ASSUME_NONNULL_BEGIN } } -- (BOOL)hasUnlockedScreenLock -{ - if (!self.lastUnlockSuccessDate) { - return NO; - } else if (!self.appEnteredBackgroundDate) { - return YES; - } else { - return [self.lastUnlockSuccessDate isAfterDate:self.appEnteredBackgroundDate]; - } -} - - (BOOL)shouldHaveScreenLock { if (![TSAccountManager isRegistered]) { @@ -305,10 +304,6 @@ NS_ASSUME_NONNULL_BEGIN // Don't show 'Screen Lock' if 'Screen Lock' isn't enabled. DDLogVerbose(@"%@ shouldHaveScreenLock NO 2.", self.logTag); return NO; - } else if (self.hasUnlockedScreenLock) { - // Don't show 'Screen Lock' if 'Screen Lock' has been unlocked. - DDLogVerbose(@"%@ shouldHaveScreenLock NO 3.", self.logTag); - return NO; } else if (self.appIsInBackground) { // Don't show 'Screen Lock' if app is in background. DDLogVerbose(@"%@ shouldHaveScreenLock NO 4.", self.logTag); @@ -322,26 +317,10 @@ NS_ASSUME_NONNULL_BEGIN // Don't show 'Screen Lock' if app is inactive. DDLogVerbose(@"%@ shouldHaveScreenLock NO 5.", self.logTag); return NO; - } else if (!self.appEnteredBackgroundDate) { - // Show 'Screen Lock' if app has just launched. - DDLogVerbose(@"%@ shouldHaveScreenLock YES 1.", self.logTag); - return YES; } else { - OWSAssert(self.appEnteredBackgroundDate); - - NSTimeInterval screenLockInterval = fabs([self.appEnteredBackgroundDate timeIntervalSinceNow]); - NSTimeInterval screenLockTimeout = OWSScreenLock.sharedManager.screenLockTimeout; - OWSAssert(screenLockInterval >= 0); - OWSAssert(screenLockTimeout >= 0); - if (screenLockInterval < screenLockTimeout) { - // Don't show 'Screen Lock' if 'Screen Lock' timeout hasn't elapsed. - DDLogVerbose(@"%@ shouldHaveScreenLock NO 6.", self.logTag); - return NO; - } else { - // Otherwise, show 'Screen Lock'. - DDLogVerbose(@"%@ shouldHaveScreenLock YES 2.", self.logTag); - return YES; - } + BOOL shouldHaveScreenLock = !self.isScreenLockUnlocked; + DDLogVerbose(@"%@ shouldHaveScreenLock ? %d.", self.logTag, shouldHaveScreenLock); + return shouldHaveScreenLock; } } @@ -504,6 +483,16 @@ NS_ASSUME_NONNULL_BEGIN [self ensureScreenProtection]; } +- (void)screenLockWasEnabled:(NSNotification *)notification +{ + // When we enable screen lock, consider that an unlock. + self.isScreenLockUnlocked = YES; + + DDLogVerbose(@"%@ screenLockWasEnabled", self.logTag); + + [self ensureScreenProtection]; +} + - (void)registrationStateDidChange { OWSAssertIsOnMainThread(); @@ -542,13 +531,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)applicationWillEnterForeground:(NSNotification *)notification { - // Clear the "delay Screen Lock UI" state; we don't want any - // delays when presenting the "unlock screen lock UI" after - // returning from background. - self.lastUnlockSuccessDate = nil; - self.appIsInBackground = NO; - self.appEnteredForegroundDate = [NSDate new]; } - (void)applicationDidEnterBackground:(NSNotification *)notification @@ -556,9 +539,29 @@ NS_ASSUME_NONNULL_BEGIN self.appIsInBackground = YES; } +#pragma mark - Inactive Timer + - (void)inactiveTimerDidFire { - [self markAppAsInBackground]; + [self startScreenLockCountdownIfNecessary]; +} + +- (void)startInactiveTimerIfNecessary +{ + if (self.appIsInactive && !self.isShowingScreenLockUI && !self.inactiveTimer) { + [self.inactiveTimer invalidate]; + self.inactiveTimer = [NSTimer weakScheduledTimerWithTimeInterval:45.f + target:self + selector:@selector(inactiveTimerDidFire) + userInfo:nil + repeats:NO]; + } +} + +- (void)clearInactiveTimer +{ + [self.inactiveTimer invalidate]; + self.inactiveTimer = nil; } @end From 2b210c7557c2f9b9750e8f2318418f6d2ced54dd Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 6 Apr 2018 12:12:49 -0400 Subject: [PATCH 2/3] Fix screen lock edge cases. --- Signal/src/util/OWSScreenLockUI.m | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Signal/src/util/OWSScreenLockUI.m b/Signal/src/util/OWSScreenLockUI.m index 9692e9a6f..2d5d3232a 100644 --- a/Signal/src/util/OWSScreenLockUI.m +++ b/Signal/src/util/OWSScreenLockUI.m @@ -31,10 +31,22 @@ NS_ASSUME_NONNULL_BEGIN // UI is dismissing. @property (nonatomic) BOOL shouldClearAuthUIWhenActive; -@property (nonatomic, nullable) NSTimer *inactiveTimer; +// Indicates whether or not the user is currently locked out of +// the app. Only applies if OWSScreenLock.isScreenLockEnabled. +// +// * The user is locked out out by default on app launch. +// * The user is also locked out if they spend more than +// "timeout" seconds outside the app. When the user leaves +// the app, a "countdown" begins. +@property (nonatomic) BOOL isScreenLockUnlocked; @property (nonatomic, nullable) NSDate *screenLockCountdownDate; -@property (nonatomic) BOOL isScreenLockUnlocked; + +// We normally start the "countdown" when the app enters the background, +// But we also want to start the "countdown" if the app is inactive for +// more than N seconds. +@property (nonatomic, nullable) NSTimer *inactiveTimer; + @end From 8f1cfed5cf0d0f9dc20ad3347807197ef8b08b0d Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 6 Apr 2018 13:51:37 -0400 Subject: [PATCH 3/3] "Bump build to 2.23.1.2." --- Signal/Signal-Info.plist | 2 +- SignalShareExtension/Info.plist | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 415a8975d..d057dad1c 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -38,7 +38,7 @@ CFBundleVersion - 2.23.1.1 + 2.23.1.2 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist index 19ed2d89a..70e519870 100644 --- a/SignalShareExtension/Info.plist +++ b/SignalShareExtension/Info.plist @@ -19,7 +19,7 @@ CFBundleShortVersionString 2.23.1 CFBundleVersion - 2.23.1.1 + 2.23.1.2 ITSAppUsesNonExemptEncryption NSAppTransportSecurity