1*9712c20fSFrederick Mayle// Copyright 2012 Google LLC 2*9712c20fSFrederick Mayle// 3*9712c20fSFrederick Mayle// Redistribution and use in source and binary forms, with or without 4*9712c20fSFrederick Mayle// modification, are permitted provided that the following conditions are 5*9712c20fSFrederick Mayle// met: 6*9712c20fSFrederick Mayle// 7*9712c20fSFrederick Mayle// * Redistributions of source code must retain the above copyright 8*9712c20fSFrederick Mayle// notice, this list of conditions and the following disclaimer. 9*9712c20fSFrederick Mayle// * Redistributions in binary form must reproduce the above 10*9712c20fSFrederick Mayle// copyright notice, this list of conditions and the following disclaimer 11*9712c20fSFrederick Mayle// in the documentation and/or other materials provided with the 12*9712c20fSFrederick Mayle// distribution. 13*9712c20fSFrederick Mayle// * Neither the name of Google LLC nor the names of its 14*9712c20fSFrederick Mayle// contributors may be used to endorse or promote products derived from 15*9712c20fSFrederick Mayle// this software without specific prior written permission. 16*9712c20fSFrederick Mayle// 17*9712c20fSFrederick Mayle// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*9712c20fSFrederick Mayle// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*9712c20fSFrederick Mayle// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20*9712c20fSFrederick Mayle// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21*9712c20fSFrederick Mayle// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22*9712c20fSFrederick Mayle// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23*9712c20fSFrederick Mayle// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24*9712c20fSFrederick Mayle// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25*9712c20fSFrederick Mayle// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26*9712c20fSFrederick Mayle// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*9712c20fSFrederick Mayle// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*9712c20fSFrederick Mayle 29*9712c20fSFrederick Mayle#import "BreakpadController.h" 30*9712c20fSFrederick Mayle 31*9712c20fSFrederick Mayle#import <UIKit/UIKit.h> 32*9712c20fSFrederick Mayle#include <asl.h> 33*9712c20fSFrederick Mayle#include <execinfo.h> 34*9712c20fSFrederick Mayle#include <signal.h> 35*9712c20fSFrederick Mayle#include <unistd.h> 36*9712c20fSFrederick Mayle#include <sys/sysctl.h> 37*9712c20fSFrederick Mayle 38*9712c20fSFrederick Mayle#include <common/scoped_ptr.h> 39*9712c20fSFrederick Mayle 40*9712c20fSFrederick Mayle#pragma mark - 41*9712c20fSFrederick Mayle#pragma mark Private Methods 42*9712c20fSFrederick Mayle 43*9712c20fSFrederick Mayle@interface BreakpadController () 44*9712c20fSFrederick Mayle 45*9712c20fSFrederick Mayle// Init the singleton instance. 46*9712c20fSFrederick Mayle- (id)initSingleton; 47*9712c20fSFrederick Mayle 48*9712c20fSFrederick Mayle// Load a crash report and send it to the server. 49*9712c20fSFrederick Mayle- (void)sendStoredCrashReports; 50*9712c20fSFrederick Mayle 51*9712c20fSFrederick Mayle// Returns when a report can be sent. |-1| means never, |0| means that a report 52*9712c20fSFrederick Mayle// can be sent immediately, a positive number is the number of seconds to wait 53*9712c20fSFrederick Mayle// before being allowed to upload a report. 54*9712c20fSFrederick Mayle- (int)sendDelay; 55*9712c20fSFrederick Mayle 56*9712c20fSFrederick Mayle// Notifies that a report will be sent, and update the last sending time 57*9712c20fSFrederick Mayle// accordingly. 58*9712c20fSFrederick Mayle- (void)reportWillBeSent; 59*9712c20fSFrederick Mayle 60*9712c20fSFrederick Mayle@end 61*9712c20fSFrederick Mayle 62*9712c20fSFrederick Mayle#pragma mark - 63*9712c20fSFrederick Mayle#pragma mark Anonymous namespace 64*9712c20fSFrederick Mayle 65*9712c20fSFrederick Maylenamespace { 66*9712c20fSFrederick Mayle 67*9712c20fSFrederick Mayle// The name of the user defaults key for the last submission to the crash 68*9712c20fSFrederick Mayle// server. 69*9712c20fSFrederick MayleNSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission"; 70*9712c20fSFrederick Mayle 71*9712c20fSFrederick Mayle// Returns a NSString describing the current platform. 72*9712c20fSFrederick MayleNSString* GetPlatform() { 73*9712c20fSFrederick Mayle // Name of the system call for getting the platform. 74*9712c20fSFrederick Mayle static const char kHwMachineSysctlName[] = "hw.machine"; 75*9712c20fSFrederick Mayle 76*9712c20fSFrederick Mayle NSString* result = nil; 77*9712c20fSFrederick Mayle 78*9712c20fSFrederick Mayle size_t size = 0; 79*9712c20fSFrederick Mayle if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0) 80*9712c20fSFrederick Mayle return nil; 81*9712c20fSFrederick Mayle google_breakpad::scoped_array<char> machine(new char[size]); 82*9712c20fSFrederick Mayle if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0) 83*9712c20fSFrederick Mayle result = [NSString stringWithUTF8String:machine.get()]; 84*9712c20fSFrederick Mayle return result; 85*9712c20fSFrederick Mayle} 86*9712c20fSFrederick Mayle 87*9712c20fSFrederick Mayle} // namespace 88*9712c20fSFrederick Mayle 89*9712c20fSFrederick Mayle#pragma mark - 90*9712c20fSFrederick Mayle#pragma mark BreakpadController Implementation 91*9712c20fSFrederick Mayle 92*9712c20fSFrederick Mayle@implementation BreakpadController 93*9712c20fSFrederick Mayle 94*9712c20fSFrederick Mayle+ (BreakpadController*)sharedInstance { 95*9712c20fSFrederick Mayle static dispatch_once_t onceToken; 96*9712c20fSFrederick Mayle static BreakpadController* sharedInstance ; 97*9712c20fSFrederick Mayle dispatch_once(&onceToken, ^{ 98*9712c20fSFrederick Mayle sharedInstance = [[BreakpadController alloc] initSingleton]; 99*9712c20fSFrederick Mayle }); 100*9712c20fSFrederick Mayle return sharedInstance; 101*9712c20fSFrederick Mayle} 102*9712c20fSFrederick Mayle 103*9712c20fSFrederick Mayle- (id)init { 104*9712c20fSFrederick Mayle return nil; 105*9712c20fSFrederick Mayle} 106*9712c20fSFrederick Mayle 107*9712c20fSFrederick Mayle- (id)initSingleton { 108*9712c20fSFrederick Mayle self = [super init]; 109*9712c20fSFrederick Mayle if (self) { 110*9712c20fSFrederick Mayle queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL); 111*9712c20fSFrederick Mayle enableUploads_ = NO; 112*9712c20fSFrederick Mayle started_ = NO; 113*9712c20fSFrederick Mayle [self resetConfiguration]; 114*9712c20fSFrederick Mayle } 115*9712c20fSFrederick Mayle return self; 116*9712c20fSFrederick Mayle} 117*9712c20fSFrederick Mayle 118*9712c20fSFrederick Mayle// Since this class is a singleton, this method is not expected to be called. 119*9712c20fSFrederick Mayle- (void)dealloc { 120*9712c20fSFrederick Mayle assert(!breakpadRef_); 121*9712c20fSFrederick Mayle dispatch_release(queue_); 122*9712c20fSFrederick Mayle [configuration_ release]; 123*9712c20fSFrederick Mayle [uploadTimeParameters_ release]; 124*9712c20fSFrederick Mayle [super dealloc]; 125*9712c20fSFrederick Mayle} 126*9712c20fSFrederick Mayle 127*9712c20fSFrederick Mayle#pragma mark - 128*9712c20fSFrederick Mayle 129*9712c20fSFrederick Mayle- (void)start:(BOOL)onCurrentThread { 130*9712c20fSFrederick Mayle if (started_) 131*9712c20fSFrederick Mayle return; 132*9712c20fSFrederick Mayle started_ = YES; 133*9712c20fSFrederick Mayle void(^startBlock)() = ^{ 134*9712c20fSFrederick Mayle assert(!breakpadRef_); 135*9712c20fSFrederick Mayle breakpadRef_ = BreakpadCreate(configuration_); 136*9712c20fSFrederick Mayle if (breakpadRef_) { 137*9712c20fSFrederick Mayle BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform()); 138*9712c20fSFrederick Mayle } 139*9712c20fSFrederick Mayle }; 140*9712c20fSFrederick Mayle if (onCurrentThread) 141*9712c20fSFrederick Mayle startBlock(); 142*9712c20fSFrederick Mayle else 143*9712c20fSFrederick Mayle dispatch_async(queue_, startBlock); 144*9712c20fSFrederick Mayle} 145*9712c20fSFrederick Mayle 146*9712c20fSFrederick Mayle- (void)stop { 147*9712c20fSFrederick Mayle if (!started_) 148*9712c20fSFrederick Mayle return; 149*9712c20fSFrederick Mayle started_ = NO; 150*9712c20fSFrederick Mayle dispatch_sync(queue_, ^{ 151*9712c20fSFrederick Mayle if (breakpadRef_) { 152*9712c20fSFrederick Mayle BreakpadRelease(breakpadRef_); 153*9712c20fSFrederick Mayle breakpadRef_ = NULL; 154*9712c20fSFrederick Mayle } 155*9712c20fSFrederick Mayle }); 156*9712c20fSFrederick Mayle} 157*9712c20fSFrederick Mayle 158*9712c20fSFrederick Mayle- (BOOL)isStarted { 159*9712c20fSFrederick Mayle return started_; 160*9712c20fSFrederick Mayle} 161*9712c20fSFrederick Mayle 162*9712c20fSFrederick Mayle// This method must be called from the breakpad queue. 163*9712c20fSFrederick Mayle- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration 164*9712c20fSFrederick Mayle withBreakpadRef:(BreakpadRef)ref { 165*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 166*9712c20fSFrederick Mayle "threadUnsafeSendReportWithConfiguration is called"); 167*9712c20fSFrederick Mayle if (breakpadRef_) { 168*9712c20fSFrederick Mayle BreakpadUploadReportWithParametersAndConfiguration( 169*9712c20fSFrederick Mayle breakpadRef_, uploadTimeParameters_, configuration, 170*9712c20fSFrederick Mayle uploadCompleteCallback_); 171*9712c20fSFrederick Mayle } 172*9712c20fSFrederick Mayle} 173*9712c20fSFrederick Mayle 174*9712c20fSFrederick Mayle- (void)setUploadingEnabled:(BOOL)enabled { 175*9712c20fSFrederick Mayle NSAssert(started_, 176*9712c20fSFrederick Mayle @"The controller must be started before setUploadingEnabled is called"); 177*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 178*9712c20fSFrederick Mayle if (enabled == enableUploads_) 179*9712c20fSFrederick Mayle return; 180*9712c20fSFrederick Mayle if (enabled) { 181*9712c20fSFrederick Mayle // Set this before calling doSendStoredCrashReport, because that 182*9712c20fSFrederick Mayle // calls sendDelay, which in turn checks this flag. 183*9712c20fSFrederick Mayle enableUploads_ = YES; 184*9712c20fSFrederick Mayle [self sendStoredCrashReports]; 185*9712c20fSFrederick Mayle } else { 186*9712c20fSFrederick Mayle // disable the enableUpload_ flag. 187*9712c20fSFrederick Mayle // sendDelay checks this flag and disables the upload of logs by sendStoredCrashReports 188*9712c20fSFrederick Mayle enableUploads_ = NO; 189*9712c20fSFrederick Mayle } 190*9712c20fSFrederick Mayle }); 191*9712c20fSFrederick Mayle} 192*9712c20fSFrederick Mayle 193*9712c20fSFrederick Mayle- (void)updateConfiguration:(NSDictionary*)configuration { 194*9712c20fSFrederick Mayle NSAssert(!started_, 195*9712c20fSFrederick Mayle @"The controller must not be started when updateConfiguration is called"); 196*9712c20fSFrederick Mayle [configuration_ addEntriesFromDictionary:configuration]; 197*9712c20fSFrederick Mayle NSString *uploadInterval = 198*9712c20fSFrederick Mayle [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; 199*9712c20fSFrederick Mayle if (uploadInterval) 200*9712c20fSFrederick Mayle [self setUploadInterval:[uploadInterval intValue]]; 201*9712c20fSFrederick Mayle} 202*9712c20fSFrederick Mayle 203*9712c20fSFrederick Mayle- (void)resetConfiguration { 204*9712c20fSFrederick Mayle NSAssert(!started_, 205*9712c20fSFrederick Mayle @"The controller must not be started when resetConfiguration is called"); 206*9712c20fSFrederick Mayle [configuration_ autorelease]; 207*9712c20fSFrederick Mayle configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy]; 208*9712c20fSFrederick Mayle NSString *uploadInterval = 209*9712c20fSFrederick Mayle [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; 210*9712c20fSFrederick Mayle [self setUploadInterval:[uploadInterval intValue]]; 211*9712c20fSFrederick Mayle [self setParametersToAddAtUploadTime:nil]; 212*9712c20fSFrederick Mayle} 213*9712c20fSFrederick Mayle 214*9712c20fSFrederick Mayle- (void)setUploadingURL:(NSString*)url { 215*9712c20fSFrederick Mayle NSAssert(!started_, 216*9712c20fSFrederick Mayle @"The controller must not be started when setUploadingURL is called"); 217*9712c20fSFrederick Mayle [configuration_ setValue:url forKey:@BREAKPAD_URL]; 218*9712c20fSFrederick Mayle} 219*9712c20fSFrederick Mayle 220*9712c20fSFrederick Mayle- (void)setUploadInterval:(int)intervalInSeconds { 221*9712c20fSFrederick Mayle NSAssert(!started_, 222*9712c20fSFrederick Mayle @"The controller must not be started when setUploadInterval is called"); 223*9712c20fSFrederick Mayle [configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL]; 224*9712c20fSFrederick Mayle uploadIntervalInSeconds_ = intervalInSeconds; 225*9712c20fSFrederick Mayle if (uploadIntervalInSeconds_ < 0) 226*9712c20fSFrederick Mayle uploadIntervalInSeconds_ = 0; 227*9712c20fSFrederick Mayle} 228*9712c20fSFrederick Mayle 229*9712c20fSFrederick Mayle- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters { 230*9712c20fSFrederick Mayle NSAssert(!started_, @"The controller must not be started when " 231*9712c20fSFrederick Mayle "setParametersToAddAtUploadTime is called"); 232*9712c20fSFrederick Mayle [uploadTimeParameters_ autorelease]; 233*9712c20fSFrederick Mayle uploadTimeParameters_ = [uploadTimeParameters copy]; 234*9712c20fSFrederick Mayle} 235*9712c20fSFrederick Mayle 236*9712c20fSFrederick Mayle- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key { 237*9712c20fSFrederick Mayle NSAssert(started_, 238*9712c20fSFrederick Mayle @"The controller must be started before addUploadParameter is called"); 239*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 240*9712c20fSFrederick Mayle if (breakpadRef_) 241*9712c20fSFrederick Mayle BreakpadAddUploadParameter(breakpadRef_, key, value); 242*9712c20fSFrederick Mayle }); 243*9712c20fSFrederick Mayle} 244*9712c20fSFrederick Mayle 245*9712c20fSFrederick Mayle- (void)setUploadCallback:(BreakpadUploadCompletionCallback)callback { 246*9712c20fSFrederick Mayle NSAssert(started_, 247*9712c20fSFrederick Mayle @"The controller must not be started before setUploadCallback is " 248*9712c20fSFrederick Mayle "called"); 249*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 250*9712c20fSFrederick Mayle uploadCompleteCallback_ = callback; 251*9712c20fSFrederick Mayle }); 252*9712c20fSFrederick Mayle} 253*9712c20fSFrederick Mayle 254*9712c20fSFrederick Mayle- (void)removeUploadParameterForKey:(NSString*)key { 255*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 256*9712c20fSFrederick Mayle "removeUploadParameterForKey is called"); 257*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 258*9712c20fSFrederick Mayle if (breakpadRef_) 259*9712c20fSFrederick Mayle BreakpadRemoveUploadParameter(breakpadRef_, key); 260*9712c20fSFrederick Mayle }); 261*9712c20fSFrederick Mayle} 262*9712c20fSFrederick Mayle 263*9712c20fSFrederick Mayle- (void)withBreakpadRef:(void(^)(BreakpadRef))callback { 264*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 265*9712c20fSFrederick Mayle callback(started_ ? breakpadRef_ : NULL); 266*9712c20fSFrederick Mayle }); 267*9712c20fSFrederick Mayle} 268*9712c20fSFrederick Mayle 269*9712c20fSFrederick Mayle- (void)hasReportToUpload:(void(^)(BOOL))callback { 270*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 271*9712c20fSFrederick Mayle "hasReportToUpload is called"); 272*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 273*9712c20fSFrederick Mayle callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0)); 274*9712c20fSFrederick Mayle }); 275*9712c20fSFrederick Mayle} 276*9712c20fSFrederick Mayle 277*9712c20fSFrederick Mayle- (void)getCrashReportCount:(void(^)(int))callback { 278*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 279*9712c20fSFrederick Mayle "getCrashReportCount is called"); 280*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 281*9712c20fSFrederick Mayle callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0); 282*9712c20fSFrederick Mayle }); 283*9712c20fSFrederick Mayle} 284*9712c20fSFrederick Mayle 285*9712c20fSFrederick Mayle- (void)getNextReportConfigurationOrSendDelay: 286*9712c20fSFrederick Mayle (void(^)(NSDictionary*, int))callback { 287*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 288*9712c20fSFrederick Mayle "getNextReportConfigurationOrSendDelay is called"); 289*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 290*9712c20fSFrederick Mayle if (!breakpadRef_) { 291*9712c20fSFrederick Mayle callback(nil, -1); 292*9712c20fSFrederick Mayle return; 293*9712c20fSFrederick Mayle } 294*9712c20fSFrederick Mayle int delay = [self sendDelay]; 295*9712c20fSFrederick Mayle if (delay != 0) { 296*9712c20fSFrederick Mayle callback(nil, delay); 297*9712c20fSFrederick Mayle return; 298*9712c20fSFrederick Mayle } 299*9712c20fSFrederick Mayle [self reportWillBeSent]; 300*9712c20fSFrederick Mayle callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0); 301*9712c20fSFrederick Mayle }); 302*9712c20fSFrederick Mayle} 303*9712c20fSFrederick Mayle 304*9712c20fSFrederick Mayle- (void)getDateOfMostRecentCrashReport:(void(^)(NSDate *))callback { 305*9712c20fSFrederick Mayle NSAssert(started_, @"The controller must be started before " 306*9712c20fSFrederick Mayle "getDateOfMostRecentCrashReport is called"); 307*9712c20fSFrederick Mayle dispatch_async(queue_, ^{ 308*9712c20fSFrederick Mayle if (!breakpadRef_) { 309*9712c20fSFrederick Mayle callback(nil); 310*9712c20fSFrederick Mayle return; 311*9712c20fSFrederick Mayle } 312*9712c20fSFrederick Mayle callback(BreakpadGetDateOfMostRecentCrashReport(breakpadRef_)); 313*9712c20fSFrederick Mayle }); 314*9712c20fSFrederick Mayle} 315*9712c20fSFrederick Mayle 316*9712c20fSFrederick Mayle#pragma mark - 317*9712c20fSFrederick Mayle 318*9712c20fSFrederick Mayle- (int)sendDelay { 319*9712c20fSFrederick Mayle if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_) 320*9712c20fSFrederick Mayle return -1; 321*9712c20fSFrederick Mayle 322*9712c20fSFrederick Mayle // To prevent overloading the crash server, crashes are not sent than one 323*9712c20fSFrederick Mayle // report every |uploadIntervalInSeconds_|. A value in the user defaults is 324*9712c20fSFrederick Mayle // used to keep the time of the last upload. 325*9712c20fSFrederick Mayle NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 326*9712c20fSFrederick Mayle NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission]; 327*9712c20fSFrederick Mayle NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0; 328*9712c20fSFrederick Mayle NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime; 329*9712c20fSFrederick Mayle 330*9712c20fSFrederick Mayle if (spanSeconds >= uploadIntervalInSeconds_) 331*9712c20fSFrederick Mayle return 0; 332*9712c20fSFrederick Mayle return uploadIntervalInSeconds_ - static_cast<int>(spanSeconds); 333*9712c20fSFrederick Mayle} 334*9712c20fSFrederick Mayle 335*9712c20fSFrederick Mayle- (void)reportWillBeSent { 336*9712c20fSFrederick Mayle NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 337*9712c20fSFrederick Mayle [userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()] 338*9712c20fSFrederick Mayle forKey:kLastSubmission]; 339*9712c20fSFrederick Mayle [userDefaults synchronize]; 340*9712c20fSFrederick Mayle} 341*9712c20fSFrederick Mayle 342*9712c20fSFrederick Mayle// This method must be called from the breakpad queue. 343*9712c20fSFrederick Mayle- (void)sendStoredCrashReports { 344*9712c20fSFrederick Mayle if (BreakpadGetCrashReportCount(breakpadRef_) == 0) 345*9712c20fSFrederick Mayle return; 346*9712c20fSFrederick Mayle 347*9712c20fSFrederick Mayle int timeToWait = [self sendDelay]; 348*9712c20fSFrederick Mayle 349*9712c20fSFrederick Mayle // Unable to ever send report. 350*9712c20fSFrederick Mayle if (timeToWait == -1) 351*9712c20fSFrederick Mayle return; 352*9712c20fSFrederick Mayle 353*9712c20fSFrederick Mayle // A report can be sent now. 354*9712c20fSFrederick Mayle if (timeToWait == 0) { 355*9712c20fSFrederick Mayle [self reportWillBeSent]; 356*9712c20fSFrederick Mayle BreakpadUploadNextReportWithParameters(breakpadRef_, uploadTimeParameters_, 357*9712c20fSFrederick Mayle uploadCompleteCallback_); 358*9712c20fSFrederick Mayle 359*9712c20fSFrederick Mayle // If more reports must be sent, make sure this method is called again. 360*9712c20fSFrederick Mayle if (BreakpadGetCrashReportCount(breakpadRef_) > 0) 361*9712c20fSFrederick Mayle timeToWait = uploadIntervalInSeconds_; 362*9712c20fSFrederick Mayle } 363*9712c20fSFrederick Mayle 364*9712c20fSFrederick Mayle // A report must be sent later. 365*9712c20fSFrederick Mayle if (timeToWait > 0) { 366*9712c20fSFrederick Mayle dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeToWait * NSEC_PER_SEC)); 367*9712c20fSFrederick Mayle dispatch_after(delay, queue_, ^{ 368*9712c20fSFrederick Mayle [self sendStoredCrashReports]; 369*9712c20fSFrederick Mayle }); 370*9712c20fSFrederick Mayle } 371*9712c20fSFrederick Mayle} 372*9712c20fSFrederick Mayle 373*9712c20fSFrederick Mayle@end 374