GDTCOREvent.m 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * Copyright 2018 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "GDTCORLibrary/Public/GDTCOREvent.h"
  17. #import <GoogleDataTransport/GDTCORAssert.h>
  18. #import <GoogleDataTransport/GDTCORClock.h>
  19. #import <GoogleDataTransport/GDTCORConsoleLogger.h>
  20. #import <GoogleDataTransport/GDTCORPlatform.h>
  21. #import "GDTCORLibrary/Private/GDTCORDataFuture.h"
  22. #import "GDTCORLibrary/Private/GDTCOREvent_Private.h"
  23. @implementation GDTCOREvent
  24. + (NSNumber *)nextEventID {
  25. static unsigned long long nextEventID = 0;
  26. static NSString *counterPath;
  27. static dispatch_queue_t eventIDQueue;
  28. static dispatch_once_t onceToken;
  29. dispatch_once(&onceToken, ^{
  30. eventIDQueue = dispatch_queue_create("com.google.GDTCOREventIDQueue", DISPATCH_QUEUE_SERIAL);
  31. counterPath = GDTCORRootDirectory().path;
  32. counterPath = [NSString stringWithFormat:@"%@/count", counterPath];
  33. NSError *error;
  34. NSString *countText = [NSString stringWithContentsOfFile:counterPath
  35. encoding:NSUTF8StringEncoding
  36. error:&error];
  37. const char *countChars = [countText UTF8String];
  38. unsigned long long count = 0ULL;
  39. if (countChars) {
  40. count = strtoull([countText UTF8String], NULL, 10);
  41. }
  42. nextEventID = error ? 0 : count;
  43. });
  44. __block NSNumber *result;
  45. dispatch_sync(eventIDQueue, ^{
  46. result = @(nextEventID);
  47. nextEventID++;
  48. NSError *error;
  49. [[result stringValue] writeToFile:counterPath
  50. atomically:YES
  51. encoding:NSUTF8StringEncoding
  52. error:&error];
  53. GDTCORAssert(error == nil, @"There was an error saving the new counter value to disk: %@",
  54. error);
  55. });
  56. return result;
  57. }
  58. - (nullable instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target {
  59. GDTCORAssert(mappingID.length > 0, @"Please give a valid mapping ID");
  60. GDTCORAssert(target > 0, @"A target cannot be negative or 0");
  61. if (mappingID == nil || mappingID.length == 0 || target <= 0) {
  62. return nil;
  63. }
  64. self = [super init];
  65. if (self) {
  66. _eventID = [GDTCOREvent nextEventID];
  67. _mappingID = mappingID;
  68. _target = target;
  69. _qosTier = GDTCOREventQosDefault;
  70. }
  71. GDTCORLogDebug(@"Event %@ created. mappingID: %@ target:%ld", self, mappingID, (long)target);
  72. return self;
  73. }
  74. - (instancetype)copy {
  75. GDTCOREvent *copy = [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target];
  76. copy->_eventID = _eventID;
  77. copy.dataObject = _dataObject;
  78. copy.qosTier = _qosTier;
  79. copy.clockSnapshot = _clockSnapshot;
  80. copy.customBytes = _customBytes;
  81. copy->_GDTFilePath = _GDTFilePath;
  82. GDTCORLogDebug(@"Copying event %@ to event %@", self, copy);
  83. return copy;
  84. }
  85. - (NSUInteger)hash {
  86. // This loses some precision, but it's probably fine.
  87. NSUInteger eventIDHash = [_eventID hash];
  88. NSUInteger mappingIDHash = [_mappingID hash];
  89. NSUInteger timeHash = [_clockSnapshot hash];
  90. NSInteger dataObjectHash = [_dataObject hash];
  91. NSUInteger fileURL = [_GDTFilePath hash];
  92. return eventIDHash ^ mappingIDHash ^ _target ^ _qosTier ^ timeHash ^ dataObjectHash ^ fileURL;
  93. }
  94. - (BOOL)isEqual:(id)object {
  95. return [self hash] == [object hash];
  96. }
  97. #pragma mark - Property overrides
  98. - (void)setDataObject:(id<GDTCOREventDataObject>)dataObject {
  99. // If you're looking here because of a performance issue in -transportBytes slowing the assignment
  100. // of -dataObject, one way to address this is to add a queue to this class,
  101. // dispatch_(barrier_ if concurrent)async here, and implement the getter with a dispatch_sync.
  102. if (dataObject != _dataObject) {
  103. _dataObject = dataObject;
  104. }
  105. }
  106. - (NSURL *)fileURL {
  107. if (!_GDTFilePath) {
  108. _GDTFilePath = [NSString stringWithFormat:@"event-%lu", (unsigned long)self.hash];
  109. }
  110. return [GDTCORRootDirectory() URLByAppendingPathComponent:_GDTFilePath];
  111. }
  112. #pragma mark - Private methods
  113. - (BOOL)writeToGDTPath:(NSString *)filePath error:(NSError **)error {
  114. NSData *dataTransportBytes = [_dataObject transportBytes];
  115. if (dataTransportBytes == nil) {
  116. _GDTFilePath = nil;
  117. _dataObject = nil;
  118. return NO;
  119. }
  120. NSURL *fileURL = [GDTCORRootDirectory() URLByAppendingPathComponent:filePath];
  121. BOOL writingSuccess = [dataTransportBytes writeToURL:fileURL
  122. options:NSDataWritingAtomic
  123. error:error];
  124. if (!writingSuccess) {
  125. GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@", fileURL);
  126. return NO;
  127. }
  128. _GDTFilePath = filePath;
  129. _dataObject = nil;
  130. return YES;
  131. }
  132. #pragma mark - NSSecureCoding and NSCoding Protocols
  133. /** NSCoding key for eventID property. */
  134. static NSString *eventIDKey = @"_eventID";
  135. /** NSCoding key for mappingID property. */
  136. static NSString *mappingIDKey = @"_mappingID";
  137. /** NSCoding key for target property. */
  138. static NSString *targetKey = @"_target";
  139. /** NSCoding key for qosTier property. */
  140. static NSString *qosTierKey = @"_qosTier";
  141. /** NSCoding key for clockSnapshot property. */
  142. static NSString *clockSnapshotKey = @"_clockSnapshot";
  143. /** NSCoding key for fileURL property. */
  144. static NSString *fileURLKey = @"_fileURL";
  145. /** NSCoding key for GDTFilePath property. */
  146. static NSString *kGDTFilePathKey = @"_GDTFilePath";
  147. /** NSCoding key for backwards compatibility of GDTCORStoredEvent mappingID property.*/
  148. static NSString *kStoredEventMappingIDKey = @"GDTCORStoredEventMappingIDKey";
  149. /** NSCoding key for backwards compatibility of GDTCORStoredEvent target property.*/
  150. static NSString *kStoredEventTargetKey = @"GDTCORStoredEventTargetKey";
  151. /** NSCoding key for backwards compatibility of GDTCORStoredEvent qosTier property.*/
  152. static NSString *kStoredEventQosTierKey = @"GDTCORStoredEventQosTierKey";
  153. /** NSCoding key for backwards compatibility of GDTCORStoredEvent clockSnapshot property.*/
  154. static NSString *kStoredEventClockSnapshotKey = @"GDTCORStoredEventClockSnapshotKey";
  155. /** NSCoding key for backwards compatibility of GDTCORStoredEvent dataFuture property.*/
  156. static NSString *kStoredEventDataFutureKey = @"GDTCORStoredEventDataFutureKey";
  157. /** NSCoding key for customData property. */
  158. static NSString *kCustomDataKey = @"GDTCOREventCustomDataKey";
  159. + (BOOL)supportsSecureCoding {
  160. return YES;
  161. }
  162. - (id)initWithCoder:(NSCoder *)aDecoder {
  163. GDTCORDataFuture *dataFuture = [aDecoder decodeObjectOfClass:[GDTCORDataFuture class]
  164. forKey:kStoredEventDataFutureKey];
  165. if (dataFuture) {
  166. return [self initWithCoderForStoredEventBackwardCompatibility:aDecoder
  167. fileURL:dataFuture.fileURL];
  168. }
  169. NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:mappingIDKey];
  170. NSInteger target = [aDecoder decodeIntegerForKey:targetKey];
  171. self = [self initWithMappingID:mappingID target:target];
  172. if (self) {
  173. _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey];
  174. if (_eventID == nil) {
  175. _eventID = [GDTCOREvent nextEventID];
  176. }
  177. _qosTier = [aDecoder decodeIntegerForKey:qosTierKey];
  178. _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:clockSnapshotKey];
  179. NSURL *fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:fileURLKey];
  180. if (fileURL) {
  181. _GDTFilePath = [fileURL lastPathComponent];
  182. } else {
  183. _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey];
  184. }
  185. _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey];
  186. }
  187. return self;
  188. }
  189. - (id)initWithCoderForStoredEventBackwardCompatibility:(NSCoder *)aDecoder
  190. fileURL:(NSURL *)fileURL {
  191. NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class]
  192. forKey:kStoredEventMappingIDKey];
  193. NSInteger target = [[aDecoder decodeObjectOfClass:[NSNumber class]
  194. forKey:kStoredEventTargetKey] integerValue];
  195. self = [self initWithMappingID:mappingID target:target];
  196. if (self) {
  197. _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey];
  198. if (_eventID == nil) {
  199. _eventID = [GDTCOREvent nextEventID];
  200. }
  201. _qosTier = [[aDecoder decodeObjectOfClass:[NSNumber class]
  202. forKey:kStoredEventQosTierKey] integerValue];
  203. _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class]
  204. forKey:kStoredEventClockSnapshotKey];
  205. if (fileURL) {
  206. _GDTFilePath = [fileURL lastPathComponent];
  207. } else {
  208. _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey];
  209. }
  210. _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey];
  211. }
  212. return self;
  213. }
  214. - (void)encodeWithCoder:(NSCoder *)aCoder {
  215. [aCoder encodeObject:_eventID forKey:eventIDKey];
  216. [aCoder encodeObject:_mappingID forKey:mappingIDKey];
  217. [aCoder encodeInteger:_target forKey:targetKey];
  218. [aCoder encodeInteger:_qosTier forKey:qosTierKey];
  219. [aCoder encodeObject:_clockSnapshot forKey:clockSnapshotKey];
  220. [aCoder encodeObject:_GDTFilePath forKey:kGDTFilePathKey];
  221. [aCoder encodeObject:_customBytes forKey:kCustomDataKey];
  222. }
  223. @end