GDTCOREvent.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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 = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
  32. counterPath = [NSString stringWithFormat:@"%@/google-sdks-events/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. });
  55. return result;
  56. }
  57. - (nullable instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target {
  58. GDTCORAssert(mappingID.length > 0, @"Please give a valid mapping ID");
  59. GDTCORAssert(target > 0, @"A target cannot be negative or 0");
  60. if (mappingID == nil || mappingID.length == 0 || target <= 0) {
  61. return nil;
  62. }
  63. self = [super init];
  64. if (self) {
  65. _eventID = [GDTCOREvent nextEventID];
  66. _mappingID = mappingID;
  67. _target = target;
  68. _qosTier = GDTCOREventQosDefault;
  69. }
  70. GDTCORLogDebug(@"Event %@ created. mappingID: %@ target:%ld", self, mappingID, (long)target);
  71. return self;
  72. }
  73. - (instancetype)copy {
  74. GDTCOREvent *copy = [[GDTCOREvent alloc] initWithMappingID:_mappingID target:_target];
  75. copy->_eventID = _eventID;
  76. copy.dataObject = _dataObject;
  77. copy.qosTier = _qosTier;
  78. copy.clockSnapshot = _clockSnapshot;
  79. copy.customBytes = _customBytes;
  80. copy->_GDTFilePath = _GDTFilePath;
  81. GDTCORLogDebug(@"Copying event %@ to event %@", self, copy);
  82. return copy;
  83. }
  84. - (NSUInteger)hash {
  85. // This loses some precision, but it's probably fine.
  86. NSUInteger eventIDHash = [_eventID hash];
  87. NSUInteger mappingIDHash = [_mappingID hash];
  88. NSUInteger timeHash = [_clockSnapshot hash];
  89. NSInteger dataObjectHash = [_dataObject hash];
  90. NSUInteger fileURL = [_GDTFilePath hash];
  91. return eventIDHash ^ mappingIDHash ^ _target ^ _qosTier ^ timeHash ^ dataObjectHash ^ fileURL;
  92. }
  93. - (BOOL)isEqual:(id)object {
  94. return [self hash] == [object hash];
  95. }
  96. #pragma mark - Property overrides
  97. - (void)setDataObject:(id<GDTCOREventDataObject>)dataObject {
  98. // If you're looking here because of a performance issue in -transportBytes slowing the assignment
  99. // of -dataObject, one way to address this is to add a queue to this class,
  100. // dispatch_(barrier_ if concurrent)async here, and implement the getter with a dispatch_sync.
  101. if (dataObject != _dataObject) {
  102. _dataObject = dataObject;
  103. }
  104. }
  105. - (NSURL *)fileURL {
  106. if (!_GDTFilePath) {
  107. _GDTFilePath = [NSString stringWithFormat:@"event-%lu", (unsigned long)self.hash];
  108. }
  109. return [GDTCORRootDirectory() URLByAppendingPathComponent:_GDTFilePath];
  110. }
  111. #pragma mark - Private methods
  112. - (BOOL)writeToGDTPath:(NSString *)filePath error:(NSError **)error {
  113. NSData *dataTransportBytes = [_dataObject transportBytes];
  114. if (dataTransportBytes == nil) {
  115. _GDTFilePath = nil;
  116. _dataObject = nil;
  117. return NO;
  118. }
  119. NSURL *fileURL = [GDTCORRootDirectory() URLByAppendingPathComponent:filePath];
  120. BOOL writingSuccess = [dataTransportBytes writeToURL:fileURL
  121. options:NSDataWritingAtomic
  122. error:error];
  123. if (!writingSuccess) {
  124. GDTCORLogError(GDTCORMCEFileWriteError, @"An event file could not be written: %@", fileURL);
  125. return NO;
  126. }
  127. _GDTFilePath = filePath;
  128. _dataObject = nil;
  129. return YES;
  130. }
  131. #pragma mark - NSSecureCoding and NSCoding Protocols
  132. /** NSCoding key for eventID property. */
  133. static NSString *eventIDKey = @"_eventID";
  134. /** NSCoding key for mappingID property. */
  135. static NSString *mappingIDKey = @"_mappingID";
  136. /** NSCoding key for target property. */
  137. static NSString *targetKey = @"_target";
  138. /** NSCoding key for qosTier property. */
  139. static NSString *qosTierKey = @"_qosTier";
  140. /** NSCoding key for clockSnapshot property. */
  141. static NSString *clockSnapshotKey = @"_clockSnapshot";
  142. /** NSCoding key for fileURL property. */
  143. static NSString *fileURLKey = @"_fileURL";
  144. /** NSCoding key for GDTFilePath property. */
  145. static NSString *kGDTFilePathKey = @"_GDTFilePath";
  146. /** NSCoding key for backwards compatibility of GDTCORStoredEvent mappingID property.*/
  147. static NSString *kStoredEventMappingIDKey = @"GDTCORStoredEventMappingIDKey";
  148. /** NSCoding key for backwards compatibility of GDTCORStoredEvent target property.*/
  149. static NSString *kStoredEventTargetKey = @"GDTCORStoredEventTargetKey";
  150. /** NSCoding key for backwards compatibility of GDTCORStoredEvent qosTier property.*/
  151. static NSString *kStoredEventQosTierKey = @"GDTCORStoredEventQosTierKey";
  152. /** NSCoding key for backwards compatibility of GDTCORStoredEvent clockSnapshot property.*/
  153. static NSString *kStoredEventClockSnapshotKey = @"GDTCORStoredEventClockSnapshotKey";
  154. /** NSCoding key for backwards compatibility of GDTCORStoredEvent dataFuture property.*/
  155. static NSString *kStoredEventDataFutureKey = @"GDTCORStoredEventDataFutureKey";
  156. /** NSCoding key for customData property. */
  157. static NSString *kCustomDataKey = @"GDTCOREventCustomDataKey";
  158. + (BOOL)supportsSecureCoding {
  159. return YES;
  160. }
  161. - (id)initWithCoder:(NSCoder *)aDecoder {
  162. GDTCORDataFuture *dataFuture = [aDecoder decodeObjectOfClass:[GDTCORDataFuture class]
  163. forKey:kStoredEventDataFutureKey];
  164. if (dataFuture) {
  165. return [self initWithCoderForStoredEventBackwardCompatibility:aDecoder
  166. fileURL:dataFuture.fileURL];
  167. }
  168. NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:mappingIDKey];
  169. NSInteger target = [aDecoder decodeIntegerForKey:targetKey];
  170. self = [self initWithMappingID:mappingID target:target];
  171. if (self) {
  172. _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey];
  173. if (_eventID == nil) {
  174. _eventID = [GDTCOREvent nextEventID];
  175. }
  176. _qosTier = [aDecoder decodeIntegerForKey:qosTierKey];
  177. _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class] forKey:clockSnapshotKey];
  178. NSURL *fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:fileURLKey];
  179. if (fileURL) {
  180. _GDTFilePath = [fileURL lastPathComponent];
  181. } else {
  182. _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey];
  183. }
  184. _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey];
  185. }
  186. return self;
  187. }
  188. - (id)initWithCoderForStoredEventBackwardCompatibility:(NSCoder *)aDecoder
  189. fileURL:(NSURL *)fileURL {
  190. NSString *mappingID = [aDecoder decodeObjectOfClass:[NSString class]
  191. forKey:kStoredEventMappingIDKey];
  192. NSInteger target = [[aDecoder decodeObjectOfClass:[NSNumber class]
  193. forKey:kStoredEventTargetKey] integerValue];
  194. self = [self initWithMappingID:mappingID target:target];
  195. if (self) {
  196. _eventID = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:eventIDKey];
  197. if (_eventID == nil) {
  198. _eventID = [GDTCOREvent nextEventID];
  199. }
  200. _qosTier = [[aDecoder decodeObjectOfClass:[NSNumber class]
  201. forKey:kStoredEventQosTierKey] integerValue];
  202. _clockSnapshot = [aDecoder decodeObjectOfClass:[GDTCORClock class]
  203. forKey:kStoredEventClockSnapshotKey];
  204. if (fileURL) {
  205. _GDTFilePath = [fileURL lastPathComponent];
  206. } else {
  207. _GDTFilePath = [aDecoder decodeObjectOfClass:[NSString class] forKey:kGDTFilePathKey];
  208. }
  209. _customBytes = [aDecoder decodeObjectOfClass:[NSData class] forKey:kCustomDataKey];
  210. }
  211. return self;
  212. }
  213. - (void)encodeWithCoder:(NSCoder *)aCoder {
  214. [aCoder encodeObject:_eventID forKey:eventIDKey];
  215. [aCoder encodeObject:_mappingID forKey:mappingIDKey];
  216. [aCoder encodeInteger:_target forKey:targetKey];
  217. [aCoder encodeInteger:_qosTier forKey:qosTierKey];
  218. [aCoder encodeObject:_clockSnapshot forKey:clockSnapshotKey];
  219. [aCoder encodeObject:_GDTFilePath forKey:kGDTFilePathKey];
  220. [aCoder encodeObject:_customBytes forKey:kCustomDataKey];
  221. }
  222. @end