xref: /aosp_15_r20/external/google-breakpad/src/client/ios/BreakpadController.mm (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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