20
20
#import " GDTAssert.h"
21
21
#import " GDTClock.h"
22
22
#import " GDTConsoleLogger.h"
23
+ #import " GDTReachability.h"
23
24
#import " GDTRegistrar_Private.h"
24
25
#import " GDTStorage.h"
25
26
#import " GDTUploadPackage_Private.h"
@@ -85,8 +86,11 @@ - (void)forceUploadForTarget:(GDTTarget)target {
85
86
if (self->_runningInBackground ) {
86
87
[self ->_forcedUploadQueue insertObject: forceUploadBlock atIndex: 0 ];
87
88
[NSKeyedArchiver archiveRootObject: self toFile: [GDTUploadCoordinator archivePath ]];
88
-
89
- // Enqueue the force upload block if there's an in-flight upload for that target already.
89
+ // Enqueue the force upload block if conditions are bad or if there's an in-flight upload for
90
+ // that target already.
91
+ } else if (([self uploadConditions ] & GDTUploadConditionNoNetwork) ==
92
+ GDTUploadConditionNoNetwork) {
93
+ [self ->_forcedUploadQueue insertObject: forceUploadBlock atIndex: 0 ];
90
94
} else if (self->_targetToInFlightEventSet [targetNumber]) {
91
95
[self ->_forcedUploadQueue insertObject: forceUploadBlock atIndex: 0 ];
92
96
} else {
@@ -109,39 +113,34 @@ - (GDTStorage *)storage {
109
113
// This should always be called in a thread-safe manner. When running the background, in theory,
110
114
// the uploader's background task should be calling this.
111
115
- (GDTUploaderCompletionBlock)onCompleteBlock {
112
- __weak GDTUploadCoordinator *weakSelf = self;
113
116
static GDTUploaderCompletionBlock onCompleteBlock;
114
117
static dispatch_once_t onceToken;
115
118
dispatch_once (&onceToken, ^{
116
119
onCompleteBlock = ^(GDTTarget target, GDTClock *nextUploadAttemptUTC, NSError *error) {
117
- GDTUploadCoordinator *strongSelf = weakSelf;
118
- if (strongSelf) {
119
- dispatch_async (strongSelf.coordinationQueue , ^{
120
- NSNumber *targetNumber = @(target);
121
- if (error) {
122
- GDTLogWarning (GDTMCWUploadFailed, @" Error during upload: %@ " , error);
123
- [strongSelf->_targetToInFlightEventSet removeObjectForKey: targetNumber];
124
- return ;
125
- }
126
- strongSelf->_targetToNextUploadTimes [targetNumber] = nextUploadAttemptUTC;
127
- NSSet <GDTStoredEvent *> *events =
128
- [strongSelf->_targetToInFlightEventSet objectForKey: targetNumber];
129
- GDTAssert (events, @" There should be an in-flight event set to remove." );
130
- [strongSelf.storage removeEvents: events];
131
- [strongSelf->_targetToInFlightEventSet removeObjectForKey: targetNumber];
132
- if (strongSelf->_runningInBackground ) {
133
- [NSKeyedArchiver archiveRootObject: self toFile: [GDTUploadCoordinator archivePath ]];
134
- } else if (strongSelf->_forcedUploadQueue .count ) {
135
- GDTUploadCoordinatorForceUploadBlock queuedBlock =
136
- [strongSelf->_forcedUploadQueue lastObject ];
137
- if (queuedBlock) {
138
- dispatch_async (strongSelf->_coordinationQueue , ^{
139
- queuedBlock ();
140
- });
141
- }
120
+ dispatch_async (self->_coordinationQueue , ^{
121
+ NSNumber *targetNumber = @(target);
122
+ if (error) {
123
+ GDTLogWarning (GDTMCWUploadFailed, @" Error during upload: %@ " , error);
124
+ [self ->_targetToInFlightEventSet removeObjectForKey: targetNumber];
125
+ return ;
126
+ }
127
+ self->_targetToNextUploadTimes [targetNumber] = nextUploadAttemptUTC;
128
+ NSSet <GDTStoredEvent *> *events =
129
+ [self ->_targetToInFlightEventSet objectForKey: targetNumber];
130
+ GDTAssert (events, @" There should be an in-flight event set to remove." );
131
+ [self ->_storage removeEvents: events];
132
+ [self ->_targetToInFlightEventSet removeObjectForKey: targetNumber];
133
+ if (self->_runningInBackground ) {
134
+ [NSKeyedArchiver archiveRootObject: self toFile: [GDTUploadCoordinator archivePath ]];
135
+ } else if (self->_forcedUploadQueue .count ) {
136
+ GDTUploadCoordinatorForceUploadBlock queuedBlock = [self ->_forcedUploadQueue lastObject ];
137
+ if (queuedBlock) {
138
+ dispatch_async (self->_coordinationQueue , ^{
139
+ queuedBlock ();
140
+ });
142
141
}
143
- });
144
- }
142
+ }
143
+ });
145
144
};
146
145
});
147
146
return onCompleteBlock;
@@ -153,20 +152,17 @@ - (GDTUploaderCompletionBlock)onCompleteBlock {
153
152
* check the next-upload clocks of all targets to determine if an upload attempt can be made.
154
153
*/
155
154
- (void )startTimer {
156
- __weak GDTUploadCoordinator *weakSelf = self;
157
155
dispatch_sync (_coordinationQueue, ^{
158
- GDTUploadCoordinator *strongSelf = weakSelf;
159
- GDTAssert (strongSelf, @" self must be real to start a timer." );
160
- strongSelf->_timer =
161
- dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0 , 0 , strongSelf->_coordinationQueue );
162
- dispatch_source_set_timer (strongSelf->_timer , DISPATCH_TIME_NOW, strongSelf->_timerInterval ,
163
- strongSelf->_timerLeeway );
164
- dispatch_source_set_event_handler (strongSelf->_timer , ^{
165
- if (!strongSelf->_runningInBackground ) {
166
- [strongSelf checkPrioritizersAndUploadEvents ];
156
+ self->_timer =
157
+ dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0 , 0 , self->_coordinationQueue );
158
+ dispatch_source_set_timer (self->_timer , DISPATCH_TIME_NOW, self->_timerInterval ,
159
+ self->_timerLeeway );
160
+ dispatch_source_set_event_handler (self->_timer , ^{
161
+ if (!self->_runningInBackground ) {
162
+ [self checkPrioritizersAndUploadEvents ];
167
163
}
168
164
});
169
- dispatch_resume (strongSelf ->_timer );
165
+ dispatch_resume (self ->_timer );
170
166
});
171
167
}
172
168
@@ -181,36 +177,72 @@ - (void)stopTimer {
181
177
* events for that target or not. If so, queries the prioritizers
182
178
*/
183
179
- (void )checkPrioritizersAndUploadEvents {
184
- __weak GDTUploadCoordinator *weakSelf = self;
185
180
dispatch_async (_coordinationQueue, ^{
186
181
if (self->_runningInBackground ) {
187
182
return ;
188
183
}
184
+
185
+ GDTUploadConditions conds = [self uploadConditions ];
186
+ if ((conds & GDTUploadConditionNoNetwork) == GDTUploadConditionNoNetwork) {
187
+ return ;
188
+ }
189
+
189
190
static int count = 0 ;
190
191
count++;
191
- GDTUploadCoordinator *strongSelf = weakSelf;
192
- if (strongSelf) {
193
- NSArray <NSNumber *> *targetsReadyForUpload = [self targetsReadyForUpload ];
194
- for (NSNumber *target in targetsReadyForUpload) {
195
- id <GDTPrioritizer> prioritizer = strongSelf->_registrar .targetToPrioritizer [target];
196
- id <GDTUploader> uploader = strongSelf->_registrar .targetToUploader [target];
197
- GDTAssert (prioritizer && uploader, @" Target '%@ ' is missing an implementation" , target);
198
- GDTUploadConditions conds = [self uploadConditions ];
199
- GDTUploadPackage *package = [[prioritizer uploadPackageWithConditions: conds] copy ];
200
- package.storage = strongSelf.storage ;
201
- if (package.events && package.events .count > 0 ) {
202
- strongSelf->_targetToInFlightEventSet [target] = package.events ;
203
- [uploader uploadPackage: package onComplete: self .onCompleteBlock];
204
- }
192
+ NSArray <NSNumber *> *targetsReadyForUpload = [self targetsReadyForUpload ];
193
+ for (NSNumber *target in targetsReadyForUpload) {
194
+ id <GDTPrioritizer> prioritizer = self->_registrar .targetToPrioritizer [target];
195
+ id <GDTUploader> uploader = self->_registrar .targetToUploader [target];
196
+ GDTAssert (prioritizer && uploader, @" Target '%@ ' is missing an implementation" , target);
197
+ GDTUploadPackage *package = [[prioritizer uploadPackageWithConditions: conds] copy ];
198
+ package.storage = self.storage ;
199
+ if (package.events && package.events .count > 0 ) {
200
+ self->_targetToInFlightEventSet [target] = package.events ;
201
+ [uploader uploadPackage: package onComplete: self .onCompleteBlock];
205
202
}
206
203
}
207
204
});
208
205
}
209
206
210
- /* * */
207
+ /* * Returns the current upload conditions after making determinations about the network connection.
208
+ *
209
+ * @return The current upload conditions.
210
+ */
211
211
- (GDTUploadConditions)uploadConditions {
212
- // TODO: Compute the real upload conditions.
213
- return GDTUploadConditionMobileData;
212
+ SCNetworkReachabilityFlags currentFlags = [GDTReachability currentFlags ];
213
+
214
+ BOOL reachable =
215
+ (currentFlags & kSCNetworkReachabilityFlagsReachable ) == kSCNetworkReachabilityFlagsReachable ;
216
+ BOOL connectionRequired = (currentFlags & kSCNetworkReachabilityFlagsConnectionRequired ) ==
217
+ kSCNetworkReachabilityFlagsConnectionRequired ;
218
+ BOOL interventionRequired = (currentFlags & kSCNetworkReachabilityFlagsInterventionRequired ) ==
219
+ kSCNetworkReachabilityFlagsInterventionRequired ;
220
+ BOOL connectionOnDemand = (currentFlags & kSCNetworkReachabilityFlagsConnectionOnDemand ) ==
221
+ kSCNetworkReachabilityFlagsConnectionOnDemand ;
222
+ BOOL connectionOnTraffic = (currentFlags & kSCNetworkReachabilityFlagsConnectionOnTraffic ) ==
223
+ kSCNetworkReachabilityFlagsConnectionOnTraffic ;
224
+ BOOL isWWAN =
225
+ (currentFlags & kSCNetworkReachabilityFlagsIsWWAN ) == kSCNetworkReachabilityFlagsIsWWAN ;
226
+
227
+ if (!reachable) {
228
+ return GDTUploadConditionNoNetwork;
229
+ }
230
+
231
+ GDTUploadConditions conditions = 0 ;
232
+ conditions |= !connectionRequired ? GDTUploadConditionWifiData : conditions;
233
+ conditions |= isWWAN ? GDTUploadConditionMobileData : conditions;
234
+ if ((connectionOnTraffic || connectionOnDemand) && !interventionRequired) {
235
+ conditions = GDTUploadConditionWifiData;
236
+ }
237
+
238
+ BOOL wifi = (conditions & GDTUploadConditionWifiData) == GDTUploadConditionWifiData;
239
+ BOOL cell = (conditions & GDTUploadConditionMobileData) == GDTUploadConditionMobileData;
240
+
241
+ if (!(wifi || cell)) {
242
+ conditions = GDTUploadConditionUnclearConnection;
243
+ }
244
+
245
+ return conditions;
214
246
}
215
247
216
248
/* * Checks the next upload time for each target and returns an array of targets that are
0 commit comments