Mitigate upload failure when app is backgrounded.

iOS closes an app’s network connections when the app is backgrounded. This can cause an in-progress upload request to fail. We can mitigate this by requesting additional background execution time using the `UIApplication` background task APIs.

BUG=b:130302235

Change-Id: Ifd8e14ca82c736ad7dd60dcdd0d4bbcabb76f5ad
Signed-off-by: Darren Mo <darrenmo@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2251020
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Darren Mo 2021-05-16 18:40:57 -07:00 committed by Mark Mentovai
parent dbbdf05740
commit 7ba29f4a36
2 changed files with 72 additions and 9 deletions

View File

@ -59,6 +59,8 @@
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */; }; CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */; };
CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */; }; CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */; };
E69213D8265202570071B04F /* HTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = E69213D6265202570071B04F /* HTTPRequest.h */; };
E69213D9265202570071B04F /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E69213D7265202570071B04F /* HTTPRequest.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -116,6 +118,8 @@
CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = long_string_dictionary.cc; sourceTree = "<group>"; }; CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = long_string_dictionary.cc; sourceTree = "<group>"; };
CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = long_string_dictionary.h; sourceTree = "<group>"; }; CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = long_string_dictionary.h; sourceTree = "<group>"; };
D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; }; D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; };
E69213D6265202570071B04F /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequest.h; sourceTree = "<group>"; };
E69213D7265202570071B04F /* HTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPRequest.m; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -289,6 +293,8 @@
children = ( children = (
16C7CC88147D4A4300776EAD /* GTMLogger.h */, 16C7CC88147D4A4300776EAD /* GTMLogger.h */,
16C7CC89147D4A4300776EAD /* GTMLogger.m */, 16C7CC89147D4A4300776EAD /* GTMLogger.m */,
E69213D6265202570071B04F /* HTTPRequest.h */,
E69213D7265202570071B04F /* HTTPRequest.m */,
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */, 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */,
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */, 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */,
16C7CC93147D4A4300776EAD /* file_id.cc */, 16C7CC93147D4A4300776EAD /* file_id.cc */,
@ -335,6 +341,7 @@
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */, 16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */,
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */, 16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */,
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */, 16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */,
E69213D8265202570071B04F /* HTTPRequest.h in Headers */,
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */, 16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */,
16C7CE84147D4A4300776EAD /* file_id.h in Headers */, 16C7CE84147D4A4300776EAD /* file_id.h in Headers */,
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */, 16C7CE86147D4A4300776EAD /* macho_id.h in Headers */,
@ -416,6 +423,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */, 16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */,
E69213D9265202570071B04F /* HTTPRequest.m in Sources */,
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */, 16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */,
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */, 16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */,
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */, 16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */,

View File

@ -32,8 +32,26 @@
#include <Availability.h> #include <Availability.h>
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0)
#import <UIKit/UIKit.h>
#define HAS_BACKGROUND_TASK_API 1
#else
#define HAS_BACKGROUND_TASK_API 0
#endif
#import "encoding_util.h" #import "encoding_util.h"
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
defined(MAC_OS_X_VERSION_10_11) && \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
#define USE_NSURLSESSION 1
#else
#define USE_NSURLSESSION 0
#endif
// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has // As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements // been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is // it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
@ -41,20 +59,17 @@
static NSData* SendSynchronousNSURLRequest(NSURLRequest* req, static NSData* SendSynchronousNSURLRequest(NSURLRequest* req,
NSURLResponse** outResponse, NSURLResponse** outResponse,
NSError** outError) { NSError** outError) {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \ #if USE_NSURLSESSION
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
defined(MAC_OS_X_VERSION_10_11) && \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
__block NSData* result = nil; __block NSData* result = nil;
__block NSError* error = nil; __block NSError* error = nil;
__block NSURLResponse* response = nil; __block NSURLResponse* response = nil;
dispatch_semaphore_t waitSemaphone = dispatch_semaphore_create(0); dispatch_semaphore_t waitSemaphone = dispatch_semaphore_create(0);
NSURLSessionConfiguration* config = NSURLSessionConfiguration* config =
[NSURLSessionConfiguration defaultSessionConfiguration]; [NSURLSessionConfiguration defaultSessionConfiguration];
[config setTimeoutIntervalForRequest:240.0]; [config setTimeoutIntervalForRequest:240.0];
NSURLSession* session = [NSURLSession sessionWithConfiguration:config]; NSURLSession* session = [NSURLSession sessionWithConfiguration:config];
[[session NSURLSessionDataTask *task = [session
dataTaskWithRequest:req dataTaskWithRequest:req
completionHandler:^(NSData* data, NSURLResponse* resp, NSError* err) { completionHandler:^(NSData* data, NSURLResponse* resp, NSError* err) {
if (outError) if (outError)
@ -64,19 +79,59 @@ static NSData* SendSynchronousNSURLRequest(NSURLRequest* req,
if (err == nil) if (err == nil)
result = [data retain]; result = [data retain];
dispatch_semaphore_signal(waitSemaphone); dispatch_semaphore_signal(waitSemaphone);
}] resume]; }];
[task resume];
#if HAS_BACKGROUND_TASK_API
// Used to guard against ending the background task twice, which UIKit
// considers to be an error.
__block BOOL isBackgroundTaskActive = YES;
__block UIBackgroundTaskIdentifier backgroundTaskIdentifier =
UIBackgroundTaskInvalid;
backgroundTaskIdentifier = [UIApplication.sharedApplication
beginBackgroundTaskWithName:@"Breakpad Upload"
expirationHandler:^{
if (!isBackgroundTaskActive) {
return;
}
isBackgroundTaskActive = NO;
[task cancel];
[UIApplication.sharedApplication
endBackgroundTask:backgroundTaskIdentifier];
}];
#endif // HAS_BACKGROUND_TASK_API
dispatch_semaphore_wait(waitSemaphone, DISPATCH_TIME_FOREVER); dispatch_semaphore_wait(waitSemaphone, DISPATCH_TIME_FOREVER);
dispatch_release(waitSemaphone); dispatch_release(waitSemaphone);
#if HAS_BACKGROUND_TASK_API
if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) {
// Dispatch to main queue in order to synchronize access to
// `isBackgroundTaskActive` with the background task expiration handler,
// which is always run on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{
if (!isBackgroundTaskActive) {
return;
}
isBackgroundTaskActive = NO;
[UIApplication.sharedApplication
endBackgroundTask:backgroundTaskIdentifier];
});
}
#endif // HAS_BACKGROUND_TASK_API
if (outError) if (outError)
*outError = [error autorelease]; *outError = [error autorelease];
if (outResponse) if (outResponse)
*outResponse = [response autorelease]; *outResponse = [response autorelease];
return [result autorelease]; return [result autorelease];
#else #else // USE_NSURLSESSION
return [NSURLConnection sendSynchronousRequest:req return [NSURLConnection sendSynchronousRequest:req
returningResponse:outResponse returningResponse:outResponse
error:outError]; error:outError];
#endif #endif // USE_NSURLSESSION
} }
@implementation HTTPRequest @implementation HTTPRequest