Let users submit debug logs if app launch fails.

This commit is contained in:
Matthew Chen 2018-02-12 11:41:22 -05:00
parent 4aaae856d0
commit 98843cd45c
4 changed files with 166 additions and 17 deletions

View File

@ -61,6 +61,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
@property (nonatomic) UIWindow *screenProtectionWindow;
@property (nonatomic) BOOL hasInitialRootViewController;
@property (nonatomic) BOOL areVersionMigrationsComplete;
@property (nonatomic) BOOL didAppLaunchFail;
@end
@ -130,7 +131,15 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
// We need to do this _after_ we set up logging, when the keychain is unlocked,
// but before we access YapDatabase, files on disk, or NSUserDefaults
[self ensureIsReadyForAppExtensions];
if (![self ensureIsReadyForAppExtensions]) {
// If this method has failed; do nothing.
//
// ensureIsReadyForAppExtensions will show a failure mode UI that
// lets users report this error.
DDLogInfo(@"%@ application: didFinishLaunchingWithOptions failed.", self.logTag);
return YES;
}
[AppVersion instance];
@ -219,10 +228,10 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
}
}
- (void)ensureIsReadyForAppExtensions
- (BOOL)ensureIsReadyForAppExtensions
{
if ([OWSPreferences isReadyForAppExtensions]) {
return;
return YES;
}
if ([NSFileManager.defaultManager fileExistsAtPath:TSStorageManager.legacyDatabaseFilePath]) {
@ -238,14 +247,56 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
}
NSError *_Nullable error = [self convertDatabaseIfNecessary];
// TODO: Handle this error.
OWSAssert(!error);
if (error) {
OWSFail(@"%@ database conversion failed: %@", self.logTag, error);
[self showLaunchFailureUI:error];
return NO;
}
[NSUserDefaults migrateToSharedUserDefaults];
[TSStorageManager migrateToSharedData];
[OWSProfileManager migrateToSharedData];
[TSAttachmentStream migrateToSharedData];
return YES;
}
- (void)showLaunchFailureUI:(NSError *)error
{
// Disable normal functioning of app.
self.didAppLaunchFail = YES;
// We perform a subset of the [application:didFinishLaunchingWithOptions:].
[AppVersion instance];
[self startupLogging];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Show the launch screen until the async database view registrations are complete.
self.window.rootViewController = [self loadingRootViewController];
[self.window makeKeyAndVisible];
UIAlertController *controller =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_TITLE",
@"Title for the 'app launch failed' alert.")
message:NSLocalizedString(@"APP_LAUNCH_FAILURE_ALERT_MESSAGE",
@"Message for the 'app launch failed' alert.")
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[Pastelog submitLogsWithShareCompletion:^{
DDLogInfo(
@"%@ exiting after sharing debug logs.", self.logTag);
[DDLog flushLog];
exit(0);
}];
}]];
UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController];
[fromViewController presentViewController:controller animated:YES completion:nil];
}
- (nullable NSError *)convertDatabaseIfNecessary
@ -368,6 +419,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFail(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogInfo(@"%@ registered vanilla push token: %@", self.logTag, deviceToken);
[PushRegistrationManager.sharedManager didReceiveVanillaPushToken:deviceToken];
}
@ -376,6 +432,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFail(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogError(@"%@ failed to register vanilla push token with error: %@", self.logTag, error);
#ifdef DEBUG
DDLogWarn(
@ -392,6 +453,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFail(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogInfo(@"%@ registered user notification settings", self.logTag);
[PushRegistrationManager.sharedManager didRegisterUserNotificationSettings];
}
@ -403,6 +469,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return NO;
}
if (!AppReadiness.isAppReady) {
DDLogWarn(@"%@ Ignoring openURL: app not ready.", self.logTag);
// TODO: Consider using [AppReadiness runNowOrWhenAppIsReady:].
@ -437,6 +508,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)applicationDidBecomeActive:(UIApplication *)application {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogWarn(@"%@ applicationDidBecomeActive.", self.logTag);
if (CurrentAppContext().isRunningTests) {
return;
@ -536,6 +612,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)applicationWillResignActive:(UIApplication *)application {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogWarn(@"%@ applicationWillResignActive.", self.logTag);
__block OWSBackgroundTask *backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
@ -564,6 +645,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
completionHandler:(void (^)(BOOL succeeded))completionHandler {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
if (!AppReadiness.isAppReady) {
DDLogWarn(@"%@ Ignoring performActionForShortcutItem: app not ready.", self.logTag);
// TODO: Consider using [AppReadiness runNowOrWhenAppIsReady:].
@ -602,6 +688,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return NO;
}
if (!AppReadiness.isAppReady) {
DDLogWarn(@"%@ Ignoring continueUserActivity: app not ready.", self.logTag);
// TODO: Consider using [AppReadiness runNowOrWhenAppIsReady:].
@ -772,6 +863,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
// It is safe to continue even if the app isn't ready.
[[PushManager sharedManager] application:application didReceiveRemoteNotification:userInfo];
}
@ -781,6 +877,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
// It is safe to continue even if the app isn't ready.
[[PushManager sharedManager] application:application
didReceiveRemoteNotification:userInfo
@ -790,6 +891,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
DDLogInfo(@"%@ %s %@", self.logTag, __PRETTY_FUNCTION__, notification);
[AppStoreRating preventPromptAtNextTest];
@ -805,6 +911,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
if (!AppReadiness.isAppReady) {
DDLogWarn(@"%@ Ignoring handleActionWithIdentifier: app not ready.", self.logTag);
// TODO: Consider using [AppReadiness runNowOrWhenAppIsReady:].
@ -825,6 +936,11 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
return;
}
if (!AppReadiness.isAppReady) {
DDLogWarn(@"%@ Ignoring handleActionWithIdentifier: app not ready.", self.logTag);
// TODO: Consider using [AppReadiness runNowOrWhenAppIsReady:].

View File

@ -1,13 +1,15 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
@interface Pastelog : NSObject
typedef void (^successBlock)(NSError *error, NSString *urlString);
typedef void (^DebugLogsUploadedBlock)(NSError *error, NSString *urlString);
typedef void (^DebugLogsSharedBlock)(void);
+(void)submitLogs;
+(void)submitLogsWithCompletion:(successBlock)block;
+(void)submitLogsWithCompletion:(successBlock)block forFileLogger:(DDFileLogger*)fileLogger;
+ (void)submitLogsWithShareCompletion:(nullable DebugLogsSharedBlock)block;
+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block;
+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block forFileLogger:(DDFileLogger *)fileLogger;
@end

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "Pastelog.h"
@ -18,7 +18,7 @@
@property (nonatomic) UIAlertController *loadingAlert;
@property (nonatomic) NSMutableData *responseData;
@property (nonatomic) successBlock block;
@property (nonatomic) DebugLogsUploadedBlock block;
@end
@ -27,7 +27,20 @@
@implementation Pastelog
+(void)submitLogs {
[self submitLogsWithCompletion:^(NSError *error, NSString *urlString) {
[self submitLogsWithShareCompletion:nil];
}
+ (void)submitLogsWithShareCompletion:(nullable DebugLogsSharedBlock)shareCompletionParam
{
DebugLogsSharedBlock shareCompletion = ^{
if (shareCompletionParam) {
// Wait a moment. If PasteLog opens a URL, it needs a moment to complete.
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), shareCompletionParam);
}
};
[self submitLogsWithUploadCompletion:^(NSError *error, NSString *urlString) {
if (!error) {
UIAlertController *alert = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_TITLE", @"Title of the debug log alert.")
@ -41,6 +54,8 @@
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[Pastelog.sharedManager submitEmail:urlString];
shareCompletion();
}]];
[alert addAction:[UIAlertAction
actionWithTitle:NSLocalizedString(@"DEBUG_LOG_ALERT_OPTION_COPY_LINK",
@ -49,6 +64,8 @@
handler:^(UIAlertAction *_Nonnull action) {
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:urlString];
shareCompletion();
}]];
#ifdef DEBUG
[alert addAction:[UIAlertAction
@ -73,7 +90,8 @@
@"Label for the 'Open a Bug Report' option of the the debug log alert.")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
[Pastelog.sharedManager prepareRedirection:urlString];
[Pastelog.sharedManager prepareRedirection:urlString
shareCompletion:shareCompletion];
}]];
UIViewController *presentingViewController
= UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts;
@ -91,11 +109,13 @@
}];
}
+(void)submitLogsWithCompletion:(successBlock)block {
[self submitLogsWithCompletion:(successBlock)block forFileLogger:[[DDFileLogger alloc] init]];
+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block
{
[self submitLogsWithUploadCompletion:block forFileLogger:[[DDFileLogger alloc] init]];
}
+(void)submitLogsWithCompletion:(successBlock)block forFileLogger:(DDFileLogger*)fileLogger {
+ (void)submitLogsWithUploadCompletion:(DebugLogsUploadedBlock)block forFileLogger:(DDFileLogger *)fileLogger
{
[self sharedManager].block = block;
@ -231,7 +251,10 @@
[UIApplication.sharedApplication openURL: [NSURL URLWithString: urlString]];
}
- (void)prepareRedirection:(NSString*)url {
- (void)prepareRedirection:(NSString *)url shareCompletion:(DebugLogsSharedBlock)shareCompletion
{
OWSAssert(shareCompletion);
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:url];
@ -248,6 +271,8 @@
[UIApplication.sharedApplication
openURL:[NSURL URLWithString:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"LOGS_URL"]]];
shareCompletion();
}]];
UIViewController *presentingViewController = UIApplication.sharedApplication.frontmostViewControllerIgnoringAlerts;
[presentingViewController presentViewController:alert animated:NO completion:nil];

View File

@ -52,6 +52,12 @@
/* No comment provided by engineer. */
"APN_MESSAGE_IN_GROUP_DETAILED" = "%@ in group %@: %@";
/* Message for the 'app launch failed' alert. */
"APP_LAUNCH_FAILURE_ALERT_MESSAGE" = "Signal can't launch. Please send your debug logs to our team so that we can try to resolve this issue.";
/* Title for the 'app launch failed' alert. */
"APP_LAUNCH_FAILURE_ALERT_TITLE" = "Error";
/* Text prompting user to edit their profile name. */
"APP_SETTINGS_EDIT_PROFILE_NAME_PROMPT" = "Enter your name";